mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-10-17 01:42:18 +00:00
Release 2.33.0 (#3903)
This commit is contained in:
committed by
GitHub
parent
62b0f7f26e
commit
b0c5924019
18
js/app.js
18
js/app.js
@@ -6,26 +6,26 @@ const path = require("node:path");
|
||||
const envsub = require("envsub");
|
||||
const Log = require("logger");
|
||||
|
||||
// global absolute root path
|
||||
global.root_path = path.resolve(`${__dirname}/../`);
|
||||
|
||||
const Server = require(`${__dirname}/server`);
|
||||
const Utils = require(`${__dirname}/utils`);
|
||||
const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`);
|
||||
const { getEnvVarsAsObj } = require(`${__dirname}/server_functions`);
|
||||
|
||||
const defaultModules = require(`${global.root_path}/modules/default/defaultmodules`);
|
||||
// used to control fetch timeout for node_helpers
|
||||
const { setGlobalDispatcher, Agent } = require("undici");
|
||||
const { getEnvVarsAsObj } = require("#server_functions");
|
||||
// common timeout value, provide environment override in case
|
||||
const fetch_timeout = process.env.mmFetchTimeout !== undefined ? process.env.mmFetchTimeout : 30000;
|
||||
|
||||
// Get version number.
|
||||
global.version = require(`${__dirname}/../package.json`).version;
|
||||
global.version = require(`${global.root_path}/package.json`).version;
|
||||
global.mmTestMode = process.env.mmTestMode === "true";
|
||||
Log.log(`Starting MagicMirror: v${global.version}`);
|
||||
|
||||
// Log system information.
|
||||
Utils.logSystemInformation();
|
||||
|
||||
// global absolute root path
|
||||
global.root_path = path.resolve(`${__dirname}/../`);
|
||||
Utils.logSystemInformation(global.version);
|
||||
|
||||
if (process.env.MM_CONFIG_FILE) {
|
||||
global.configuration_file = process.env.MM_CONFIG_FILE.replace(`${global.root_path}/`, "");
|
||||
@@ -181,10 +181,10 @@ function App () {
|
||||
const elements = module.split("/");
|
||||
const moduleName = elements[elements.length - 1];
|
||||
const env = getEnvVarsAsObj();
|
||||
let moduleFolder = path.resolve(`${__dirname}/../${env.modulesDir}`, module);
|
||||
let moduleFolder = path.resolve(`${global.root_path}/${env.modulesDir}`, module);
|
||||
|
||||
if (defaultModules.includes(moduleName)) {
|
||||
const defaultModuleFolder = path.resolve(`${__dirname}/../modules/default/`, module);
|
||||
const defaultModuleFolder = path.resolve(`${global.root_path}/modules/default/`, module);
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
moduleFolder = defaultModuleFolder;
|
||||
} else {
|
||||
|
14
js/main.js
14
js/main.js
@@ -90,7 +90,7 @@ const MM = (function () {
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
* @param {Module} [sendTo] The (optional) module to send the notification to.
|
||||
*/
|
||||
@@ -262,7 +262,7 @@ const MM = (function () {
|
||||
* Hide the module.
|
||||
* @param {Module} module The module to hide.
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
const hideModule = function (module, speed, callback, options = {}) {
|
||||
@@ -347,7 +347,7 @@ const MM = (function () {
|
||||
* Show the module.
|
||||
* @param {Module} module The module to show.
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
const showModule = function (module, speed, callback, options = {}) {
|
||||
@@ -552,7 +552,7 @@ const MM = (function () {
|
||||
|
||||
/**
|
||||
* Walks thru a collection of modules and executes the callback with the module as an argument.
|
||||
* @param {Function} callback The function to execute with the module as an argument.
|
||||
* @param {module} callback The function to execute with the module as an argument.
|
||||
*/
|
||||
const enumerate = function (callback) {
|
||||
modules.map(function (module) {
|
||||
@@ -629,7 +629,7 @@ const MM = (function () {
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
sendNotification (notification, payload, sender) {
|
||||
@@ -688,7 +688,7 @@ const MM = (function () {
|
||||
* Hide the module.
|
||||
* @param {Module} module The module to hide.
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hideModule (module, speed, callback, options) {
|
||||
@@ -700,7 +700,7 @@ const MM = (function () {
|
||||
* Show the module.
|
||||
* @param {Module} module The module to show.
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
showModule (module, speed, callback, options) {
|
||||
|
14
js/module.js
14
js/module.js
@@ -68,7 +68,7 @@ const Module = Class.extend({
|
||||
* Returns a map of translation files the module requires to be loaded.
|
||||
*
|
||||
* return Map<String, String> -
|
||||
* @returns {*} A map with langKeys and filenames.
|
||||
* @returns {Map} A map with langKeys and filenames.
|
||||
*/
|
||||
getTranslations () {
|
||||
return false;
|
||||
@@ -140,7 +140,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Called by the MagicMirror² core when a notification arrives.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
notificationReceived (notification, payload, sender) {
|
||||
@@ -176,7 +176,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Called when a socket notification arrives.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived (notification, payload) {
|
||||
Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
|
||||
@@ -344,7 +344,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
*/
|
||||
sendNotification (notification, payload) {
|
||||
MM.sendNotification(notification, payload, this);
|
||||
@@ -353,7 +353,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Send a socket notification to the node helper.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification (notification, payload) {
|
||||
this.socket().sendNotification(notification, payload);
|
||||
@@ -362,7 +362,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Hide this module.
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hide (speed, callback, options = {}) {
|
||||
@@ -389,7 +389,7 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Show this module.
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {Promise} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
show (speed, callback, options) {
|
||||
|
18
js/module_functions.js
Normal file
18
js/module_functions.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Schedule the timer for the next update
|
||||
* @param {object} timer The timer of the module
|
||||
* @param {bigint} intervalMS interval in milliseconds
|
||||
* @param {Promise} callback function to call when the timer expires
|
||||
*/
|
||||
const scheduleTimer = function (timer, intervalMS, callback) {
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
// only set timer when not running in jest
|
||||
let tmr = timer;
|
||||
clearTimeout(tmr);
|
||||
tmr = setTimeout(function () {
|
||||
callback();
|
||||
}, intervalMS);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { scheduleTimer };
|
@@ -27,7 +27,7 @@ const NodeHelper = Class.extend({
|
||||
/**
|
||||
* This method is called when a socket notification arrives.
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {object} payload The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived (notification, payload) {
|
||||
Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
|
||||
|
29
js/server.js
29
js/server.js
@@ -7,7 +7,7 @@ const ipfilter = require("express-ipfilter").IpFilter;
|
||||
const helmet = require("helmet");
|
||||
const socketio = require("socket.io");
|
||||
const Log = require("logger");
|
||||
const { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars } = require("./server_functions");
|
||||
const { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars } = require("#server_functions");
|
||||
|
||||
const vendor = require(`${__dirname}/vendor`);
|
||||
|
||||
@@ -42,7 +42,9 @@ function Server (config) {
|
||||
origin: /.*$/,
|
||||
credentials: true
|
||||
},
|
||||
allowEIO3: true
|
||||
allowEIO3: true,
|
||||
pingInterval: 120000, // server → client ping every 2 mins
|
||||
pingTimeout: 120000 // wait up to 2 mins for client pong
|
||||
});
|
||||
|
||||
server.on("connection", (socket) => {
|
||||
@@ -53,6 +55,29 @@ function Server (config) {
|
||||
});
|
||||
|
||||
Log.log(`Starting server on port ${port} ... `);
|
||||
|
||||
// Add explicit error handling BEFORE calling listen so we can give user-friendly feedback
|
||||
server.once("error", (err) => {
|
||||
if (err && err.code === "EADDRINUSE") {
|
||||
const bindAddr = config.address || "localhost";
|
||||
const portInUseMessage = [
|
||||
"",
|
||||
"────────────────────────────────────────────────────────────────",
|
||||
` PORT IN USE: ${bindAddr}:${port}`,
|
||||
"",
|
||||
" Another process (most likely another MagicMirror instance)",
|
||||
" is already using this port.",
|
||||
"",
|
||||
" Stop the other process (free the port) or use a different port.",
|
||||
"────────────────────────────────────────────────────────────────"
|
||||
].join("\n");
|
||||
Log.error(portInUseMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.error("Failed to start server:", err);
|
||||
});
|
||||
|
||||
server.listen(port, config.address || "localhost");
|
||||
|
||||
if (config.ipWhitelist instanceof Array && config.ipWhitelist.length === 0) {
|
||||
|
@@ -69,7 +69,7 @@ async function cors (req, res) {
|
||||
* @returns {object} An object specifying name and value of the headers.
|
||||
*/
|
||||
function getHeadersToSend (url) {
|
||||
const headersToSend = { "User-Agent": `Mozilla/5.0 MagicMirror/${global.version}` };
|
||||
const headersToSend = { "User-Agent": getUserAgent() };
|
||||
const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url);
|
||||
if (headersToSendMatch) {
|
||||
const headers = headersToSendMatch[1].split(",");
|
||||
@@ -129,6 +129,27 @@ function getVersion (req, res) {
|
||||
res.send(global.version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred `User-Agent`
|
||||
* @returns {string} `User-Agent` to be used
|
||||
*/
|
||||
function getUserAgent () {
|
||||
const defaultUserAgent = `Mozilla/5.0 (Node.js ${Number(process.version.match(/^v(\d+\.\d+)/)[1])}) MagicMirror/${global.version}`;
|
||||
|
||||
if (typeof config === "undefined") {
|
||||
return defaultUserAgent;
|
||||
}
|
||||
|
||||
switch (typeof config.userAgent) {
|
||||
case "function":
|
||||
return config.userAgent();
|
||||
case "string":
|
||||
return config.userAgent;
|
||||
default:
|
||||
return defaultUserAgent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets environment variables needed in the browser.
|
||||
* @returns {object} environment variables key: values
|
||||
@@ -155,4 +176,4 @@ function getEnvVars (req, res) {
|
||||
res.send(obj);
|
||||
}
|
||||
|
||||
module.exports = { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars, getEnvVarsAsObj };
|
||||
module.exports = { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars, getEnvVarsAsObj, getUserAgent };
|
||||
|
@@ -13,7 +13,9 @@ const MMSocket = function (moduleName) {
|
||||
base = config.basePath;
|
||||
}
|
||||
this.socket = io(`/${this.moduleName}`, {
|
||||
path: `${base}socket.io`
|
||||
path: `${base}socket.io`,
|
||||
pingInterval: 120000, // send pings every 2 mins
|
||||
pingTimeout: 120000 // wait up to 2 mins for a pong
|
||||
});
|
||||
|
||||
let notificationCallback = function () {};
|
||||
|
38
js/utils.js
38
js/utils.js
@@ -1,4 +1,3 @@
|
||||
const execSync = require("node:child_process").execSync;
|
||||
const path = require("node:path");
|
||||
|
||||
const rootPath = path.resolve(`${__dirname}/../`);
|
||||
@@ -14,27 +13,34 @@ const discoveredPositionsJSFilename = "js/positions.js";
|
||||
|
||||
module.exports = {
|
||||
|
||||
async logSystemInformation () {
|
||||
async logSystemInformation (mirrorVersion) {
|
||||
try {
|
||||
let installedNodeVersion = execSync("node -v", { encoding: "utf-8" }).replace("v", "").replace(/(?:\r\n|\r|\n)/g, "");
|
||||
const system = await si.system();
|
||||
const osInfo = await si.osInfo();
|
||||
const versions = await si.versions();
|
||||
|
||||
const staticData = await si.get({
|
||||
system: "manufacturer, model, virtual",
|
||||
osInfo: "platform, distro, release, arch",
|
||||
versions: "kernel, node, npm, pm2"
|
||||
});
|
||||
let systemDataString = `System information:
|
||||
### SYSTEM: manufacturer: ${staticData.system.manufacturer}; model: ${staticData.system.model}; virtual: ${staticData.system.virtual}
|
||||
### OS: platform: ${staticData.osInfo.platform}; distro: ${staticData.osInfo.distro}; release: ${staticData.osInfo.release}; arch: ${staticData.osInfo.arch}; kernel: ${staticData.versions.kernel}
|
||||
### VERSIONS: electron: ${process.versions.electron}; used node: ${staticData.versions.node}; installed node: ${installedNodeVersion}; npm: ${staticData.versions.npm}; pm2: ${staticData.versions.pm2}
|
||||
### OTHER: timeZone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}; ELECTRON_ENABLE_GPU: ${process.env.ELECTRON_ENABLE_GPU}`
|
||||
.replace(/\t/g, "");
|
||||
const usedNodeVersion = process.version.replace("v", "");
|
||||
const installedNodeVersion = versions.node;
|
||||
const totalRam = (os.totalmem() / 1024 / 1024).toFixed(2);
|
||||
const freeRam = (os.freemem() / 1024 / 1024).toFixed(2);
|
||||
const usedRam = ((os.totalmem() - os.freemem()) / 1024 / 1024).toFixed(2);
|
||||
|
||||
let systemDataString = [
|
||||
"\n#### System Information ####",
|
||||
`- SYSTEM: manufacturer: ${system.manufacturer}; model: ${system.model}; virtual: ${system.virtual}; MM: ${mirrorVersion}`,
|
||||
`- OS: platform: ${osInfo.platform}; distro: ${osInfo.distro}; release: ${osInfo.release}; arch: ${osInfo.arch}; kernel: ${versions.kernel}`,
|
||||
`- VERSIONS: electron: ${process.versions.electron}; used node: ${usedNodeVersion}; installed node: ${installedNodeVersion}; npm: ${versions.npm}; pm2: ${versions.pm2}`,
|
||||
`- ENV: XDG_SESSION_TYPE: ${process.env.XDG_SESSION_TYPE}; MM_CONFIG_FILE: ${process.env.MM_CONFIG_FILE}`,
|
||||
` WAYLAND_DISPLAY: ${process.env.WAYLAND_DISPLAY}; DISPLAY: ${process.env.DISPLAY}; ELECTRON_ENABLE_GPU: ${process.env.ELECTRON_ENABLE_GPU}`,
|
||||
`- RAM: total: ${totalRam} MB; free: ${freeRam} MB; used: ${usedRam} MB`,
|
||||
`- OTHERS: uptime: ${Math.floor(os.uptime() / 60)} minutes; timeZone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`
|
||||
].join("\n");
|
||||
Log.info(systemDataString);
|
||||
|
||||
// Return is currently only for jest
|
||||
return systemDataString;
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
} catch (error) {
|
||||
Log.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user