organize middlewares
removed app.locals.middleware middlewares can be required anywhere, ie in controllersv1.18.x
parent
f5941cbf1c
commit
8f408faf46
@ -1,130 +1,125 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var app,
|
var async = require('async');
|
||||||
middleware = {},
|
var winston = require('winston');
|
||||||
nconf = require('nconf'),
|
var user = require('../user');
|
||||||
async = require('async'),
|
var meta = require('../meta');
|
||||||
winston = require('winston'),
|
var plugins = require('../plugins');
|
||||||
user = require('../user'),
|
|
||||||
meta = require('../meta'),
|
var controllers = {
|
||||||
plugins = require('../plugins'),
|
api: require('../controllers/api'),
|
||||||
|
helpers: require('../controllers/helpers')
|
||||||
controllers = {
|
|
||||||
api: require('../controllers/api'),
|
|
||||||
helpers: require('../controllers/helpers')
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.isAdmin = function(req, res, next) {
|
|
||||||
winston.warn('[middleware.admin.isAdmin] deprecation warning, no need to use this from plugins!');
|
|
||||||
|
|
||||||
if (!req.user) {
|
|
||||||
return controllers.helpers.notAllowed(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.isAdministrator(req.user.uid, function (err, isAdmin) {
|
|
||||||
if (err || isAdmin) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
controllers.helpers.notAllowed(req, res);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
middleware.buildHeader = function(req, res, next) {
|
module.exports = function(middleware) {
|
||||||
res.locals.renderAdminHeader = true;
|
middleware.admin = {};
|
||||||
|
middleware.admin.isAdmin = function(req, res, next) {
|
||||||
|
winston.warn('[middleware.admin.isAdmin] deprecation warning, no need to use this from plugins!');
|
||||||
|
|
||||||
async.parallel({
|
if (!req.user) {
|
||||||
config: function(next) {
|
return controllers.helpers.notAllowed(req, res);
|
||||||
controllers.api.getConfig(req, res, next);
|
|
||||||
},
|
|
||||||
footer: function(next) {
|
|
||||||
app.render('admin/footer', {}, next);
|
|
||||||
}
|
|
||||||
}, function(err, results) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.config = results.config;
|
user.isAdministrator(req.user.uid, function (err, isAdmin) {
|
||||||
res.locals.adminFooter = results.footer;
|
if (err || isAdmin) {
|
||||||
next();
|
return next(err);
|
||||||
});
|
}
|
||||||
};
|
|
||||||
|
|
||||||
middleware.renderHeader = function(req, res, data, next) {
|
controllers.helpers.notAllowed(req, res);
|
||||||
var custom_header = {
|
});
|
||||||
'plugins': [],
|
|
||||||
'authentication': []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) {
|
middleware.admin.buildHeader = function(req, res, next) {
|
||||||
if (err) {
|
res.locals.renderAdminHeader = true;
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
userData.uid = req.uid;
|
|
||||||
userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1;
|
|
||||||
|
|
||||||
async.parallel({
|
async.parallel({
|
||||||
scripts: function(next) {
|
|
||||||
plugins.fireHook('filter:admin.scripts.get', [], function(err, scripts) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
var arr = [];
|
|
||||||
scripts.forEach(function(script) {
|
|
||||||
arr.push({src: script});
|
|
||||||
});
|
|
||||||
|
|
||||||
next(null, arr);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
custom_header: function(next) {
|
|
||||||
plugins.fireHook('filter:admin.header.build', custom_header, next);
|
|
||||||
},
|
|
||||||
config: function(next) {
|
config: function(next) {
|
||||||
controllers.api.getConfig(req, res, next);
|
controllers.api.getConfig(req, res, next);
|
||||||
},
|
},
|
||||||
configs: function(next) {
|
footer: function(next) {
|
||||||
meta.configs.list(next);
|
req.app.render('admin/footer', {}, next);
|
||||||
}
|
}
|
||||||
}, function(err, results) {
|
}, function(err, results) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.config = results.config;
|
res.locals.config = results.config;
|
||||||
|
res.locals.adminFooter = results.footer;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.admin.renderHeader = function(req, res, data, next) {
|
||||||
|
var custom_header = {
|
||||||
|
'plugins': [],
|
||||||
|
'authentication': []
|
||||||
|
};
|
||||||
|
|
||||||
var acpPath = req.path.slice(1).split('/');
|
user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) {
|
||||||
acpPath.forEach(function(path, i) {
|
if (err) {
|
||||||
acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1);
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
userData.uid = req.uid;
|
||||||
|
userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1;
|
||||||
|
|
||||||
|
async.parallel({
|
||||||
|
scripts: function(next) {
|
||||||
|
plugins.fireHook('filter:admin.scripts.get', [], function(err, scripts) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
var arr = [];
|
||||||
|
scripts.forEach(function(script) {
|
||||||
|
arr.push({src: script});
|
||||||
|
});
|
||||||
|
|
||||||
|
next(null, arr);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
custom_header: function(next) {
|
||||||
|
plugins.fireHook('filter:admin.header.build', custom_header, next);
|
||||||
|
},
|
||||||
|
config: function(next) {
|
||||||
|
controllers.api.getConfig(req, res, next);
|
||||||
|
},
|
||||||
|
configs: function(next) {
|
||||||
|
meta.configs.list(next);
|
||||||
|
}
|
||||||
|
}, function(err, results) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
res.locals.config = results.config;
|
||||||
|
|
||||||
|
var acpPath = req.path.slice(1).split('/');
|
||||||
|
acpPath.forEach(function(path, i) {
|
||||||
|
acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1);
|
||||||
|
});
|
||||||
|
acpPath = acpPath.join(' > ');
|
||||||
|
|
||||||
|
var templateValues = {
|
||||||
|
config: results.config,
|
||||||
|
configJSON: JSON.stringify(results.config),
|
||||||
|
relative_path: results.config.relative_path,
|
||||||
|
adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)),
|
||||||
|
user: userData,
|
||||||
|
userJSON: JSON.stringify(userData).replace(/'/g, "\\'"),
|
||||||
|
plugins: results.custom_header.plugins,
|
||||||
|
authentication: results.custom_header.authentication,
|
||||||
|
scripts: results.scripts,
|
||||||
|
'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
|
||||||
|
env: process.env.NODE_ENV ? true : false,
|
||||||
|
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
|
||||||
|
bodyClass: data.bodyClass
|
||||||
|
};
|
||||||
|
|
||||||
|
templateValues.template = {name: res.locals.template};
|
||||||
|
templateValues.template[res.locals.template] = true;
|
||||||
|
|
||||||
|
req.app.render('admin/header', templateValues, next);
|
||||||
});
|
});
|
||||||
acpPath = acpPath.join(' > ');
|
|
||||||
|
|
||||||
var templateValues = {
|
|
||||||
config: results.config,
|
|
||||||
configJSON: JSON.stringify(results.config),
|
|
||||||
relative_path: results.config.relative_path,
|
|
||||||
adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)),
|
|
||||||
user: userData,
|
|
||||||
userJSON: JSON.stringify(userData).replace(/'/g, "\\'"),
|
|
||||||
plugins: results.custom_header.plugins,
|
|
||||||
authentication: results.custom_header.authentication,
|
|
||||||
scripts: results.scripts,
|
|
||||||
'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
|
|
||||||
env: process.env.NODE_ENV ? true : false,
|
|
||||||
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
|
|
||||||
bodyClass: data.bodyClass
|
|
||||||
};
|
|
||||||
|
|
||||||
templateValues.template = {name: res.locals.template};
|
|
||||||
templateValues.template[res.locals.template] = true;
|
|
||||||
|
|
||||||
app.render('admin/header', templateValues, next);
|
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function(webserver) {
|
|
||||||
app = webserver;
|
|
||||||
return middleware;
|
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
var meta = require('../meta');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function(middleware) {
|
||||||
|
|
||||||
|
middleware.addHeaders = function (req, res, next) {
|
||||||
|
var defaults = {
|
||||||
|
'X-Powered-By': 'NodeBB',
|
||||||
|
'X-Frame-Options': 'SAMEORIGIN',
|
||||||
|
'Access-Control-Allow-Origin': 'null' // yes, string null.
|
||||||
|
};
|
||||||
|
var headers = {
|
||||||
|
'X-Powered-By': meta.config['powered-by'],
|
||||||
|
'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + meta.config['allow-from-uri'] : undefined,
|
||||||
|
'Access-Control-Allow-Origin': meta.config['access-control-allow-origin'],
|
||||||
|
'Access-Control-Allow-Methods': meta.config['access-control-allow-methods'],
|
||||||
|
'Access-Control-Allow-Headers': meta.config['access-control-allow-headers']
|
||||||
|
};
|
||||||
|
|
||||||
|
_.defaults(headers, defaults);
|
||||||
|
headers = _.pick(headers, Boolean); // Remove falsy headers
|
||||||
|
|
||||||
|
for(var key in headers) {
|
||||||
|
if (headers.hasOwnProperty(key)) {
|
||||||
|
res.setHeader(key, headers[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.addExpiresHeaders = function(req, res, next) {
|
||||||
|
if (req.app.enabled('cache')) {
|
||||||
|
res.setHeader("Cache-Control", "public, max-age=5184000");
|
||||||
|
res.setHeader("Expires", new Date(Date.now() + 5184000000).toUTCString());
|
||||||
|
} else {
|
||||||
|
res.setHeader("Cache-Control", "public, max-age=0");
|
||||||
|
res.setHeader("Expires", new Date().toUTCString());
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,83 +1,202 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var meta = require('../meta'),
|
var async = require('async');
|
||||||
db = require('../database'),
|
var fs = require('fs');
|
||||||
file = require('../file'),
|
var path = require('path');
|
||||||
auth = require('../routes/authentication'),
|
var csrf = require('csurf');
|
||||||
|
var validator = require('validator');
|
||||||
path = require('path'),
|
var nconf = require('nconf');
|
||||||
nconf = require('nconf'),
|
var ensureLoggedIn = require('connect-ensure-login');
|
||||||
flash = require('connect-flash'),
|
var toobusy = require('toobusy-js');
|
||||||
templates = require('templates.js'),
|
|
||||||
bodyParser = require('body-parser'),
|
var plugins = require('../plugins');
|
||||||
cookieParser = require('cookie-parser'),
|
var languages = require('../languages');
|
||||||
compression = require('compression'),
|
var meta = require('../meta');
|
||||||
favicon = require('serve-favicon'),
|
var user = require('../user');
|
||||||
session = require('express-session'),
|
var groups = require('../groups');
|
||||||
useragent = require('express-useragent');
|
|
||||||
|
var analytics = require('../analytics');
|
||||||
|
|
||||||
|
var controllers = {
|
||||||
|
api: require('./../controllers/api'),
|
||||||
|
helpers: require('../controllers/helpers')
|
||||||
|
};
|
||||||
|
|
||||||
var middleware = {};
|
var middleware = {};
|
||||||
|
|
||||||
function setupFavicon(app) {
|
middleware.applyCSRF = csrf();
|
||||||
var faviconPath = path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico');
|
|
||||||
if (file.existsSync(faviconPath)) {
|
middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login');
|
||||||
app.use(nconf.get('relative_path'), favicon(faviconPath));
|
|
||||||
|
require('./admin')(middleware);
|
||||||
|
require('./header')(middleware);
|
||||||
|
require('./render')(middleware);
|
||||||
|
require('./maintenance')(middleware);
|
||||||
|
require('./user')(middleware);
|
||||||
|
require('./headers')(middleware);
|
||||||
|
|
||||||
|
middleware.authenticate = function(req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
return next();
|
||||||
|
} else if (plugins.hasListeners('action:middleware.authenticate')) {
|
||||||
|
return plugins.fireHook('action:middleware.authenticate', {
|
||||||
|
req: req,
|
||||||
|
res: res,
|
||||||
|
next: next
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(app) {
|
controllers.helpers.notAllowed(req, res);
|
||||||
var relativePath = nconf.get('relative_path');
|
};
|
||||||
|
|
||||||
middleware = require('./middleware')(app);
|
middleware.pageView = function(req, res, next) {
|
||||||
|
analytics.pageView({
|
||||||
|
ip: req.ip,
|
||||||
|
path: req.path,
|
||||||
|
uid: req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') ? parseInt(req.user.uid, 10) : 0
|
||||||
|
});
|
||||||
|
|
||||||
|
plugins.fireHook('action:middleware.pageView', {req: req});
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
user.updateLastOnlineTime(req.user.uid);
|
||||||
|
if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) {
|
||||||
|
user.updateOnlineUsers(req.user.uid, next);
|
||||||
|
} else {
|
||||||
|
user.updateOnlineUsers(req.user.uid);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
app.engine('tpl', templates.__express);
|
|
||||||
app.set('view engine', 'tpl');
|
|
||||||
app.set('views', nconf.get('views_dir'));
|
|
||||||
app.set('json spaces', process.env.NODE_ENV === 'development' ? 4 : 0);
|
|
||||||
app.use(flash());
|
|
||||||
|
|
||||||
app.enable('view cache');
|
middleware.pluginHooks = function(req, res, next) {
|
||||||
|
async.each(plugins.loadedHooks['filter:router.page'] || [], function(hookObj, next) {
|
||||||
|
hookObj.method(req, res, next);
|
||||||
|
}, function() {
|
||||||
|
// If it got here, then none of the subscribed hooks did anything, or there were no hooks
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
app.use(compression());
|
middleware.validateFiles = function(req, res, next) {
|
||||||
|
if (!Array.isArray(req.files.files) || !req.files.files.length) {
|
||||||
|
return next(new Error(['[[error:invalid-files]]']));
|
||||||
|
}
|
||||||
|
|
||||||
setupFavicon(app);
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon);
|
middleware.prepareAPI = function(req, res, next) {
|
||||||
|
res.locals.isAPI = true;
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.routeTouchIcon = function(req, res) {
|
||||||
|
if (meta.config['brand:touchIcon'] && validator.isURL(meta.config['brand:touchIcon'])) {
|
||||||
|
return res.redirect(meta.config['brand:touchIcon']);
|
||||||
|
} else {
|
||||||
|
return res.sendFile(path.join(__dirname, '../../public', meta.config['brand:touchIcon'] || '/logo.png'), {
|
||||||
|
maxAge: req.app.enabled('cache') ? 5184000000 : 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({extended: true}));
|
middleware.privateTagListing = function(req, res, next) {
|
||||||
app.use(bodyParser.json());
|
if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) {
|
||||||
app.use(cookieParser());
|
controllers.helpers.notAllowed(req, res);
|
||||||
app.use(useragent.express());
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var cookie = {
|
middleware.exposeGroupName = function(req, res, next) {
|
||||||
maxAge: 1000 * 60 * 60 * 24 * (parseInt(meta.config.loginDays, 10) || 14)
|
expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (meta.config.cookieDomain) {
|
middleware.exposeUid = function(req, res, next) {
|
||||||
cookie.domain = meta.config.cookieDomain;
|
expose('uid', user.getUidByUserslug, 'userslug', req, res, next);
|
||||||
|
};
|
||||||
|
|
||||||
|
function expose(exposedField, method, field, req, res, next) {
|
||||||
|
if (!req.params.hasOwnProperty(field)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
method(req.params[field], function(err, id) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals[exposedField] = id;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.privateUploads = function(req, res, next) {
|
||||||
|
if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) {
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
|
if (req.path.startsWith('/uploads/files')) {
|
||||||
|
return res.status(403).json('not-allowed');
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
if (nconf.get('secure')) {
|
middleware.busyCheck = function(req, res, next) {
|
||||||
cookie.secure = true;
|
if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) {
|
||||||
|
analytics.increment('errors:503');
|
||||||
|
res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html'));
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.applyBlacklist = function(req, res, next) {
|
||||||
|
meta.blacklist.test(req.ip, function(err) {
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.processLanguages = function(req, res, next) {
|
||||||
|
var code = req.params.code;
|
||||||
|
var key = req.path.match(/[\w]+\.json/);
|
||||||
|
|
||||||
if (relativePath !== '') {
|
if (code && key) {
|
||||||
cookie.path = relativePath;
|
languages.get(code, key[0], function(err, language) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json(language);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).json('{}');
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
app.use(session({
|
middleware.processTimeagoLocales = function(req, res, next) {
|
||||||
store: db.sessionStore,
|
var fallback = req.path.indexOf('-short') === -1 ? 'jquery.timeago.en.js' : 'jquery.timeago.en-short.js',
|
||||||
secret: nconf.get('secret'),
|
localPath = path.join(__dirname, '../../public/vendor/jquery/timeago/locales', req.path),
|
||||||
key: 'express.sid',
|
exists;
|
||||||
cookie: cookie,
|
|
||||||
resave: true,
|
|
||||||
saveUninitialized: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.use(middleware.addHeaders);
|
try {
|
||||||
app.use(middleware.processRender);
|
exists = fs.accessSync(localPath, fs.F_OK | fs.R_OK);
|
||||||
auth.initialize(app, middleware);
|
} catch(e) {
|
||||||
|
exists = false;
|
||||||
|
}
|
||||||
|
|
||||||
return middleware;
|
if (exists) {
|
||||||
|
res.status(200).sendFile(localPath, {
|
||||||
|
maxAge: req.app.enabled('cache') ? 5184000000 : 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(200).sendFile(path.join(__dirname, '../../public/vendor/jquery/timeago/locales', fallback), {
|
||||||
|
maxAge: req.app.enabled('cache') ? 5184000000 : 0
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = middleware;
|
||||||
|
@ -1,384 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
var app;
|
|
||||||
var middleware = {
|
|
||||||
admin: {}
|
|
||||||
};
|
|
||||||
var async = require('async');
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var csrf = require('csurf');
|
|
||||||
var _ = require('underscore');
|
|
||||||
|
|
||||||
var validator = require('validator');
|
|
||||||
var nconf = require('nconf');
|
|
||||||
var ensureLoggedIn = require('connect-ensure-login');
|
|
||||||
var toobusy = require('toobusy-js');
|
|
||||||
|
|
||||||
var plugins = require('../plugins');
|
|
||||||
var languages = require('../languages');
|
|
||||||
var meta = require('../meta');
|
|
||||||
var user = require('../user');
|
|
||||||
var groups = require('../groups');
|
|
||||||
|
|
||||||
var analytics = require('../analytics');
|
|
||||||
|
|
||||||
var controllers = {
|
|
||||||
api: require('./../controllers/api'),
|
|
||||||
helpers: require('../controllers/helpers')
|
|
||||||
};
|
|
||||||
|
|
||||||
toobusy.maxLag(parseInt(meta.config.eventLoopLagThreshold, 10) || 100);
|
|
||||||
toobusy.interval(parseInt(meta.config.eventLoopInterval, 10) || 500);
|
|
||||||
|
|
||||||
middleware.authenticate = function(req, res, next) {
|
|
||||||
if (req.user) {
|
|
||||||
return next();
|
|
||||||
} else if (plugins.hasListeners('action:middleware.authenticate')) {
|
|
||||||
return plugins.fireHook('action:middleware.authenticate', {
|
|
||||||
req: req,
|
|
||||||
res: res,
|
|
||||||
next: next
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
controllers.helpers.notAllowed(req, res);
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.applyCSRF = csrf();
|
|
||||||
|
|
||||||
middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login');
|
|
||||||
|
|
||||||
middleware.pageView = function(req, res, next) {
|
|
||||||
analytics.pageView({
|
|
||||||
ip: req.ip,
|
|
||||||
path: req.path,
|
|
||||||
uid: req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') ? parseInt(req.user.uid, 10) : 0
|
|
||||||
});
|
|
||||||
|
|
||||||
plugins.fireHook('action:middleware.pageView', {req: req});
|
|
||||||
|
|
||||||
if (req.user) {
|
|
||||||
user.updateLastOnlineTime(req.user.uid);
|
|
||||||
if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) {
|
|
||||||
user.updateOnlineUsers(req.user.uid, next);
|
|
||||||
} else {
|
|
||||||
user.updateOnlineUsers(req.user.uid);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.addHeaders = function (req, res, next) {
|
|
||||||
var defaults = {
|
|
||||||
'X-Powered-By': 'NodeBB',
|
|
||||||
'X-Frame-Options': 'SAMEORIGIN',
|
|
||||||
'Access-Control-Allow-Origin': 'null' // yes, string null.
|
|
||||||
};
|
|
||||||
var headers = {
|
|
||||||
'X-Powered-By': meta.config['powered-by'],
|
|
||||||
'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + meta.config['allow-from-uri'] : undefined,
|
|
||||||
'Access-Control-Allow-Origin': meta.config['access-control-allow-origin'],
|
|
||||||
'Access-Control-Allow-Methods': meta.config['access-control-allow-methods'],
|
|
||||||
'Access-Control-Allow-Headers': meta.config['access-control-allow-headers']
|
|
||||||
};
|
|
||||||
|
|
||||||
_.defaults(headers, defaults);
|
|
||||||
headers = _.pick(headers, Boolean); // Remove falsy headers
|
|
||||||
|
|
||||||
for(var key in headers) {
|
|
||||||
if (headers.hasOwnProperty(key)) {
|
|
||||||
res.setHeader(key, headers[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.pluginHooks = function(req, res, next) {
|
|
||||||
async.each(plugins.loadedHooks['filter:router.page'] || [], function(hookObj, next) {
|
|
||||||
hookObj.method(req, res, next);
|
|
||||||
}, function() {
|
|
||||||
// If it got here, then none of the subscribed hooks did anything, or there were no hooks
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.redirectToAccountIfLoggedIn = function(req, res, next) {
|
|
||||||
if (req.session.forceLogin) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!req.user) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
controllers.helpers.redirect(res, '/user/' + userslug);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.validateFiles = function(req, res, next) {
|
|
||||||
if (!Array.isArray(req.files.files) || !req.files.files.length) {
|
|
||||||
return next(new Error(['[[error:invalid-files]]']));
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.prepareAPI = function(req, res, next) {
|
|
||||||
res.locals.isAPI = true;
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.checkGlobalPrivacySettings = function(req, res, next) {
|
|
||||||
if (!req.user && !!parseInt(meta.config.privateUserInfo, 10)) {
|
|
||||||
return controllers.helpers.notAllowed(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.checkAccountPermissions = function(req, res, next) {
|
|
||||||
// This middleware ensures that only the requested user and admins can pass
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
middleware.authenticate(req, res, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
user.getUidByUserslug(req.params.userslug, next);
|
|
||||||
},
|
|
||||||
function (uid, next) {
|
|
||||||
if (parseInt(uid, 10) === req.uid) {
|
|
||||||
return next(null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.isAdminOrGlobalMod(req.uid, next);
|
|
||||||
}
|
|
||||||
], function (err, allowed) {
|
|
||||||
if (err || allowed) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
controllers.helpers.notAllowed(req, res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.redirectUidToUserslug = function(req, res, next) {
|
|
||||||
var uid = parseInt(req.params.uid, 10);
|
|
||||||
if (!uid) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
user.getUserField(uid, 'userslug', function(err, userslug) {
|
|
||||||
if (err || !userslug) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = req.path.replace(/^\/api/, '')
|
|
||||||
.replace('uid', 'user')
|
|
||||||
.replace(uid, function() { return userslug; });
|
|
||||||
controllers.helpers.redirect(res, path);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.isAdmin = function(req, res, next) {
|
|
||||||
if (!req.uid) {
|
|
||||||
return controllers.helpers.notAllowed(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.isAdministrator(req.uid, function (err, isAdmin) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAdmin) {
|
|
||||||
user.hasPassword(req.uid, function(err, hasPassword) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasPassword) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
var loginTime = req.session.meta ? req.session.meta.datetime : 0;
|
|
||||||
if (loginTime && parseInt(loginTime, 10) > Date.now() - 3600000) {
|
|
||||||
var timeLeft = parseInt(loginTime, 10) - (Date.now() - 3600000);
|
|
||||||
if (timeLeft < 300000) {
|
|
||||||
req.session.meta.datetime += 300000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
req.session.returnTo = req.path.replace(/^\/api/, '');
|
|
||||||
req.session.forceLogin = 1;
|
|
||||||
if (res.locals.isAPI) {
|
|
||||||
res.status(401).json({});
|
|
||||||
} else {
|
|
||||||
res.redirect(nconf.get('relative_path') + '/login');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.locals.isAPI) {
|
|
||||||
return controllers.helpers.notAllowed(req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware.buildHeader(req, res, function() {
|
|
||||||
controllers.helpers.notAllowed(req, res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.routeTouchIcon = function(req, res) {
|
|
||||||
if (meta.config['brand:touchIcon'] && validator.isURL(meta.config['brand:touchIcon'])) {
|
|
||||||
return res.redirect(meta.config['brand:touchIcon']);
|
|
||||||
} else {
|
|
||||||
return res.sendFile(path.join(__dirname, '../../public', meta.config['brand:touchIcon'] || '/logo.png'), {
|
|
||||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.addExpiresHeaders = function(req, res, next) {
|
|
||||||
if (app.enabled('cache')) {
|
|
||||||
res.setHeader("Cache-Control", "public, max-age=5184000");
|
|
||||||
res.setHeader("Expires", new Date(Date.now() + 5184000000).toUTCString());
|
|
||||||
} else {
|
|
||||||
res.setHeader("Cache-Control", "public, max-age=0");
|
|
||||||
res.setHeader("Expires", new Date().toUTCString());
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.privateTagListing = function(req, res, next) {
|
|
||||||
if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) {
|
|
||||||
controllers.helpers.notAllowed(req, res);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.exposeGroupName = function(req, res, next) {
|
|
||||||
expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next);
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.exposeUid = function(req, res, next) {
|
|
||||||
expose('uid', user.getUidByUserslug, 'userslug', req, res, next);
|
|
||||||
};
|
|
||||||
|
|
||||||
function expose(exposedField, method, field, req, res, next) {
|
|
||||||
if (!req.params.hasOwnProperty(field)) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
method(req.params[field], function(err, id) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.locals[exposedField] = id;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware.requireUser = function(req, res, next) {
|
|
||||||
if (req.user) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(403).render('403', {title: '[[global:403.title]]'});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.privateUploads = function(req, res, next) {
|
|
||||||
if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
if (req.path.startsWith('/uploads/files')) {
|
|
||||||
return res.status(403).json('not-allowed');
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.busyCheck = function(req, res, next) {
|
|
||||||
if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) {
|
|
||||||
analytics.increment('errors:503');
|
|
||||||
res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html'));
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.applyBlacklist = function(req, res, next) {
|
|
||||||
meta.blacklist.test(req.ip, function(err) {
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.processLanguages = function(req, res, next) {
|
|
||||||
var code = req.params.code;
|
|
||||||
var key = req.path.match(/[\w]+\.json/);
|
|
||||||
|
|
||||||
if (code && key) {
|
|
||||||
languages.get(code, key[0], function(err, language) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json(language);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.status(404).json('{}');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.processTimeagoLocales = function(req, res, next) {
|
|
||||||
var fallback = req.path.indexOf('-short') === -1 ? 'jquery.timeago.en.js' : 'jquery.timeago.en-short.js',
|
|
||||||
localPath = path.join(__dirname, '../../public/vendor/jquery/timeago/locales', req.path),
|
|
||||||
exists;
|
|
||||||
|
|
||||||
try {
|
|
||||||
exists = fs.accessSync(localPath, fs.F_OK | fs.R_OK);
|
|
||||||
} catch(e) {
|
|
||||||
exists = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
res.status(200).sendFile(localPath, {
|
|
||||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.status(200).sendFile(path.join(__dirname, '../../public/vendor/jquery/timeago/locales', fallback), {
|
|
||||||
maxAge: app.enabled('cache') ? 5184000000 : 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
middleware.registrationComplete = function(req, res, next) {
|
|
||||||
// If the user's session contains registration data, redirect the user to complete registration
|
|
||||||
if (!req.session.hasOwnProperty('registration')) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
if (!req.path.endsWith('/register/complete')) {
|
|
||||||
controllers.helpers.redirect(res, '/register/complete');
|
|
||||||
} else {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function(webserver) {
|
|
||||||
app = webserver;
|
|
||||||
middleware.admin = require('./admin')(webserver);
|
|
||||||
|
|
||||||
require('./header')(app, middleware);
|
|
||||||
require('./render')(middleware);
|
|
||||||
require('./maintenance')(middleware);
|
|
||||||
|
|
||||||
return middleware;
|
|
||||||
};
|
|
@ -0,0 +1,154 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var meta = require('../meta');
|
||||||
|
var user = require('../user');
|
||||||
|
|
||||||
|
var controllers = {
|
||||||
|
helpers: require('../controllers/helpers')
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function(middleware) {
|
||||||
|
|
||||||
|
middleware.checkGlobalPrivacySettings = function(req, res, next) {
|
||||||
|
if (!req.user && !!parseInt(meta.config.privateUserInfo, 10)) {
|
||||||
|
return controllers.helpers.notAllowed(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.checkAccountPermissions = function(req, res, next) {
|
||||||
|
// This middleware ensures that only the requested user and admins can pass
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
middleware.authenticate(req, res, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
user.getUidByUserslug(req.params.userslug, next);
|
||||||
|
},
|
||||||
|
function (uid, next) {
|
||||||
|
if (parseInt(uid, 10) === req.uid) {
|
||||||
|
return next(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.isAdminOrGlobalMod(req.uid, next);
|
||||||
|
}
|
||||||
|
], function (err, allowed) {
|
||||||
|
if (err || allowed) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
controllers.helpers.notAllowed(req, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.redirectToAccountIfLoggedIn = function(req, res, next) {
|
||||||
|
if (req.session.forceLogin) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.user) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
user.getUserField(req.user.uid, 'userslug', function (err, userslug) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
controllers.helpers.redirect(res, '/user/' + userslug);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.redirectUidToUserslug = function(req, res, next) {
|
||||||
|
var uid = parseInt(req.params.uid, 10);
|
||||||
|
if (!uid) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
user.getUserField(uid, 'userslug', function(err, userslug) {
|
||||||
|
if (err || !userslug) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = req.path.replace(/^\/api/, '')
|
||||||
|
.replace('uid', 'user')
|
||||||
|
.replace(uid, function() { return userslug; });
|
||||||
|
controllers.helpers.redirect(res, path);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.isAdmin = function(req, res, next) {
|
||||||
|
if (!req.uid) {
|
||||||
|
return controllers.helpers.notAllowed(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.isAdministrator(req.uid, function (err, isAdmin) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAdmin) {
|
||||||
|
user.hasPassword(req.uid, function(err, hasPassword) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPassword) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginTime = req.session.meta ? req.session.meta.datetime : 0;
|
||||||
|
if (loginTime && parseInt(loginTime, 10) > Date.now() - 3600000) {
|
||||||
|
var timeLeft = parseInt(loginTime, 10) - (Date.now() - 3600000);
|
||||||
|
if (timeLeft < 300000) {
|
||||||
|
req.session.meta.datetime += 300000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
req.session.returnTo = req.path.replace(/^\/api/, '');
|
||||||
|
req.session.forceLogin = 1;
|
||||||
|
if (res.locals.isAPI) {
|
||||||
|
res.status(401).json({});
|
||||||
|
} else {
|
||||||
|
res.redirect(nconf.get('relative_path') + '/login');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.locals.isAPI) {
|
||||||
|
return controllers.helpers.notAllowed(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.buildHeader(req, res, function() {
|
||||||
|
controllers.helpers.notAllowed(req, res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.requireUser = function(req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(403).render('403', {title: '[[global:403.title]]'});
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.registrationComplete = function(req, res, next) {
|
||||||
|
// If the user's session contains registration data, redirect the user to complete registration
|
||||||
|
if (!req.session.hasOwnProperty('registration')) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
if (!req.path.endsWith('/register/complete')) {
|
||||||
|
controllers.helpers.redirect(res, '/register/complete');
|
||||||
|
} else {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue