wait for templates to compile even if using clustering
fix emitter.all
upgrade.check error first
removed plugins.ready, using callback on plugins.init
removed webserver.init, using webserver.listen
This commit is contained in:
barisusakli
2015-04-27 20:26:02 -04:00
parent 51212c2bfa
commit d946a2fcbd
9 changed files with 325 additions and 316 deletions

94
app.js
View File

@@ -133,57 +133,16 @@ function start() {
winston.verbose('* using themes stored in: %s', nconf.get('themes_path')); winston.verbose('* using themes stored in: %s', nconf.get('themes_path'));
} }
var webserver = require('./src/webserver');
require('./src/database').init(function(err) {
if (err) {
winston.error(err.stack);
process.exit();
}
var meta = require('./src/meta');
meta.configs.init(function () {
var templates = require('templates.js'),
sockets = require('./src/socket.io'),
plugins = require('./src/plugins'),
upgrade = require('./src/upgrade');
templates.setGlobal('relative_path', nconf.get('relative_path'));
upgrade.check(function(schema_ok) {
if (schema_ok || nconf.get('check-schema') === false) {
webserver.init();
sockets.init(webserver.server);
if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) {
require('./src/notifications').init();
require('./src/user').startJobs();
}
webserver.listen();
async.waterfall([
async.apply(meta.themes.setupPaths),
async.apply(plugins.ready),
async.apply(meta.templates.compile)
], function(err) {
if (err) {
winston.error(err.stack);
process.exit();
}
if (process.send) {
process.send({
action: 'ready'
});
}
});
process.on('SIGTERM', shutdown); process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown); process.on('SIGINT', shutdown);
process.on('SIGHUP', restart); process.on('SIGHUP', restart);
process.on('message', function(message) { process.on('message', function(message) {
switch(message.action) { if (typeof message !== 'object') {
return;
}
var meta = require('./src/meta');
var emitter = require('./src/emitter');
switch (message.action) {
case 'reload': case 'reload':
meta.reload(); meta.reload();
break; break;
@@ -191,14 +150,19 @@ function start() {
meta.js.cache = message.cache; meta.js.cache = message.cache;
meta.js.map = message.map; meta.js.map = message.map;
meta.js.hash = message.hash; meta.js.hash = message.hash;
emitter.emit('meta:js.compiled');
winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid); winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid);
break; break;
case 'css-propagate': case 'css-propagate':
meta.css.cache = message.cache; meta.css.cache = message.cache;
meta.css.acpCache = message.acpCache; meta.css.acpCache = message.acpCache;
meta.css.hash = message.hash; meta.css.hash = message.hash;
emitter.emit('meta:css.compiled');
winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid); winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid);
break; break;
case 'templates:compiled':
emitter.emit('templates:compiled');
break;
} }
}); });
@@ -206,16 +170,42 @@ function start() {
winston.error(err.stack); winston.error(err.stack);
console.log(err.stack); console.log(err.stack);
meta.js.killMinifier(); require('./src/meta').js.killMinifier();
shutdown(1); shutdown(1);
}); });
} else {
async.waterfall([
function(next) {
require('./src/database').init(next);
},
function(next) {
require('./src/meta').configs.init(next);
},
function(next) {
require('./src/upgrade').check(next);
},
function(schema_ok, next) {
if (!schema_ok && nconf.get('check-schema') !== false) {
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
winston.warn(' ./nodebb upgrade'); winston.warn(' ./nodebb upgrade');
process.exit(); process.exit();
return;
}
var webserver = require('./src/webserver');
require('./src/socket.io').init(webserver.server);
if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) {
require('./src/notifications').init();
require('./src/user').startJobs();
}
webserver.listen();
}
], function(err) {
if (err) {
winston.error(err.stack);
process.exit();
} }
});
});
}); });
} }
@@ -243,7 +233,7 @@ function setup() {
winston.error('There was a problem completing NodeBB setup: ', err.message); winston.error('There was a problem completing NodeBB setup: ', err.message);
} else { } else {
if (data.hasOwnProperty('password')) { if (data.hasOwnProperty('password')) {
process.stdout.write('An administrative user was automatically created for you:\n') process.stdout.write('An administrative user was automatically created for you:\n');
process.stdout.write(' Username: ' + data.username + '\n'); process.stdout.write(' Username: ' + data.username + '\n');
process.stdout.write(' Password: ' + data.password + '\n'); process.stdout.write(' Password: ' + data.password + '\n');
process.stdout.write('\n'); process.stdout.write('\n');

View File

@@ -82,7 +82,7 @@ Loader.addWorkerEvents = function(worker) {
if (message && typeof message === 'object' && message.action) { if (message && typeof message === 'object' && message.action) {
switch (message.action) { switch (message.action) {
case 'ready': case 'ready':
if (Loader.js.cache) { if (Loader.js.cache && !worker.isPrimary) {
worker.send({ worker.send({
action: 'js-propagate', action: 'js-propagate',
cache: Loader.js.cache, cache: Loader.js.cache,
@@ -91,7 +91,7 @@ Loader.addWorkerEvents = function(worker) {
}); });
} }
if (Loader.css.cache) { if (Loader.css.cache && !worker.isPrimary) {
worker.send({ worker.send({
action: 'css-propagate', action: 'css-propagate',
cache: Loader.css.cache, cache: Loader.css.cache,
@@ -99,6 +99,8 @@ Loader.addWorkerEvents = function(worker) {
hash: Loader.css.hash hash: Loader.css.hash
}); });
} }
break; break;
case 'restart': case 'restart':
console.log('[cluster] Restarting...'); console.log('[cluster] Restarting...');
@@ -132,6 +134,11 @@ Loader.addWorkerEvents = function(worker) {
hash: message.hash hash: message.hash
}, worker.pid); }, worker.pid);
break; break;
case 'templates:compiled':
Loader.notifyWorkers({
action: 'templates:compiled',
}, worker.pid);
break;
} }
} }
}); });

