From dc6a2cb7f3cb93645fe1c691ee08188b188cd77f Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Sun, 1 Sep 2024 21:13:29 +0200 Subject: [PATCH 1/4] use new env vars MM_MODULES_DIR and MM_CUSTOMCSS_FILE for setting these things from outside (and overriding corresponding config.js properties) --- CHANGELOG.md | 2 ++ js/app.js | 4 +++- js/loader.js | 22 +++++++++++++++++----- js/server.js | 12 ++++++++---- js/server_functions.js | 28 +++++++++++++++++++++++++++- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6ac2d10b..188899a074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ _This release is scheduled to be released on 2024-10-01._ ### Added - [core] Add spelling check (cspell): `npm run test:spelling` and handle spelling issues +- [core] add variable `MM_CUSTOMCSS_FILE` which - if set - overrides `config.customCss` +- [core] add variable `MM_MODULES_DIR` which - if set - overrides `config.paths.modules` for foreign modules (not default modules) ### Removed diff --git a/js/app.js b/js/app.js index f6d510a64a..3bdf47ceca 100644 --- a/js/app.js +++ b/js/app.js @@ -9,6 +9,7 @@ const Log = require("logger"); const Server = require(`${__dirname}/server`); const Utils = require(`${__dirname}/utils`); const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`); +const { getEnvVarsAsObj } = require(`${__dirname}/server_functions`); // Get version number. global.version = require(`${__dirname}/../package.json`).version; @@ -159,7 +160,8 @@ function App () { function loadModule (module) { const elements = module.split("/"); const moduleName = elements[elements.length - 1]; - let moduleFolder = `${__dirname}/../modules/${module}`; + const env = getEnvVarsAsObj(); + let moduleFolder = `${__dirname}/../${env.modulesDir}/${module}`; if (defaultModules.includes(moduleName)) { moduleFolder = `${__dirname}/../modules/default/${module}`; diff --git a/js/loader.js b/js/loader.js index e8dff1907c..362f3690ff 100644 --- a/js/loader.js +++ b/js/loader.js @@ -10,6 +10,15 @@ const Loader = (function () { /* Private Methods */ + /** + * Retrieve object of env variables. + * @returns {object} with key: values as assembled in js/server_functions.js + */ + const getEnvVars = async function () { + const res = await fetch(`${location.protocol}//${location.host}/env`); + return JSON.parse(await res.text()); + }; + /** * Loops through all modules and requests start for every module. */ @@ -58,19 +67,20 @@ const Loader = (function () { * Generate array with module information including module paths. * @returns {object[]} Module information. */ - const getModuleData = function () { + const getModuleData = async function () { const modules = getAllModules(); const moduleFiles = []; + const envVars = await getEnvVars(); modules.forEach(function (moduleData, index) { const module = moduleData.module; const elements = module.split("/"); const moduleName = elements[elements.length - 1]; - let moduleFolder = `${config.paths.modules}/${module}`; + let moduleFolder = `${envVars.modulesDir}/${module}`; if (defaultModules.indexOf(moduleName) !== -1) { - moduleFolder = `${config.paths.modules}/default/${module}`; + moduleFolder = `modules/default/${module}`; } if (moduleData.disabled === true) { @@ -197,7 +207,9 @@ const Loader = (function () { * Load all modules as defined in the config. */ async loadModules () { - let moduleData = getModuleData(); + let moduleData = await getModuleData(); + const envVars = await getEnvVars(); + const customCss = envVars.customCss; /** * @returns {Promise} when all modules are loaded @@ -212,7 +224,7 @@ const Loader = (function () { // All modules loaded. Load custom.css // This is done after all the modules so we can // overwrite all the defined styles. - await loadFile(config.customCss); + await loadFile(customCss); // custom.css loaded. Start all modules. await startModules(); } diff --git a/js/server.js b/js/server.js index bfa0f911d7..0830034d32 100644 --- a/js/server.js +++ b/js/server.js @@ -8,8 +8,7 @@ const helmet = require("helmet"); const socketio = require("socket.io"); const Log = require("logger"); -const Utils = require("./utils"); -const { cors, getConfig, getHtml, getVersion, getStartup } = require("./server_functions"); +const { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars } = require("./server_functions"); /** * Server @@ -73,8 +72,11 @@ function Server (config) { app.use(helmet(config.httpHeaders)); app.use("/js", express.static(__dirname)); - // TODO add tests directory only when running tests? - const directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/tests/configs", "/tests/mocks"]; + let directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/env"]; + if (process.env.JEST_WORKER_ID !== undefined) { + // add tests directories only when running tests + directories.push("/tests/configs", "/tests/mocks"); + } for (const directory of directories) { app.use(directory, express.static(path.resolve(global.root_path + directory))); } @@ -87,6 +89,8 @@ function Server (config) { app.get("/startup", (req, res) => getStartup(req, res)); + app.get("/env", (req, res) => getEnvVars(req, res)); + app.get("/", (req, res) => getHtml(req, res)); server.on("listening", () => { diff --git a/js/server_functions.js b/js/server_functions.js index 73d11c0464..8d3c961a2e 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -128,4 +128,30 @@ function getVersion (req, res) { res.send(global.version); } -module.exports = { cors, getConfig, getHtml, getVersion, getStartup }; +/** + * Gets environment variables needed in the browser. + * @returns {object} environment variables key: values + */ +function getEnvVarsAsObj () { + const obj = { modulesDir: `${config.paths.modules}`, customCss: `${config.customCss}` }; + if (process.env.MM_MODULES_DIR) { + obj.modulesDir = process.env.MM_MODULES_DIR.replace(`${global.root_path}/`, ""); + } + if (process.env.MM_CUSTOMCSS_FILE) { + obj.customCss = process.env.MM_CUSTOMCSS_FILE.replace(`${global.root_path}/`, ""); + } + + return obj; +} + +/** + * Gets environment variables needed in the browser. + * @param {Request} req - the request + * @param {Response} res - the result + */ +function getEnvVars (req, res) { + const obj = getEnvVarsAsObj(); + res.send(obj); +} + +module.exports = { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars, getEnvVarsAsObj }; From 8811e0c9ac2b74d86be59a12e70a4fe2bbf87798 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Sat, 7 Sep 2024 18:37:12 +0200 Subject: [PATCH 2/4] publishing /env not needed --- js/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/server.js b/js/server.js index 0830034d32..42b3ff6692 100644 --- a/js/server.js +++ b/js/server.js @@ -72,7 +72,7 @@ function Server (config) { app.use(helmet(config.httpHeaders)); app.use("/js", express.static(__dirname)); - let directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/env"]; + let directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations"]; if (process.env.JEST_WORKER_ID !== undefined) { // add tests directories only when running tests directories.push("/tests/configs", "/tests/mocks"); From 4f38993f18be6449ae51880c4fc34322bf35f9b0 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 11 Sep 2024 21:49:10 +0200 Subject: [PATCH 3/4] removed `config.paths.vendor and renamed `config.paths.modules` to `config.foreignModulesDir` --- CHANGELOG.md | 3 ++- js/defaults.js | 8 ++------ js/loader.js | 2 +- js/server_functions.js | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 188899a074..82d5a0e5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ _This release is scheduled to be released on 2024-10-01._ ### Added - [core] Add spelling check (cspell): `npm run test:spelling` and handle spelling issues +- [core] removed `config.paths.vendor` (could not work because `vendor` is hardcoded in `index.html`), renamed `config.paths.modules` to `config.foreignModulesDir` - [core] add variable `MM_CUSTOMCSS_FILE` which - if set - overrides `config.customCss` -- [core] add variable `MM_MODULES_DIR` which - if set - overrides `config.paths.modules` for foreign modules (not default modules) +- [core] add variable `MM_MODULES_DIR` which - if set - overrides `config.foreignModulesDir` ### Removed diff --git a/js/defaults.js b/js/defaults.js index c4efa77e95..f3a894d6a9 100644 --- a/js/defaults.js +++ b/js/defaults.js @@ -19,6 +19,7 @@ const defaults = { units: "metric", zoom: 1, customCss: "css/custom.css", + foreignModulesDir: "modules", // httpHeaders used by helmet, see https://helmetjs.github.io/. You can add other/more object values by overriding this in config.js, // e.g. you need to add `frameguard: false` for embedding MagicMirror in another website, see https://github.com/MagicMirrorOrg/MagicMirror/issues/2847 httpHeaders: { contentSecurityPolicy: false, crossOriginOpenerPolicy: false, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: false, originAgentCluster: false }, @@ -72,12 +73,7 @@ const defaults = { text: "www.michaelteeuw.nl" } } - ], - - paths: { - modules: "modules", - vendor: "vendor" - } + ] }; /*************** DO NOT EDIT THE LINE BELOW ***************/ diff --git a/js/loader.js b/js/loader.js index 362f3690ff..02a18d44de 100644 --- a/js/loader.js +++ b/js/loader.js @@ -256,7 +256,7 @@ const Loader = (function () { // This file is available in the vendor folder. // Load it from this vendor folder. loadedFiles.push(fileName.toLowerCase()); - return loadFile(`${config.paths.vendor}/${vendor[fileName]}`); + return loadFile(`vendor/${vendor[fileName]}`); } // File not loaded yet. diff --git a/js/server_functions.js b/js/server_functions.js index 8d3c961a2e..65928d732e 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -133,7 +133,7 @@ function getVersion (req, res) { * @returns {object} environment variables key: values */ function getEnvVarsAsObj () { - const obj = { modulesDir: `${config.paths.modules}`, customCss: `${config.customCss}` }; + const obj = { modulesDir: `${config.foreignModulesDir}`, customCss: `${config.customCss}` }; if (process.env.MM_MODULES_DIR) { obj.modulesDir = process.env.MM_MODULES_DIR.replace(`${global.root_path}/`, ""); } From 3016cef9028828353350e96c21206c6625ed0ed3 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 11 Sep 2024 22:20:29 +0200 Subject: [PATCH 4/4] remove elements from index.html when loading script or stylesheet files fails --- CHANGELOG.md | 5 ++--- js/loader.js | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d5a0e5b0..62ad64e240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,8 @@ _This release is scheduled to be released on 2024-10-01._ ### Added - [core] Add spelling check (cspell): `npm run test:spelling` and handle spelling issues -- [core] removed `config.paths.vendor` (could not work because `vendor` is hardcoded in `index.html`), renamed `config.paths.modules` to `config.foreignModulesDir` -- [core] add variable `MM_CUSTOMCSS_FILE` which - if set - overrides `config.customCss` -- [core] add variable `MM_MODULES_DIR` which - if set - overrides `config.foreignModulesDir` +- [core] removed `config.paths.vendor` (could not work because `vendor` is hardcoded in `index.html`), renamed `config.paths.modules` to `config.foreignModulesDir`, added variable `MM_CUSTOMCSS_FILE` which - if set - overrides `config.customCss`, added variable `MM_MODULES_DIR` which - if set - overrides `config.foreignModulesDir` +- [core] elements are now removed from index.html when loading script or stylesheet files fails ### Removed diff --git a/js/loader.js b/js/loader.js index 02a18d44de..acc77d2801 100644 --- a/js/loader.js +++ b/js/loader.js @@ -176,6 +176,7 @@ const Loader = (function () { }; script.onerror = function () { Log.error("Error on loading script:", fileName); + script.remove(); resolve(); }; document.getElementsByTagName("body")[0].appendChild(script); @@ -193,6 +194,7 @@ const Loader = (function () { }; stylesheet.onerror = function () { Log.error("Error on loading stylesheet:", fileName); + stylesheet.remove(); resolve(); }; document.getElementsByTagName("head")[0].appendChild(stylesheet);