UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

321 lines (273 loc) 10.6 kB
'use strict'; //////////////////////////////////////////////////////////////////////////// // actionhero framework in node.js // http://www.actionherojs.com // https://github.com/evantahler/actionhero var path = require('path'); var async = require('async'); // HELPERS /// var fatalError = function(api, errors, type){ if(errors && !(errors instanceof Array)){ errors = [errors]; } if(errors){ api.log(['Error with initializer step: %s', type], 'emerg'); errors.forEach(function(error){ api.log(error.stack, 'emerg'); }); api.commands.stop.call(api, function(){ process.exit(1); }); } }; var sortNumber = function(a, b){ return a - b; }; var flattenOrderedInitialzer = function(collection){ var output = []; var keys = []; for(var key in collection){ keys.push(parseInt(key)); } keys.sort(sortNumber); keys.forEach(function(key){ collection[key].forEach(function(d){ output.push(d); }); }); return output; }; // ACTIONHERO // var actionhero = function(){ var self = this; self.initializers = {}; self.api = { running: false, initialized: false, shuttingDown: false }; }; actionhero.prototype.initialize = function(params, callback){ var self = this; if(this._self){ self = this._self; } self.api._self = self; self.api.commands = { initialize: self.initialize, start: self.start, stop: self.stop, restart: self.restart }; self.api.projectRoot = process.cwd(); if(process.env.project_root){ self.api.projectRoot = process.env.project_root; }else if(process.env.projectRoot){ self.api.projectRoot = process.env.projectRoot; }else if(process.env.PROJECT_ROOT){ self.api.projectRoot = process.env.PROJECT_ROOT; } if(!callback && typeof params === 'function'){ callback = params; params = {}; } if(params === null){ params = {}; } self.startingParams = params; self.api._startingParams = self.startingParams; self.api.initializerDefaults = { load: 1000, start: 1000, stop: 1000 }; var loadInitializerRankings = {}; var startInitializerRankings = {}; var stopInitializerRankings = {}; self.configInitializers = []; self.loadInitializers = []; self.startInitializers = []; self.stopInitializers = []; // we need to load the config first [ path.resolve(__dirname + '/initializers/' + 'utils.js'), path.resolve(__dirname + '/initializers/' + 'config.js'), ].forEach(function(file){ var filename = file.replace(/^.*[\\\/]/, ''); var initializer = filename.split('.')[0]; delete require.cache[require.resolve(file)]; self.initializers[initializer] = require(file); self.configInitializers.push(function(next){ self.initializers[initializer].initialize(self.api, next); }); }); self.configInitializers.push(function(){ var customInitializers = []; self.api.config.general.paths.initializer.forEach(function(startPath){ customInitializers = customInitializers.concat(self.api.utils.recursiveDirectoryGlob(startPath)); }); // load all other initializers self.api.utils.arrayUniqueify( self.api.utils.recursiveDirectoryGlob(__dirname + path.sep + 'initializers') .sort() .concat( customInitializers .sort() ) ).forEach(function(f){ var file = path.normalize(f); var initializer = path.basename(f).split('.')[0]; var fileParts = file.split('.'); var ext = fileParts[(fileParts.length - 1)]; if(ext === 'js'){ delete require.cache[require.resolve(file)]; self.initializers[initializer] = require(file); var loadFunction = function(next){ self.api.watchFileAndAct(file, function(){ self.api.log(['*** Rebooting due to initializer change (%s) ***', file], 'info'); self.api.commands.restart.call(self.api._self); }); if(typeof self.initializers[initializer].initialize === 'function'){ if(typeof self.api.log === 'function'){ self.api.log(['Loading initializer: %s', initializer], 'debug', file); } self.initializers[initializer].initialize(self.api, function(error){ try{ self.api.log(['Loaded initializer: %s', initializer], 'debug', file); }catch(e){ } next(error); }); }else{ next(); } }; var startFunction = function(next){ if(typeof self.initializers[initializer].start === 'function'){ if(typeof self.api.log === 'function'){ self.api.log(['Starting initializer: %s', initializer], 'debug', file); } self.initializers[initializer].start(self.api, function(error){ self.api.log(['Started initializer: %s', initializer], 'debug', file); next(error); }); }else{ next(); } }; var stopFunction = function(next){ if(typeof self.initializers[initializer].stop === 'function'){ if(typeof self.api.log === 'function'){ self.api.log(['Stopping initializer: %s', initializer], 'debug', file); } self.initializers[initializer].stop(self.api, function(error){ self.api.log(['Stopped initializer: %s', initializer], 'debug', file); next(error); }); }else{ next(); } }; if(self.initializers[initializer].loadPriority === undefined){ self.initializers[initializer].loadPriority = self.api.initializerDefaults.load; } if(self.initializers[initializer].startPriority === undefined){ self.initializers[initializer].startPriority = self.api.initializerDefaults.start; } if(self.initializers[initializer].stopPriority === undefined){ self.initializers[initializer].stopPriority = self.api.initializerDefaults.stop; } if(loadInitializerRankings[self.initializers[initializer].loadPriority] === undefined){ loadInitializerRankings[self.initializers[initializer].loadPriority] = []; } if(startInitializerRankings[self.initializers[initializer].startPriority] === undefined){ startInitializerRankings[self.initializers[initializer].startPriority] = []; } if(stopInitializerRankings[self.initializers[initializer].stopPriority] === undefined){ stopInitializerRankings[self.initializers[initializer].stopPriority] = []; } if(self.initializers[initializer].loadPriority > 0){ loadInitializerRankings[self.initializers[initializer].loadPriority].push(loadFunction); } if(self.initializers[initializer].startPriority > 0){ startInitializerRankings[self.initializers[initializer].startPriority].push(startFunction); } if(self.initializers[initializer].stopPriority > 0){ stopInitializerRankings[self.initializers[initializer].stopPriority].push(stopFunction); } } }); // flatten all the ordered initializer methods self.loadInitializers = flattenOrderedInitialzer(loadInitializerRankings); self.startInitializers = flattenOrderedInitialzer(startInitializerRankings); self.stopInitializers = flattenOrderedInitialzer(stopInitializerRankings); self.loadInitializers.push(function(){ process.nextTick(function(){ self.api.initialized = true; callback(null, self.api); }); }); async.series(self.loadInitializers, function(errors){ fatalError(self.api, errors, 'initialize'); }); }); async.series(self.configInitializers, function(errors){ fatalError(self.api, errors, 'config'); }); }; actionhero.prototype.start = function(params, callback){ var self = this; if(this._self){ self = this._self; } if(!callback && typeof params === 'function'){ callback = params; params = {}; } var _start = function(){ self.api.running = true; if(self.startInitializers[(self.startInitializers.length - 1)].name === 'finalStartInitializer'){ self.startInitializers.pop(); } self.startInitializers.push(function finalStartInitializer(){ self.api.bootTime = new Date().getTime(); self.api.log(['*** Server Started ***'], 'notice'); callback(null, self.api); }); async.series(self.startInitializers, function(errors){ fatalError(self.api, errors, 'start'); }); }; if(self.api.initialized === true){ _start(); }else{ self.initialize(params, function(){ _start(); }); } }; actionhero.prototype.stop = function(callback){ var self = this; if(this._self){ self = this._self; } if(self.api.running === true){ self.api.shuttingDown = true; self.api.running = false; self.api.initialized = false; self.api.log('Shutting down open servers and stopping task processing...', 'alert'); if(self.stopInitializers[(self.stopInitializers.length - 1)].name === 'finalStopInitializer'){ self.stopInitializers.pop(); } self.stopInitializers.push(function finalStopInitializer(){ self.api.unWatchAllFiles(); self.api.pids.clearPidFile(); self.api.log('The actionhero has been stopped', 'alert'); self.api.log('***', 'debug'); delete self.api.shuttingDown; process.nextTick(function(){ if(typeof callback === 'function'){ callback(null, self.api); } }); }); async.series(self.stopInitializers, function(errors){ fatalError(self.api, errors, 'stop'); }); }else if(self.api.shuttingDown === true){ // double sigterm; ignore it }else{ self.api.log('Cannot shut down actionhero, not running', 'error'); if(typeof callback === 'function'){ callback(null, self.api); } } }; actionhero.prototype.restart = function(callback){ var self = this; if(this._self){ self = this._self; } if(self.api.running === true){ self.stop(function(error){ if(error){ self.api.log(error, 'error'); } self.start(self.startingParams, function(error){ if(error){ self.api.log(error, 'error'); } self.api.log('*** actionhero restarted ***', 'info'); if(typeof callback === 'function'){ callback(null, self.api); } }); }); }else{ self.start(self.startingParams, function(error){ if(error){ self.api.log(error, 'error'); } self.api.log('*** actionhero restarted ***', 'info'); if(typeof callback === 'function'){ callback(null, self.api); } }); } }; exports.actionheroPrototype = actionhero;