View File

@@ -6,39 +6,30 @@ var eventEmitter = new (require('events')).EventEmitter();
eventEmitter.all = function(events, callback) { eventEmitter.all = function(events, callback) {
var eventList = events.slice(0); var eventList = events.slice(0);
function onEvent(event) { events.forEach(function onEvent(event) {
eventEmitter.on(events[event], function() { eventEmitter.on(event, function() {
eventList.splice(eventList.indexOf(events[event]), 1); var index = eventList.indexOf(event);
if (index === -1) {
return;
}
eventList.splice(index, 1);
if (eventList.length === 0) { if (eventList.length === 0) {
callback(); callback();
} }
}); });
} });
for (var ev in events) {
if (events.hasOwnProperty(ev)) {
onEvent(ev);
}
}
}; };
eventEmitter.any = function(events, callback) { eventEmitter.any = function(events, callback) {
function onEvent(event) { events.forEach(function onEvent(event) {
eventEmitter.on(events[event], function() { eventEmitter.on(event, function() {
if (events !== null) { if (events !== null) {
callback(); callback();
} }
events = null; events = null;
}); });
} });
for (var ev in events) {
if (events.hasOwnProperty(ev)) {
onEvent(ev);
}
}
}; };
module.exports = eventEmitter; module.exports = eventEmitter;

View File

