Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

app.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /* Magic Mirror
  2. * The Core App (Server)
  3. *
  4. * By Michael Teeuw https://michaelteeuw.nl
  5. * MIT Licensed.
  6. */
  7. // Alias modules mentioned in package.js under _moduleAliases.
  8. require("module-alias/register");
  9. const fs = require("fs");
  10. const path = require("path");
  11. const Log = require("logger");
  12. const Server = require(`${__dirname}/server`);
  13. const Utils = require(`${__dirname}/utils`);
  14. const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`);
  15. // Get version number.
  16. global.version = require(`${__dirname}/../package.json`).version;
  17. Log.log("Starting MagicMirror: v" + global.version);
  18. // global absolute root path
  19. global.root_path = path.resolve(`${__dirname}/../`);
  20. if (process.env.MM_CONFIG_FILE) {
  21. global.configuration_file = process.env.MM_CONFIG_FILE;
  22. }
  23. // FIXME: Hotfix Pull Request
  24. // https://github.com/MichMich/MagicMirror/pull/673
  25. if (process.env.MM_PORT) {
  26. global.mmPort = process.env.MM_PORT;
  27. }
  28. // The next part is here to prevent a major exception when there
  29. // is no internet connection. This could probable be solved better.
  30. process.on("uncaughtException", function (err) {
  31. Log.error("Whoops! There was an uncaught exception...");
  32. Log.error(err);
  33. Log.error("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
  34. Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
  35. });
  36. /**
  37. * The core app.
  38. *
  39. * @class
  40. */
  41. function App() {
  42. let nodeHelpers = [];
  43. let httpServer;
  44. /**
  45. * Loads the config file. Combines it with the defaults, and runs the
  46. * callback with the found config as argument.
  47. *
  48. * @param {Function} callback Function to be called after loading the config
  49. */
  50. function loadConfig(callback) {
  51. Log.log("Loading config ...");
  52. const defaults = require(`${__dirname}/defaults`);
  53. // For this check proposed to TestSuite
  54. // https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8
  55. const configFilename = path.resolve(global.configuration_file || `${global.root_path}/config/config.js`);
  56. try {
  57. fs.accessSync(configFilename, fs.F_OK);
  58. const c = require(configFilename);
  59. checkDeprecatedOptions(c);
  60. const config = Object.assign(defaults, c);
  61. callback(config);
  62. } catch (e) {
  63. if (e.code === "ENOENT") {
  64. Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
  65. } else if (e instanceof ReferenceError || e instanceof SyntaxError) {
  66. Log.error(Utils.colors.error(`WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: ${e.stack}`));
  67. } else {
  68. Log.error(Utils.colors.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`));
  69. }
  70. callback(defaults);
  71. }
  72. }
  73. /**
  74. * Checks the config for deprecated options and throws a warning in the logs
  75. * if it encounters one option from the deprecated.js list
  76. *
  77. * @param {object} userConfig The user config
  78. */
  79. function checkDeprecatedOptions(userConfig) {
  80. const deprecated = require(`${global.root_path}/js/deprecated`);
  81. const deprecatedOptions = deprecated.configs;
  82. const usedDeprecated = deprecatedOptions.filter((option) => userConfig.hasOwnProperty(option));
  83. if (usedDeprecated.length > 0) {
  84. Log.warn(Utils.colors.warn(`WARNING! Your config is using deprecated options: ${usedDeprecated.join(", ")}. Check README and CHANGELOG for more up-to-date ways of getting the same functionality.`));
  85. }
  86. }
  87. /**
  88. * Loads a specific module.
  89. *
  90. * @param {string} module The name of the module (including subpath).
  91. * @param {Function} callback Function to be called after loading
  92. */
  93. function loadModule(module, callback) {
  94. const elements = module.split("/");
  95. const moduleName = elements[elements.length - 1];
  96. let moduleFolder = `${__dirname}/../modules/${module}`;
  97. if (defaultModules.includes(moduleName)) {
  98. moduleFolder = `${__dirname}/../modules/default/${module}`;
  99. }
  100. const helperPath = `${moduleFolder}/node_helper.js`;
  101. let loadHelper = true;
  102. try {
  103. fs.accessSync(helperPath, fs.R_OK);
  104. } catch (e) {
  105. loadHelper = false;
  106. Log.log(`No helper found for module: ${moduleName}.`);
  107. }
  108. if (loadHelper) {
  109. const Module = require(helperPath);
  110. let m = new Module();
  111. if (m.requiresVersion) {
  112. Log.log(`Check MagicMirror version for node helper '${moduleName}' - Minimum version: ${m.requiresVersion} - Current version: ${global.version}`);
  113. if (cmpVersions(global.version, m.requiresVersion) >= 0) {
  114. Log.log("Version is ok!");
  115. } else {
  116. Log.warn(`Version is incorrect. Skip module: '${moduleName}'`);
  117. return;
  118. }
  119. }
  120. m.setName(moduleName);
  121. m.setPath(path.resolve(moduleFolder));
  122. nodeHelpers.push(m);
  123. m.loaded(callback);
  124. } else {
  125. callback();
  126. }
  127. }
  128. /**
  129. * Loads all modules.
  130. *
  131. * @param {Module[]} modules All modules to be loaded
  132. * @param {Function} callback Function to be called after loading
  133. */
  134. function loadModules(modules, callback) {
  135. Log.log("Loading module helpers ...");
  136. /**
  137. *
  138. */
  139. function loadNextModule() {
  140. if (modules.length > 0) {
  141. const nextModule = modules[0];
  142. loadModule(nextModule, function () {
  143. modules = modules.slice(1);
  144. loadNextModule();
  145. });
  146. } else {
  147. // All modules are loaded
  148. Log.log("All module helpers loaded.");
  149. callback();
  150. }
  151. }
  152. loadNextModule();
  153. }
  154. /**
  155. * Compare two semantic version numbers and return the difference.
  156. *
  157. * @param {string} a Version number a.
  158. * @param {string} b Version number b.
  159. * @returns {number} A positive number if a is larger than b, a negative
  160. * number if a is smaller and 0 if they are the same
  161. */
  162. function cmpVersions(a, b) {
  163. let i, diff;
  164. const regExStrip0 = /(\.0+)+$/;
  165. const segmentsA = a.replace(regExStrip0, "").split(".");
  166. const segmentsB = b.replace(regExStrip0, "").split(".");
  167. const l = Math.min(segmentsA.length, segmentsB.length);
  168. for (i = 0; i < l; i++) {
  169. diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
  170. if (diff) {
  171. return diff;
  172. }
  173. }
  174. return segmentsA.length - segmentsB.length;
  175. }
  176. /**
  177. * Start the core app.
  178. *
  179. * It loads the config, then it loads all modules. When it's done it
  180. * executes the callback with the config as argument.
  181. *
  182. * @param {Function} callback Function to be called after start
  183. */
  184. this.start = function (callback) {
  185. loadConfig(function (c) {
  186. config = c;
  187. Log.setLogLevel(config.logLevel);
  188. let modules = [];
  189. for (const module of config.modules) {
  190. if (!modules.includes(module.module) && !module.disabled) {
  191. modules.push(module.module);
  192. }
  193. }
  194. loadModules(modules, function () {
  195. httpServer = new Server(config, function (app, io) {
  196. Log.log("Server started ...");
  197. for (let nodeHelper of nodeHelpers) {
  198. nodeHelper.setExpressApp(app);
  199. nodeHelper.setSocketIO(io);
  200. nodeHelper.start();
  201. }
  202. Log.log("Sockets connected & modules started ...");
  203. if (typeof callback === "function") {
  204. callback(config);
  205. }
  206. });
  207. });
  208. });
  209. };
  210. /**
  211. * Stops the core app. This calls each node_helper's STOP() function, if it
  212. * exists.
  213. *
  214. * Added to fix #1056
  215. */
  216. this.stop = function () {
  217. for (const nodeHelper of nodeHelpers) {
  218. if (typeof nodeHelper.stop === "function") {
  219. nodeHelper.stop();
  220. }
  221. }
  222. httpServer.close();
  223. };
  224. /**
  225. * Listen for SIGINT signal and call stop() function.
  226. *
  227. * Added to fix #1056
  228. * Note: this is only used if running `server-only`. Otherwise
  229. * this.stop() is called by app.on("before-quit"... in `electron.js`
  230. */
  231. process.on("SIGINT", () => {
  232. Log.log("[SIGINT] Received. Shutting down server...");
  233. setTimeout(() => {
  234. process.exit(0);
  235. }, 3000); // Force quit after 3 seconds
  236. this.stop();
  237. process.exit(0);
  238. });
  239. /**
  240. * Listen to SIGTERM signals so we can stop everything when we
  241. * are asked to stop by the OS.
  242. */
  243. process.on("SIGTERM", () => {
  244. Log.log("[SIGTERM] Received. Shutting down server...");
  245. setTimeout(() => {
  246. process.exit(0);
  247. }, 3000); // Force quit after 3 seconds
  248. this.stop();
  249. process.exit(0);
  250. });
  251. }
  252. module.exports = new App();