-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Arnaud Varin
authored
Mar 15, 2023
1 parent
e3829f4
commit 9d4d5db
Showing
14 changed files
with
3,349 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* Copyright © 2020. TIBCO Software Inc. | ||
* This file is subject to the license terms contained | ||
* in the license file that is distributed with this file. | ||
*/ | ||
|
||
//@ts-check | ||
|
||
/** | ||
* # Spotfire mods development server | ||
* The purpose of the development server is to simplify the development of mods. The development server mimics the way the Spotfire runtime works in regards to cross origin requests and content security policies. | ||
*/ | ||
|
||
const liveServer = require("live-server"); | ||
const path = require("path"); | ||
const fs = require("fs"); | ||
const { promisify } = require("util"); | ||
const readFile = promisify(fs.readFile); | ||
const readdir = promisify(fs.readdir); | ||
|
||
const manifestName = "mod-manifest.json"; | ||
const rootDirectory = process.argv[2] || "./src/"; | ||
const scenario = process.argv[3]; | ||
|
||
// The development server tries to mimic the CSP policy used by the Spotfire runtime. | ||
const allowedExternalResources = new Set(); | ||
let declaredExternalResourcesInManifest = []; | ||
|
||
main(); | ||
|
||
async function main() { | ||
await readExternalResourcesFromManifest(); | ||
|
||
// If test scenario, run the server for a short while | ||
if (scenario === "test") { | ||
setTimeout(function () { | ||
return process.exit(0); | ||
}, 5000); | ||
} | ||
liveServer.start({ | ||
port: 8090, | ||
noCssInject: true, | ||
cors: false, | ||
// @ts-ignore | ||
open: "/" + manifestName, | ||
root: rootDirectory, | ||
wait: 250, // Waits for all changes, before reloading. Defaults to 0 sec. | ||
middleware: [cacheRedirect] | ||
}); | ||
} | ||
|
||
/** | ||
* Read external resources from the mod manifest placed in the root directory. | ||
*/ | ||
async function readExternalResourcesFromManifest() { | ||
const rootDirectoryAbsolutePath = path.resolve(rootDirectory); | ||
const files = await readdir(rootDirectoryAbsolutePath); | ||
|
||
if (files.find((fileName) => fileName == manifestName)) { | ||
const manifestPath = path.join(rootDirectoryAbsolutePath, manifestName); | ||
|
||
await readExternalResources(); | ||
fs.watch(manifestPath, {}, readExternalResources); | ||
|
||
async function readExternalResources() { | ||
let content = await readFile(manifestPath, { encoding: "utf-8" }); | ||
|
||
try { | ||
let json = JSON.parse(content); | ||
declaredExternalResourcesInManifest = json.externalResources || []; | ||
} catch (err) {} | ||
} | ||
} else { | ||
console.warn("Could not find a mod-manifest.json in the root directory", rootDirectoryAbsolutePath); | ||
} | ||
} | ||
|
||
/** | ||
* Middleware to manage caching and CSP headers. | ||
* @param {any} req - request object | ||
* @param {any} res - response object | ||
* @param {any} next - next callback to invoke the next middleware | ||
*/ | ||
function cacheRedirect(req, res, next) { | ||
const isCorsRequest = req.headers.origin != undefined; | ||
const requestFromOutsideSandbox = req.headers.origin != "null"; | ||
|
||
// Prevent CORS requests from the sandboxed iframe. E.g module loading will not work in embedded mode. | ||
if (isCorsRequest && requestFromOutsideSandbox) { | ||
allowedExternalResources.add(req.headers.origin); | ||
|
||
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); | ||
res.setHeader("Access-Control-Allow-Origin", "*"); | ||
} | ||
|
||
// Turn off caching on everything to avoid stale CSP headers etc. in the browser. | ||
// This also ensures that live server can inject its websocket snippet in .html pages it serves to the mod iframe. | ||
res.setHeader("Cache-Control", "no-store"); | ||
|
||
if (req.method !== "GET") { | ||
next(); | ||
return; | ||
} | ||
|
||
// Set same security headers in the development server as in the Spotfire runtime. | ||
res.setHeader( | ||
"content-security-policy", | ||
`sandbox allow-scripts; default-src 'self' 'unsafe-eval' 'unsafe-hashes' 'unsafe-inline' blob: data: ${[ | ||
...allowedExternalResources.values(), | ||
...declaredExternalResourcesInManifest | ||
].join(" ")}` | ||
); | ||
|
||
// CSP header used by older browsers where the CSP policy is not fully supported. | ||
res.setHeader("x-content-security-policy", "sandbox allow-scripts"); | ||
|
||
next(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "mod-starter", | ||
"version": "1.0.0", | ||
"description": "", | ||
"scripts": { | ||
"start": "npm install && npm run server", | ||
"server": "node development-server.js src" | ||
}, | ||
"keywords": [], | ||
"author": "Emanuel Dellsén, Hartmut Fischer, Jonatan Vaara, Alexander Strand & Karl Västgård", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"clean-css": "^4.2.3", | ||
"husky": "^4.3.0", | ||
"live-server": "^1.2.1", | ||
"prettier": "^2.1.2", | ||
"pretty-quick": "^3.0.2" | ||
}, | ||
"dependencies": { | ||
"clipboardy": "^2.3.0", | ||
"eslint": "^7.10.0", | ||
"html-minifier": "^4.0.0", | ||
"minify": "^6.0.0", | ||
"minimist": "^1.2.5", | ||
"puppeteer": "^5.3.1" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged" | ||
} | ||
} | ||
} |
Oops, something went wrong.