@@ -499,7 +499,10 @@ install.setup = function (callback) {
setCopyrightWidget, setCopyrightWidget,
function (next) { function (next) {
var upgrade = require('./upgrade'); var upgrade = require('./upgrade');
upgrade.check(function(uptodate) { upgrade.check(function(err, uptodate) {
if (err) {
return next(err);
}
if (!uptodate) { upgrade.upgrade(next); } if (!uptodate) { upgrade.upgrade(next); }
else { next(); } else { next(); }
}); });

View File

@@ -15,6 +15,7 @@ var mkdirp = require('mkdirp'),
Templates = {}; Templates = {};
Templates.compile = function(callback) { Templates.compile = function(callback) {
callback = callback || function() {};
var fromFile = nconf.get('from-file') || ''; var fromFile = nconf.get('from-file') || '';
if (nconf.get('isPrimary') === 'false' || fromFile.match('tpl')) { if (nconf.get('isPrimary') === 'false' || fromFile.match('tpl')) {
@@ -22,11 +23,7 @@ Templates.compile = function(callback) {
winston.info('[minifier] Compiling templates skipped'); winston.info('[minifier] Compiling templates skipped');
} }
emitter.emit('templates:compiled'); return callback();
if (callback) {
callback();
}
return;
} }
var coreTemplatesPath = nconf.get('core_templates_path'), var coreTemplatesPath = nconf.get('core_templates_path'),
@@ -119,15 +116,20 @@ Templates.compile = function(callback) {
}, function(err) { }, function(err) {
if (err) { if (err) {
winston.error('[meta/templates] ' + err.stack); winston.error('[meta/templates] ' + err.stack);
} else { return callback(err);
}
compileIndex(viewsPath, function() { compileIndex(viewsPath, function() {
winston.verbose('[meta/templates] Successfully compiled templates.'); winston.verbose('[meta/templates] Successfully compiled templates.');
emitter.emit('templates:compiled'); emitter.emit('templates:compiled');
if (callback) { if (process.send) {
callback(); process.send({
} action: 'templates:compiled'
}); });
} }
callback();
});
}); });
}); });
}); });

View File

