moved all app.configure() code into middleware/index.js + organization/cleanup

v1.18.x
psychobunny 11 years ago
parent 804da0d8de
commit 08a9ce4950

@ -0,0 +1,251 @@
var templates = require('./../../public/src/templates'),
translator = require('./../../public/src/translator'),
meta = require('./../meta'),
db = require('./../database'),
auth = require('./../routes/authentication'),
async = require('async'),
path = require('path'),
fs = require('fs'),
nconf = require('nconf'),
express = require('express')
winston = require('winston');
/*
* todo: move out into their own file(s)
*/
var middleware = {};
middleware.processRender = function(req, res, next) {
// res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687
var render = res.render;
res.render = function(template, options, fn) {
var self = this,
options = options || {},
req = this.req,
app = req.app,
defaultFn = function(err, str){
if (err) {
return req.next(err);
}
self.send(str);
};
if ('function' == typeof options) {
fn = options, options = {};
}
if ('function' != typeof fn) {
fn = defaultFn;
}
render.call(self, template, options, function(err, str) {
if (res.locals.header) {
str = res.locals.header + str;
}
if (res.locals.footer) {
str = str + res.locals.footer;
}
if (str) {
translator.translate(str, function(translated) {
fn(err, translated);
});
} else {
fn(err, str);
}
});
};
next();
};
middleware.routeTouchIcon = function(req, res) {
if (meta.config['brand:logo'] && validator.isURL(meta.config['brand:logo'])) {
return res.redirect(meta.config['brand:logo']);
} else {
return res.sendfile(path.join(__dirname, '../../public', meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
});
}
}
/*
* Helper functions
*/
function routeThemeScreenshots(app, themes) {
var screenshotPath;
async.each(themes, function(themeObj, next) {
if (themeObj.screenshot) {
screenshotPath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.screenshot);
(function(id, path) {
fs.exists(path, function(exists) {
if (exists) {
app.get('/css/previews/' + id, function(req, res) {
res.sendfile(path);
});
}
});
})(themeObj.id, screenshotPath);
} else {
next(false);
}
});
}
function routeCurrentTheme(app, themeData) {
var themeId = (themeData['theme:id'] || 'nodebb-theme-vanilla');
// Detect if a theme has been selected, and handle appropriately
if (!themeData['theme:type'] || themeData['theme:type'] === 'local') {
// Local theme
if (process.env.NODE_ENV === 'development') {
winston.info('[themes] Using theme ' + themeId);
}
// Theme's static directory
if (themeData['theme:staticDir']) {
app.use('/css/assets', express.static(path.join(nconf.get('themes_path'), themeData['theme:id'], themeData['theme:staticDir']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
winston.info('Static directory routed for theme: ' + themeData['theme:id']);
}
}
if (themeData['theme:templates']) {
app.use('/templates', express.static(path.join(nconf.get('themes_path'), themeData['theme:id'], themeData['theme:templates']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
winston.info('Custom templates directory routed for theme: ' + themeData['theme:id']);
}
}
} else {
// If not using a local theme (bootswatch, etc), drop back to vanilla
if (process.env.NODE_ENV === 'development') {
winston.info('[themes] Using theme ' + themeId);
}
app.use(require('less-middleware')({
src: path.join(nconf.get('themes_path'), '/nodebb-theme-vanilla'),
dest: path.join(__dirname, '../../public/css'),
prefix: nconf.get('relative_path') + '/css',
yuicompress: app.enabled('minification') ? true : false
}));
}
}
function handleErrors(err, req, res, next) {
// we may use properties of the error object
// here and next(err) appropriately, or if
// we possibly recovered from the error, simply next().
console.error(err.stack);
var status = err.status || 500;
res.status(status);
res.json(status, {
error: err.message
});
}
function catch404(req, res, next) {
var isLanguage = new RegExp('^' + nconf.get('relative_path') + '/language/[\\w]{2,}/.*.json'),
isClientScript = new RegExp('^' + nconf.get('relative_path') + '\\/src\\/forum(\\/admin)?\\/[\\w]+\\.js');
res.status(404);
if (isClientScript.test(req.url)) {
// Handle missing client-side scripts
res.type('text/javascript').send(200, '');
} else if (isLanguage.test(req.url)) {
// Handle languages by sending an empty object
res.json(200, {});
} else if (req.accepts('html')) {
// respond with html page
if (process.env.NODE_ENV === 'development') {
winston.warn('Route requested but not found: ' + req.url);
}
res.redirect(nconf.get('relative_path') + '/404');
} else if (req.accepts('json')) {
// respond with json
if (process.env.NODE_ENV === 'development') {
winston.warn('Route requested but not found: ' + req.url);
}
res.json({
error: 'Not found'
});
} else {
// default to plain-text. send()
res.type('txt').send('Not found');
}
}
module.exports = function(app, data) {
// Middlewares
app.configure(function() {
app.engine('tpl', templates.__express);
app.set('view engine', 'tpl');
app.set('views', path.join(__dirname, '../../public/templates'));
// Pre-router middlewares
app.use(express.compress());
app.use(express.favicon(path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico')));
app.use('/apple-touch-icon', middleware.routeTouchIcon);
app.use(require('less-middleware')({
src: path.join(__dirname, '../../', 'public'),
prefix: nconf.get('relative_path'),
yuicompress: app.enabled('minification') ? true : false
}));
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({
store: db.sessionStore,
secret: nconf.get('secret'),
key: 'express.sid',
cookie: {
maxAge: 1000 * 60 * 60 * 24 * parseInt(meta.configs.loginDays || 14, 10)
}
}));
app.use(express.csrf()); // todo, make this a conditional middleware
// Local vars, other assorted setup
app.use(function (req, res, next) {
res.locals.csrf_token = req.session._csrf;
// Disable framing
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
next();
});
app.use(middleware.processRender);
// Authentication Routes
auth.initialize(app);
routeCurrentTheme(app, data.currentThemeData);
// Route paths to screenshots for installed themes
routeThemeScreenshots(app, data.themesData);
app.use(app.router);
// Static directory /public
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../../', 'public'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
app.use(catch404);
app.use(handleErrors);
});
};

@ -10,6 +10,9 @@ var path = require('path'),
async = require('async'), async = require('async'),
utils = require('../public/src/utils'), utils = require('../public/src/utils'),
templates = require('./../public/src/templates'), // todo remove
translator = require('./../public/src/translator'),
db = require('./database'), db = require('./database'),
user = require('./user'), user = require('./user'),
notifications = require('./notifications'), notifications = require('./notifications'),
@ -17,9 +20,8 @@ var path = require('path'),
meta = require('./meta'), meta = require('./meta'),
plugins = require('./plugins'), plugins = require('./plugins'),
logger = require('./logger'), logger = require('./logger'),
templates = require('./../public/src/templates'),
translator = require('./../public/src/translator'),
controllers = require('./controllers'), controllers = require('./controllers'),
middleware = require('./middleware'),
admin = require('./routes/admin'), admin = require('./routes/admin'),
apiRoute = require('./routes/api'), apiRoute = require('./routes/api'),
@ -35,8 +37,6 @@ if(nconf.get('ssl')) {
server = require('http').createServer(WebServer); server = require('http').createServer(WebServer);
} }
module.exports.server = server;
// Signals // Signals
var shutdown = function(code) { var shutdown = function(code) {
winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.'); winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.');
@ -67,6 +67,7 @@ process.on('uncaughtException', function(err) {
(function (app) { (function (app) {
"use strict"; "use strict";
// this can be moved to app.js
var clientScripts; var clientScripts;
plugins.ready(function() { plugins.ready(function() {
@ -82,6 +83,29 @@ process.on('uncaughtException', function(err) {
}); });
}); });
logger.init(app);
async.series({
themesData: meta.themes.get,
currentThemeData: function(next) {
db.getObjectFields('config', ['theme:type', 'theme:id', 'theme:staticDir', 'theme:templates'], next);
}
}, function(err, data) {
middleware(app, data);
if (err) {
winston.error('Errors were encountered while attempting to initialise NodeBB.');
process.exit();
} else {
if (process.env.NODE_ENV === 'development') {
winston.info('Middlewares loaded.');
}
}
});
app.prepareAPI = function(req, res, next) { app.prepareAPI = function(req, res, next) {
res.locals.isAPI = true; res.locals.isAPI = true;
next(); next();
@ -323,266 +347,16 @@ process.on('uncaughtException', function(err) {
}); });
} }
// Middlewares if (nconf.get('port') != 80 && nconf.get('port') != 443 && nconf.get('use_port') === false) {
app.configure(function() { winston.info('Enabling \'trust proxy\'');
app.engine('tpl', templates.__express); app.enable('trust proxy');
app.set('view engine', 'tpl'); }
app.set('views', path.join(__dirname, '../public/templates'));
async.series([
function(next) {
// Pre-router middlewares
app.use(express.compress());
logger.init(app);
// favicon & apple-touch-icon middleware
app.use(express.favicon(path.join(__dirname, '../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico')));
app.use('/apple-touch-icon', function(req, res) {
if (meta.config['brand:logo'] && validator.isURL(meta.config['brand:logo'])) {
return res.redirect(meta.config['brand:logo']);
} else {
return res.sendfile(path.join(__dirname, '../public', meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
});
}
});
app.use(require('less-middleware')({
src: path.join(__dirname, '../', 'public'),
prefix: nconf.get('relative_path'),
yuicompress: app.enabled('minification') ? true : false
}));
app.use(express.bodyParser()); // Puts POST vars in request.body
app.use(express.cookieParser()); // If you want to parse cookies (res.cookies)
app.use(express.session({
store: db.sessionStore,
secret: nconf.get('secret'),
key: 'express.sid',
cookie: {
maxAge: 1000 * 60 * 60 * 24 * parseInt(meta.configs.loginDays || 14, 10)
}
}));
app.use(express.csrf());
if (nconf.get('port') != 80 && nconf.get('port') != 443 && nconf.get('use_port') === false) {
winston.info('Enabling \'trust proxy\'');
app.enable('trust proxy');
}
if ((nconf.get('port') == 80 || nconf.get('port') == 443) && process.env.NODE_ENV !== 'development') {
winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md');
}
// Local vars, other assorted setup
app.use(function (req, res, next) {
res.locals.csrf_token = req.session._csrf;
// Disable framing
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
next();
});
app.use(function(req, res, next) {
// res.render post-processing middleware, modified from here: https://gist.github.com/mrlannigan/5051687
var render = res.render;
res.render = function(template, options, fn) {
var self = this,
options = options || {},
req = this.req,
app = req.app,
defaultFn = function(err, str){
if (err) {
return req.next(err);
}
self.send(str);
};
if ('function' == typeof options) {
fn = options, options = {};
}
if ('function' != typeof fn) {
fn = defaultFn;
}
render.call(self, template, options, function(err, str) {
if (res.locals.header) {
str = res.locals.header + str;
}
if (res.locals.footer) {
str = str + res.locals.footer;
}
if (str) {
translator.translate(str, function(translated) {
fn(err, translated);
});
} else {
fn(err, str);
}
});
};
next();
});
// Authentication Routes
auth.initialize(app);
next();
},
function(next) {
async.parallel([
function(next) {
db.getObjectFields('config', ['theme:type', 'theme:id', 'theme:staticDir', 'theme:templates'], function(err, themeData) {
var themeId = (themeData['theme:id'] || 'nodebb-theme-vanilla');
// Detect if a theme has been selected, and handle appropriately
if (!themeData['theme:type'] || themeData['theme:type'] === 'local') {
// Local theme
if (process.env.NODE_ENV === 'development') {
winston.info('[themes] Using theme ' + themeId);
}
// Theme's static directory
if (themeData['theme:staticDir']) {
app.use('/css/assets', express.static(path.join(nconf.get('themes_path'), themeData['theme:id'], themeData['theme:staticDir']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
winston.info('Static directory routed for theme: ' + themeData['theme:id']);
}
}
if (themeData['theme:templates']) {
app.use('/templates', express.static(path.join(nconf.get('themes_path'), themeData['theme:id'], themeData['theme:templates']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
winston.info('Custom templates directory routed for theme: ' + themeData['theme:id']);
}
}
next();
} else {
// If not using a local theme (bootswatch, etc), drop back to vanilla
if (process.env.NODE_ENV === 'development') {
winston.info('[themes] Using theme ' + themeId);
}
app.use(require('less-middleware')({
src: path.join(nconf.get('themes_path'), '/nodebb-theme-vanilla'),
dest: path.join(__dirname, '../public/css'),
prefix: nconf.get('relative_path') + '/css',
yuicompress: app.enabled('minification') ? true : false
}));
next();
}
});
// Route paths to screenshots for installed themes
meta.themes.get(function(err, themes) {
var screenshotPath;
async.each(themes, function(themeObj, next) {
if (themeObj.screenshot) {
screenshotPath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.screenshot);
(function(id, path) {
fs.exists(path, function(exists) {
if (exists) {
app.get('/css/previews/' + id, function(req, res) {
res.sendfile(path);
});
}
});
})(themeObj.id, screenshotPath);
} else {
next(false);
}
});
});
}
], next);
},
function(next) {
// Router & post-router middlewares
app.use(app.router);
// Static directory /public
app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../', 'public'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
// 404 catch-all
app.use(function (req, res, next) {
var isLanguage = new RegExp('^' + nconf.get('relative_path') + '/language/[\\w]{2,}/.*.json'),
isClientScript = new RegExp('^' + nconf.get('relative_path') + '\\/src\\/forum(\\/admin)?\\/[\\w]+\\.js');
res.status(404);
if (isClientScript.test(req.url)) {
// Handle missing client-side scripts
res.type('text/javascript').send(200, '');
} else if (isLanguage.test(req.url)) {
// Handle languages by sending an empty object
res.json(200, {});
} else if (req.accepts('html')) {
// respond with html page
if (process.env.NODE_ENV === 'development') {
winston.warn('Route requested but not found: ' + req.url);
}
res.redirect(nconf.get('relative_path') + '/404');
} else if (req.accepts('json')) {
// respond with json
if (process.env.NODE_ENV === 'development') {
winston.warn('Route requested but not found: ' + req.url);
}
res.json({
error: 'Not found'
});
} else {
// default to plain-text. send()
res.type('txt').send('Not found');
}
});
app.use(function (err, req, res, next) {
// we may use properties of the error object
// here and next(err) appropriately, or if
// we possibly recovered from the error, simply next().
console.error(err.stack);
var status = err.status || 500;
res.status(status);
res.json(status, {
error: err.message
});
});
next(); if ((nconf.get('port') == 80 || nconf.get('port') == 443) && process.env.NODE_ENV !== 'development') {
} winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md');
], function(err) { }
if (err) {
winston.error('Errors were encountered while attempting to initialise NodeBB.');
process.exit();
} else {
if (process.env.NODE_ENV === 'development') {
winston.info('Middlewares loaded.');
}
}
});
});
module.exports.server = server;
module.exports.init = function () { module.exports.init = function () {
// translate all static templates served by webserver here. ex. footer, logout // translate all static templates served by webserver here. ex. footer, logout
plugins.fireHook('action:app.load', app); plugins.fireHook('action:app.load', app);

Loading…
Cancel
Save