2016-04-08 22:16:22 +02:00
/ * M a g i c M i r r o r
* The Core App ( Server )
*
2020-04-28 23:05:28 +02:00
* By Michael Teeuw https : //michaelteeuw.nl
2016-04-08 22:16:22 +02:00
* MIT Licensed .
* /
var fs = require ( "fs" ) ;
2020-05-11 23:12:50 +02:00
var path = require ( "path" ) ;
var Log = require ( _ _dirname + "/logger.js" ) ;
2016-04-08 22:16:22 +02:00
var Server = require ( _ _dirname + "/server.js" ) ;
2017-02-28 01:41:21 -03:00
var Utils = require ( _ _dirname + "/utils.js" ) ;
2016-04-08 22:16:22 +02:00
var defaultModules = require ( _ _dirname + "/../modules/default/defaultmodules.js" ) ;
2020-01-01 19:17:23 +01:00
// Alias modules mentioned in package.js under _moduleAliases.
require ( "module-alias/register" ) ;
2016-10-13 16:42:15 +02:00
// Get version number.
global . version = JSON . parse ( fs . readFileSync ( "package.json" , "utf8" ) ) . version ;
2020-05-11 07:25:42 +02:00
Log . log ( "Starting MagicMirror: v" + global . version ) ;
2016-10-13 16:42:15 +02:00
2016-12-27 14:31:35 -03:00
// global absolute root path
global . root _path = path . resolve ( _ _dirname + "/../" ) ;
2017-01-24 02:59:20 -03:00
if ( process . env . MM _CONFIG _FILE ) {
global . configuration _file = process . env . MM _CONFIG _FILE ;
}
2017-02-24 18:52:33 -03:00
// FIXME: Hotfix Pull Request
// https://github.com/MichMich/MagicMirror/pull/673
2017-01-31 21:58:46 -03:00
if ( process . env . MM _PORT ) {
global . mmPort = process . env . MM _PORT ;
}
2016-04-19 11:25:50 +02:00
// The next part is here to prevent a major exception when there
// is no internet connection. This could probable be solved better.
2016-05-03 19:09:38 -04:00
process . on ( "uncaughtException" , function ( err ) {
2020-06-01 16:40:20 +02:00
Log . error ( "Whoops! There was an uncaught exception..." ) ;
2020-05-11 07:25:42 +02:00
Log . error ( err ) ;
2020-06-01 16:40:20 +02:00
Log . error ( "MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?" ) ;
Log . error ( "If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues" ) ;
2016-04-19 11:25:50 +02:00
} ) ;
2016-04-08 22:16:22 +02:00
/ * A p p - T h e c o r e a p p .
* /
2020-05-11 22:22:32 +02:00
var App = function ( ) {
2016-04-08 22:16:22 +02:00
var nodeHelpers = [ ] ;
/ * l o a d C o n f i g ( c a l l b a c k )
* Loads the config file . combines it with the defaults ,
* and runs the callback with the found config as argument .
*
* argument callback function - The callback function .
* /
2020-05-25 18:57:15 +02:00
var loadConfig = function ( callback ) {
2020-05-11 07:25:42 +02:00
Log . log ( "Loading config ..." ) ;
2016-04-08 22:16:22 +02:00
var defaults = require ( _ _dirname + "/defaults.js" ) ;
2017-01-24 02:59:20 -03:00
// For this check proposed to TestSuite
// https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8
2017-01-26 18:33:37 -03:00
var configFilename = path . resolve ( global . root _path + "/config/config.js" ) ;
2020-05-11 22:22:32 +02:00
if ( typeof global . configuration _file !== "undefined" ) {
2020-04-20 22:16:23 +02:00
configFilename = path . resolve ( global . configuration _file ) ;
2017-01-24 02:59:20 -03:00
}
2016-04-08 22:16:22 +02:00
try {
fs . accessSync ( configFilename , fs . F _OK ) ;
var c = require ( configFilename ) ;
2016-12-14 18:54:44 +01:00
checkDeprecatedOptions ( c ) ;
2016-04-08 22:16:22 +02:00
var config = Object . assign ( defaults , c ) ;
callback ( config ) ;
} catch ( e ) {
2019-06-05 08:48:22 +02:00
if ( e . code === "ENOENT" ) {
2020-05-11 07:25:42 +02:00
Log . error ( Utils . colors . error ( "WARNING! Could not find config file. Please create one. Starting with default configuration." ) ) ;
2016-08-05 19:18:31 +02:00
} else if ( e instanceof ReferenceError || e instanceof SyntaxError ) {
2020-05-11 07:25:42 +02:00
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 ) ) ;
2016-08-05 19:18:31 +02:00
} else {
2020-05-11 07:25:42 +02:00
Log . error ( Utils . colors . error ( "WARNING! Could not load config file. Starting with default configuration. Error found: " + e ) ) ;
2016-08-05 19:18:31 +02:00
}
2017-03-05 22:05:54 -03:00
callback ( defaults ) ;
2016-04-08 22:16:22 +02:00
}
} ;
2020-05-11 22:22:32 +02:00
var checkDeprecatedOptions = function ( userConfig ) {
2017-02-21 20:21:46 -03:00
var deprecated = require ( global . root _path + "/js/deprecated.js" ) ;
var deprecatedOptions = deprecated . configs ;
2016-12-14 18:54:44 +01:00
var usedDeprecated = [ ] ;
2020-05-11 22:22:32 +02:00
deprecatedOptions . forEach ( function ( option ) {
2016-12-14 18:54:44 +01:00
if ( userConfig . hasOwnProperty ( option ) ) {
usedDeprecated . push ( option ) ;
}
} ) ;
if ( usedDeprecated . length > 0 ) {
2020-05-25 18:57:15 +02:00
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." ) ) ;
2016-12-14 18:54:44 +01:00
}
2019-06-05 10:23:58 +02:00
} ;
2016-12-14 18:54:44 +01:00
2016-04-08 22:16:22 +02:00
/ * l o a d M o d u l e ( m o d u l e )
* Loads a specific module .
*
* argument module string - The name of the module ( including subpath ) .
* /
2020-05-11 22:22:32 +02:00
var loadModule = function ( module , callback ) {
2016-04-08 22:16:22 +02:00
var elements = module . split ( "/" ) ;
var moduleName = elements [ elements . length - 1 ] ;
2020-03-15 15:49:34 +01:00
var moduleFolder = _ _dirname + "/../modules/" + module ;
2016-04-08 22:16:22 +02:00
if ( defaultModules . indexOf ( moduleName ) !== - 1 ) {
2020-03-15 15:49:34 +01:00
moduleFolder = _ _dirname + "/../modules/default/" + module ;
2016-04-08 22:16:22 +02:00
}
var helperPath = moduleFolder + "/node_helper.js" ;
var loadModule = true ;
try {
fs . accessSync ( helperPath , fs . R _OK ) ;
} catch ( e ) {
loadModule = false ;
2020-05-11 07:25:42 +02:00
Log . log ( "No helper found for module: " + moduleName + "." ) ;
2016-04-08 22:16:22 +02:00
}
if ( loadModule ) {
var Module = require ( helperPath ) ;
var m = new Module ( ) ;
2016-10-13 16:42:15 +02:00
if ( m . requiresVersion ) {
2020-05-11 07:25:42 +02:00
Log . log ( "Check MagicMirror version for node helper '" + moduleName + "' - Minimum version: " + m . requiresVersion + " - Current version: " + global . version ) ;
2016-10-13 16:42:15 +02:00
if ( cmpVersions ( global . version , m . requiresVersion ) >= 0 ) {
2020-05-11 07:25:42 +02:00
Log . log ( "Version is ok!" ) ;
2016-10-13 16:42:15 +02:00
} else {
2020-05-11 07:25:42 +02:00
Log . log ( "Version is incorrect. Skip module: '" + moduleName + "'" ) ;
2016-10-13 16:42:15 +02:00
return ;
}
}
2016-04-08 22:16:22 +02:00
m . setName ( moduleName ) ;
m . setPath ( path . resolve ( moduleFolder ) ) ;
nodeHelpers . push ( m ) ;
2016-12-07 17:19:05 +01:00
m . loaded ( callback ) ;
} else {
callback ( ) ;
2016-04-08 22:16:22 +02:00
}
} ;
/ * l o a d M o d u l e s ( m o d u l e s )
* Loads all modules .
*
* argument module string - The name of the module ( including subpath ) .
* /
2020-05-25 18:57:15 +02:00
var loadModules = function ( modules , callback ) {
2020-05-11 07:25:42 +02:00
Log . log ( "Loading module helpers ..." ) ;
2016-04-08 22:16:22 +02:00
2020-05-11 22:22:32 +02:00
var loadNextModule = function ( ) {
2016-12-07 17:19:05 +01:00
if ( modules . length > 0 ) {
var nextModule = modules [ 0 ] ;
2020-05-11 22:22:32 +02:00
loadModule ( nextModule , function ( ) {
2016-12-07 17:19:05 +01:00
modules = modules . slice ( 1 ) ;
loadNextModule ( ) ;
} ) ;
} else {
// All modules are loaded
2020-05-11 07:25:42 +02:00
Log . log ( "All module helpers loaded." ) ;
2016-12-07 17:19:05 +01:00
callback ( ) ;
}
} ;
2016-04-08 22:16:22 +02:00
2016-12-07 17:19:05 +01:00
loadNextModule ( ) ;
2016-04-08 22:16:22 +02:00
} ;
2020-07-27 14:24:30 +02:00
/ * *
2019-06-04 09:33:53 +02:00
* Compare two semantic version numbers and return the difference .
2016-10-13 16:42:15 +02:00
*
2020-07-27 14:24:30 +02:00
* @ param { string } a Version number a .
* @ param { string } b Version number b .
2016-10-13 16:42:15 +02:00
* /
function cmpVersions ( a , b ) {
var i , diff ;
var regExStrip0 = /(\.0+)+$/ ;
var segmentsA = a . replace ( regExStrip0 , "" ) . split ( "." ) ;
var segmentsB = b . replace ( regExStrip0 , "" ) . split ( "." ) ;
var l = Math . min ( segmentsA . length , segmentsB . length ) ;
for ( i = 0 ; i < l ; i ++ ) {
diff = parseInt ( segmentsA [ i ] , 10 ) - parseInt ( segmentsB [ i ] , 10 ) ;
if ( diff ) {
return diff ;
}
}
return segmentsA . length - segmentsB . length ;
}
2016-04-08 22:16:22 +02:00
/ * s t a r t ( c a l l b a c k )
* This methods starts the core app .
* It loads the config , then it loads all modules .
2019-06-04 09:33:53 +02:00
* When it ' s done it executes the callback with the config as argument .
2016-04-08 22:16:22 +02:00
*
* argument callback function - The callback function .
* /
2020-05-11 22:22:32 +02:00
this . start = function ( callback ) {
loadConfig ( function ( c ) {
2016-04-08 22:16:22 +02:00
config = c ;
2020-07-04 22:02:39 +02:00
Log . setLogLevel ( config . logLevel ) ;
2016-04-08 22:16:22 +02:00
var modules = [ ] ;
for ( var m in config . modules ) {
var module = config . modules [ m ] ;
2016-11-14 14:43:30 -03:00
if ( modules . indexOf ( module . module ) === - 1 && ! module . disabled ) {
2016-04-08 22:16:22 +02:00
modules . push ( module . module ) ;
}
}
2020-05-25 18:57:15 +02:00
loadModules ( modules , function ( ) {
var server = new Server ( config , function ( app , io ) {
2020-05-11 07:25:42 +02:00
Log . log ( "Server started ..." ) ;
2016-04-08 22:16:22 +02:00
2016-12-07 17:19:05 +01:00
for ( var h in nodeHelpers ) {
var nodeHelper = nodeHelpers [ h ] ;
nodeHelper . setExpressApp ( app ) ;
nodeHelper . setSocketIO ( io ) ;
nodeHelper . start ( ) ;
}
2016-04-08 22:16:22 +02:00
2020-05-11 07:25:42 +02:00
Log . log ( "Sockets connected & modules started ..." ) ;
2016-04-08 22:16:22 +02:00
2016-12-07 17:19:05 +01:00
if ( typeof callback === "function" ) {
callback ( config ) ;
}
} ) ;
2016-04-08 22:16:22 +02:00
} ) ;
} ) ;
} ;
2017-10-13 16:43:11 -05:00
/ * s t o p ( )
* This methods stops the core app .
* This calls each node _helper ' s STOP ( ) function , if it exists .
* Added to fix # 1056
* /
2020-05-11 22:22:32 +02:00
this . stop = function ( ) {
2017-10-13 16:43:11 -05:00
for ( var h in nodeHelpers ) {
var nodeHelper = nodeHelpers [ h ] ;
if ( typeof nodeHelper . stop === "function" ) {
nodeHelper . stop ( ) ;
}
}
} ;
/ * L i s t e n f o r S I G I N T s i g n a l a n d c a l l s t o p ( ) f u n c t i o n .
*
* Added to fix # 1056
* Note : this is only used if running ` server-only ` . Otherwise
* this . stop ( ) is called by app . on ( "before-quit" ... in ` electron.js `
* /
process . on ( "SIGINT" , ( ) => {
2020-05-11 07:25:42 +02:00
Log . log ( "[SIGINT] Received. Shutting down server..." ) ;
2020-05-25 18:57:15 +02:00
setTimeout ( ( ) => {
process . exit ( 0 ) ;
} , 3000 ) ; // Force quit after 3 seconds
2017-10-13 16:43:11 -05:00
this . stop ( ) ;
process . exit ( 0 ) ;
} ) ;
2019-04-07 16:41:05 +02:00
2019-04-06 20:50:54 +02:00
/ * W e a l s o n e e d t o l i s t e n t o S I G T E R M s i g n a l s s o w e s t o p e v e r y t h i n g w h e n w e a r e a s k e d t o s t o p b y t h e O S .
* /
process . on ( "SIGTERM" , ( ) => {
2020-05-11 07:25:42 +02:00
Log . log ( "[SIGTERM] Received. Shutting down server..." ) ;
2020-05-25 18:57:15 +02:00
setTimeout ( ( ) => {
process . exit ( 0 ) ;
} , 3000 ) ; // Force quit after 3 seconds
2019-04-06 20:50:54 +02:00
this . stop ( ) ;
process . exit ( 0 ) ;
} ) ;
2016-04-08 22:16:22 +02:00
} ;
2016-11-14 14:43:30 -03:00
module . exports = new App ( ) ;