@@ -39,9 +39,10 @@ var fs = require('fs'),
Plugins.libraryPaths.push(libraryPath); Plugins.libraryPaths.push(libraryPath);
}; };
Plugins.init = function(nbbApp, nbbMiddleware) { Plugins.init = function(nbbApp, nbbMiddleware, callback) {
callback = callback || function() {};
if (Plugins.initialized) { if (Plugins.initialized) {
return; return callback();
} }
app = nbbApp; app = nbbApp;
@@ -55,7 +56,7 @@ var fs = require('fs'),
Plugins.reload(function(err) { Plugins.reload(function(err) {
if (err) { if (err) {
winston.error('[plugins] NodeBB encountered a problem while loading plugins', err.message); winston.error('[plugins] NodeBB encountered a problem while loading plugins', err.message);
return; return callback(err);
} }
if (global.env === 'development') { if (global.env === 'development') {
@@ -64,6 +65,7 @@ var fs = require('fs'),
Plugins.initialized = true; Plugins.initialized = true;
emitter.emit('plugins:loaded'); emitter.emit('plugins:loaded');
callback();
}); });
Plugins.registerHook('core', { Plugins.registerHook('core', {
@@ -72,14 +74,6 @@ var fs = require('fs'),
}); });
}; };
Plugins.ready = function(callback) {
if (!Plugins.initialized) {
emitter.once('plugins:loaded', callback);
} else {
callback();
}
};
Plugins.reload = function(callback) { Plugins.reload = function(callback) {
// Resetting all local plugin data // Resetting all local plugin data
Plugins.libraries = {}; Plugins.libraries = {};

View File

@@ -40,7 +40,6 @@
var router = express.Router(); var router = express.Router();
router.hotswapId = 'auth'; router.hotswapId = 'auth';
plugins.ready(function() {
loginStrategies.length = 0; loginStrategies.length = 0;
if (plugins.hasListeners('action:auth.overrideLogin')) { if (plugins.hasListeners('action:auth.overrideLogin')) {
@@ -78,7 +77,6 @@
callback(); callback();
} }
}); });
});
}; };
Auth.login = function(req, username, password, next) { Auth.login = function(req, username, password, next) {

View File

@@ -25,18 +25,21 @@ var db = require('./database'),
Upgrade.check = function(callback) { Upgrade.check = function(callback) {
db.get('schemaDate', function(err, value) { db.get('schemaDate', function(err, value) {
if(!value) { if (err) {
return callback(err);
}
if (!value) {
db.set('schemaDate', latestSchema, function(err) { db.set('schemaDate', latestSchema, function(err) {
callback(true); if (err) {
return callback(err);
}
callback(null, true);
}); });
return; return;
} }
if (parseInt(value, 10) >= latestSchema) { callback(null, parseInt(value, 10) >= latestSchema);
callback(true);
} else {
callback(false);
}
}); });
}; };

View File

@@ -5,7 +5,7 @@ var path = require('path'),
fs = require('fs'), fs = require('fs'),
nconf = require('nconf'), nconf = require('nconf'),
express = require('express'), express = require('express'),
WebServer = express(), app = express(),
server, server,
winston = require('winston'), winston = require('winston'),
async = require('async'), async = require('async'),
@@ -18,19 +18,129 @@ var path = require('path'),
routes = require('./routes'), routes = require('./routes'),
emitter = require('./emitter'), emitter = require('./emitter'),
helpers = require('./../public/src/modules/helpers'), helpers = require('../public/src/modules/helpers');
net;
if(nconf.get('ssl')) { if (nconf.get('ssl')) {
server = require('https').createServer({ server = require('https').createServer({
key: fs.readFileSync(nconf.get('ssl').key), key: fs.readFileSync(nconf.get('ssl').key),
cert: fs.readFileSync(nconf.get('ssl').cert) cert: fs.readFileSync(nconf.get('ssl').cert)
}, WebServer); }, app);
} else { } else {
server = require('http').createServer(WebServer); server = require('http').createServer(app);
} }
(function (app) { module.exports.server = server;
server.on('error', function(err) {
winston.error(err);
if (err.code === 'EADDRINUSE') {
winston.error('NodeBB address in use, exiting...');
process.exit(0);
} else {
throw err;
}
});
if (server.setTimeout) {
server.setTimeout(10000);
}
module.exports.listen = function() {
emailer.registerApp(app);
middleware = middleware(app);
helpers.register();
logger.init(app);
emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() {
winston.info('NodeBB Ready');
emitter.emit('nodebb:ready');
listen();
});
initializeNodeBB(function(err) {
if (err) {
winston.error(err);
process.exit();
}
if (process.send) {
process.send({
action: 'ready'
});
}
});
};
function initializeNodeBB(callback) {
var skipJS, skipLess, fromFile = nconf.get('from-file') || '';
if (fromFile.match('js')) {
winston.info('[minifier] Minifying client-side JS skipped');
skipJS = true;
}
if (fromFile.match('less')) {
winston.info('[minifier] Compiling LESS files skipped');
skipLess = true;
}
async.waterfall([
async.apply(cacheStaticFiles),
async.apply(meta.themes.setupPaths),
function(next) {
plugins.init(app, middleware, next);
},
function(next) {
async.parallel([
async.apply(meta.templates.compile),
async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')),
async.apply(!skipLess ? meta.css.minify : meta.css.getFromFile),
async.apply(meta.sounds.init)
], next);
},
function(results, next) {
plugins.fireHook('static:app.preload', {
app: app,
middleware: middleware
}, next);
},
function(results, next) {
routes(app, middleware);
next();
}
], callback);
}
function cacheStaticFiles(callback) {
if (global.env === 'development') {
return callback();
}
app.enable('cache');
app.enable('minification');
// Configure cache-buster timestamp
require('child_process').exec('git describe --tags', {
cwd: path.join(__dirname, '../')
}, function(err, stdOut) {
if (!err) {
meta.config['cache-buster'] = stdOut.trim();
callback();
} else {
fs.stat(path.join(__dirname, '../package.json'), function(err, stats) {
if (err) {
return callback(err);
}
meta.config['cache-buster'] = new Date(stats.mtime).getTime();
callback();
});
}
});
}
function listen(callback) {
var port = nconf.get('port'); var port = nconf.get('port');
if (Array.isArray(port)) { if (Array.isArray(port)) {
@@ -48,66 +158,6 @@ if(nconf.get('ssl')) {
} }
} }
module.exports.init = function() {
var skipJS, skipLess, fromFile = nconf.get('from-file') || '';
emailer.registerApp(app);
if (fromFile.match('js')) {
winston.info('[minifier] Minifying client-side JS skipped');
skipJS = true;
}
if (fromFile.match('less')) {
winston.info('[minifier] Compiling LESS files skipped');
skipLess = true;
}
// Preparation dependent on plugins
plugins.ready(function() {
async.parallel([
async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')),
async.apply(!skipLess ? meta.css.minify : meta.css.getFromFile),
async.apply(meta.sounds.init)
]);
plugins.fireHook('static:app.preload', {
app: app,
middleware: middleware
}, function(err) {
if (err) {
return winston.error('[plugins] Encountered error while executing pre-router plugins hooks: ' + err.message);
}
routes(app, middleware);
});
});
middleware = middleware(app);
plugins.init(app, middleware);
// Load server-side template helpers
helpers.register();
// Cache static files on production
if (global.env !== 'development') {
app.enable('cache');
app.enable('minification');
// Configure cache-buster timestamp
require('child_process').exec('git describe --tags', {
cwd: path.join(__dirname, '../')
}, function(err, stdOut) {
if (!err) {
meta.config['cache-buster'] = stdOut.trim();
} else {
fs.stat(path.join(__dirname, '../package.json'), function(err, stats) {
meta.config['cache-buster'] = new Date(stats.mtime).getTime();
});
}
});
}
if (port !== 80 && port !== 443 && nconf.get('use_port') === false) { if (port !== 80 && port !== 443 && nconf.get('use_port') === false) {
winston.info('Enabling \'trust proxy\''); winston.info('Enabling \'trust proxy\'');
app.enable('trust proxy'); app.enable('trust proxy');
@@ -116,30 +166,6 @@ if(nconf.get('ssl')) {
if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') { if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') {
winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md'); winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md');
} }
};
server.on('error', function(err) {
winston.error(err.stack);
console.log(err.stack);
if (err.code === 'EADDRINUSE') {
winston.error('NodeBB address in use, exiting...');
process.exit(0);
} else {
throw err;
}
});
module.exports.server = server;
emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() {
winston.info('NodeBB Ready');
emitter.emit('nodebb:ready');
});
server.setTimout && server.setTimeout(10000);
module.exports.listen = function() {
logger.init(app);
var isSocket = isNaN(port), var isSocket = isNaN(port),
args = isSocket ? [port] : [port, nconf.get('bind_address')], args = isSocket ? [port] : [port, nconf.get('bind_address')],
@@ -161,12 +187,9 @@ if(nconf.get('ssl')) {
// Alter umask if necessary // Alter umask if necessary
if (isSocket) { if (isSocket) {
oldUmask = process.umask('0000'); oldUmask = process.umask('0000');
net = require('net');
module.exports.testSocket(port, function(err) { module.exports.testSocket(port, function(err) {
if (!err) { if (!err) {
emitter.on('nodebb:ready', function() {
server.listen.apply(server, args); server.listen.apply(server, args);
});
} else { } else {
winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')'); winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')');
winston.error('[startup] ' + err.message); winston.error('[startup] ' + err.message);
@@ -174,17 +197,15 @@ if(nconf.get('ssl')) {
} }
}); });
} else { } else {
emitter.on('nodebb:ready', function() {
server.listen.apply(server, args); server.listen.apply(server, args);
});
} }
}; }
module.exports.testSocket = function(socketPath, callback) { module.exports.testSocket = function(socketPath, callback) {
if (typeof socketPath !== 'string') { if (typeof socketPath !== 'string') {
return callback(new Error('invalid socket path : ' + socketPath)); return callback(new Error('invalid socket path : ' + socketPath));
} }
var net = require('net');
async.series([ async.series([
function(next) { function(next) {
fs.exists(socketPath, function(exists) { fs.exists(socketPath, function(exists) {
@@ -207,6 +228,6 @@ if(nconf.get('ssl')) {
}, },
async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way
], callback); ], callback);
}; };
}(WebServer));