proof-of-concept for hot-swapping of routes

v1.18.x
Julian Lam 11 years ago
parent 69b9e57daf
commit 943874805d

@ -0,0 +1,33 @@
var HotSwap = {},
winston = require('winston'),
stack;
HotSwap.prepare = function(app) {
stack = app._router.stack;
};
HotSwap.find = function(id) {
if (stack) {
for(var x=0,numEntries=stack.length;x<numEntries;x++) {
if (stack[x].handle.hotswapId === id) {
return x;
break;
}
}
} else {
winston.error('[hotswap] HotSwap module has not been prepared!');
}
};
HotSwap.replace = function(id, middleware) {
var idx = HotSwap.find(id);
if (idx) {
delete stack[idx].handle; // Destroy the old middleware
stack[idx].handle = middleware; // Replace with the new one
winston.info('[hotswap] Middleware with id `' + id + '` replaced successfully');
} else {
winston.warn('[hotswap] Could not find middleware in stack with hotswapId `' + id + '`');
}
};
module.exports = HotSwap;

@ -6,13 +6,18 @@ var fs = require('fs'),
winston = require('winston'), winston = require('winston'),
nconf = require('nconf'), nconf = require('nconf'),
semver = require('semver'), semver = require('semver'),
express = require('express'),
db = require('./database'), db = require('./database'),
emitter = require('./emitter'), emitter = require('./emitter'),
meta = require('./meta'), meta = require('./meta'),
translator = require('../public/src/translator'), translator = require('../public/src/translator'),
utils = require('../public/src/utils'), utils = require('../public/src/utils'),
pkg = require('../package.json'); hotswap = require('./hotswap'),
pkg = require('../package.json'),
controllers = require('./controllers'),
app, middleware;
(function(Plugins) { (function(Plugins) {
@ -57,6 +62,12 @@ var fs = require('fs'),
}); });
}; };
Plugins.prepareApp = function(nbbApp, nbbMiddleware) {
app = nbbApp;
middleware = nbbMiddleware;
hotswap.prepare(nbbApp);
};
Plugins.ready = function(callback) { Plugins.ready = function(callback) {
if (!Plugins.initialized) { if (!Plugins.initialized) {
emitter.once('plugins:loaded', callback); emitter.once('plugins:loaded', callback);
@ -107,10 +118,29 @@ var fs = require('fs'),
}); });
next(); next();
} },
async.apply(Plugins.reloadRoutes)
], callback); ], callback);
}; };
Plugins.reloadRoutes = function(callback) {
if (!app || !middleware || !controllers) {
return;
} else {
var router = express.Router();
router.hotswapId = 'plugins';
// Deprecated as of v0.5.0, remove this hook call for NodeBB v0.6.0-1
Plugins.fireHook('action:app.load', router, middleware, controllers);
Plugins.fireHook('static:app.load', router, middleware, controllers, function() {
hotswap.replace('plugins', router);
});
callback();
}
};
Plugins.loadPlugin = function(pluginPath, callback) { Plugins.loadPlugin = function(pluginPath, callback) {
fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) { fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) {
if (err) { if (err) {

@ -56,6 +56,10 @@ module.exports = function(app, middleware, controllers) {
}); });
router.get('/test', function(req, res) { router.get('/test', function(req, res) {
res.redirect('404'); res.redirect(404);
var plugins = require('../plugins');
plugins.reloadRoutes(function() {
res.send(200, 'routes replaced');
});
}); });
}; };

@ -152,22 +152,20 @@ function groupRoutes(app, middleware, controllers) {
module.exports = function(app, middleware) { module.exports = function(app, middleware) {
plugins.ready(function() {
var router = express.Router(), var router = express.Router(),
pluginRouter = express.Router(),
relativePath = nconf.get('relative_path'); relativePath = nconf.get('relative_path');
router.render = function() { pluginRouter.render = function() {
app.render.apply(app, arguments); app.render.apply(app, arguments);
}; };
pluginRouter.hotswapId = 'plugins';
app.all(relativePath + '/api/?*', middleware.updateLastOnlineTime, middleware.prepareAPI); app.all(relativePath + '/api/?*', middleware.updateLastOnlineTime, middleware.prepareAPI);
app.all(relativePath + '/api/admin/*', middleware.admin.isAdmin, middleware.prepareAPI); app.all(relativePath + '/api/admin/*', middleware.admin.isAdmin, middleware.prepareAPI);
app.all(relativePath + '/admin/*', middleware.admin.isAdmin); app.all(relativePath + '/admin/*', middleware.admin.isAdmin);
app.get(relativePath + '/admin', middleware.admin.isAdmin); app.get(relativePath + '/admin', middleware.admin.isAdmin);
// Deprecated as of v0.5.0, remove this hook call for NodeBB v0.6.0-1
plugins.fireHook('action:app.load', router, middleware, controllers);
adminRoutes(router, middleware, controllers); adminRoutes(router, middleware, controllers);
metaRoutes(router, middleware, controllers); metaRoutes(router, middleware, controllers);
apiRoutes(router, middleware, controllers); apiRoutes(router, middleware, controllers);
@ -188,21 +186,27 @@ module.exports = function(app, middleware) {
userRoutes(router, middleware, controllers); userRoutes(router, middleware, controllers);
groupRoutes(router, middleware, controllers); groupRoutes(router, middleware, controllers);
plugins.fireHook('static:app.load', router, middleware, controllers, function() { // Add the routers to the application
app.use(relativePath, router); app.use(relativePath, router);
app.use(relativePath, pluginRouter);
app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), { app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), {
maxAge: app.enabled('cache') ? 5184000000 : 0 maxAge: app.enabled('cache') ? 5184000000 : 0
})); }));
app.use(catch404);
app.use(handleErrors);
});
});
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
require('./debug')(app, middleware, controllers); require('./debug')(app, middleware, controllers);
} }
app.use(catch404);
app.use(handleErrors);
// Add plugin routes
plugins.reloadRoutes();
// plugins.ready(function() {
// plugins.fireHook('static:app.load', pluginRouter, middleware, controllers, function() {
// });
// });
}; };
function handleErrors(err, req, res, next) { function handleErrors(err, req, res, next) {

@ -56,6 +56,7 @@ if(nconf.get('ssl')) {
}, function(err, data) { }, function(err, data) {
middleware = middleware(app, data); middleware = middleware(app, data);
routes(app, middleware); routes(app, middleware);
plugins.prepareApp(app, middleware);
if (err) { if (err) {
winston.error('Errors were encountered while attempting to initialise NodeBB.'); winston.error('Errors were encountered while attempting to initialise NodeBB.');

Loading…
Cancel
Save