mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
proof-of-concept for hot-swapping of routes
This commit is contained in:
33
src/hotswap.js
Normal file
33
src/hotswap.js
Normal file
@@ -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,57 +152,61 @@ 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
|
adminRoutes(router, middleware, controllers);
|
||||||
plugins.fireHook('action:app.load', router, middleware, controllers);
|
metaRoutes(router, middleware, controllers);
|
||||||
|
apiRoutes(router, middleware, controllers);
|
||||||
|
feedRoutes(router, middleware, controllers);
|
||||||
|
pluginRoutes(router, middleware, controllers);
|
||||||
|
authRoutes.createRoutes(router, middleware, controllers);
|
||||||
|
|
||||||
adminRoutes(router, middleware, controllers);
|
/**
|
||||||
metaRoutes(router, middleware, controllers);
|
* Every view has an associated API route.
|
||||||
apiRoutes(router, middleware, controllers);
|
*
|
||||||
feedRoutes(router, middleware, controllers);
|
*/
|
||||||
pluginRoutes(router, middleware, controllers);
|
mainRoutes(router, middleware, controllers);
|
||||||
authRoutes.createRoutes(router, middleware, controllers);
|
staticRoutes(router, middleware, controllers);
|
||||||
|
topicRoutes(router, middleware, controllers);
|
||||||
/**
|
tagRoutes(router, middleware, controllers);
|
||||||
* Every view has an associated API route.
|
categoryRoutes(router, middleware, controllers);
|
||||||
*
|
accountRoutes(router, middleware, controllers);
|
||||||
*/
|
userRoutes(router, middleware, controllers);
|
||||||
mainRoutes(router, middleware, controllers);
|
groupRoutes(router, middleware, controllers);
|
||||||
staticRoutes(router, middleware, controllers);
|
|
||||||
topicRoutes(router, middleware, controllers);
|
|
||||||
tagRoutes(router, middleware, controllers);
|
|
||||||
categoryRoutes(router, middleware, controllers);
|
|
||||||
accountRoutes(router, middleware, controllers);
|
|
||||||
userRoutes(router, middleware, controllers);
|
|
||||||
groupRoutes(router, middleware, controllers);
|
|
||||||
|
|
||||||
plugins.fireHook('static:app.load', router, middleware, controllers, function() {
|
|
||||||
app.use(relativePath, router);
|
|
||||||
|
|
||||||
app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), {
|
|
||||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
|
||||||
}));
|
|
||||||
app.use(catch404);
|
|
||||||
app.use(handleErrors);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Add the routers to the application
|
||||||
|
app.use(relativePath, router);
|
||||||
|
app.use(relativePath, pluginRouter);
|
||||||
|
app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), {
|
||||||
|
maxAge: app.enabled('cache') ? 5184000000 : 0
|
||||||
|
}));
|
||||||
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.');
|
||||||
|
|||||||
Reference in New Issue
Block a user