refactor: async/await middleware

v1.18.x
Barış Soner Uşaklı 5 years ago
parent 610ecf352b
commit a227cbe328

@ -1,31 +1,29 @@
'use strict'; 'use strict';
var async = require('async'); const util = require('util');
var nconf = require('nconf'); const nconf = require('nconf');
var meta = require('../meta'); const meta = require('../meta');
var user = require('../user'); const user = require('../user');
module.exports = function (middleware) { module.exports = function (middleware) {
middleware.maintenanceMode = function maintenanceMode(req, res, callback) { middleware.maintenanceMode = async function maintenanceMode(req, res, next) {
if (!meta.config.maintenanceMode) { if (!meta.config.maintenanceMode) {
return setImmediate(callback); return setImmediate(next);
} }
var url = req.url.replace(nconf.get('relative_path'), '');
const url = req.url.replace(nconf.get('relative_path'), '');
if (url.startsWith('/login') || url.startsWith('/api/login')) { if (url.startsWith('/login') || url.startsWith('/api/login')) {
return setImmediate(callback); return setImmediate(next);
} }
var data;
async.waterfall([ const isAdmin = await user.isAdministrator(req.uid);
function (next) {
user.isAdministrator(req.uid, next);
},
function (isAdmin, next) {
if (isAdmin) { if (isAdmin) {
return callback(); return setImmediate(next);
} }
res.status(meta.config.maintenanceModeStatus); res.status(meta.config.maintenanceModeStatus);
data = {
const data = {
site_title: meta.config.title || 'NodeBB', site_title: meta.config.title || 'NodeBB',
message: meta.config.maintenanceModeMessage, message: meta.config.maintenanceModeMessage,
}; };
@ -33,12 +31,8 @@ module.exports = function (middleware) {
if (res.locals.isAPI) { if (res.locals.isAPI) {
return res.json(data); return res.json(data);
} }
const buildHeaderAsync = util.promisify(middleware.buildHeader);
middleware.buildHeader(req, res, next); await buildHeaderAsync(req, res);
},
function () {
res.render('503', data); res.render('503', data);
},
], callback);
}; };
}; };

@ -1,11 +1,11 @@
'use strict'; 'use strict';
var winston = require('winston'); const winston = require('winston');
var ratelimit = module.exports; const ratelimit = module.exports;
var allowedCalls = 100; const allowedCalls = 100;
var timeframe = 10000; const timeframe = 10000;
ratelimit.isFlooding = function (socket) { ratelimit.isFlooding = function (socket) {
socket.callsPerSecond = socket.callsPerSecond || 0; socket.callsPerSecond = socket.callsPerSecond || 0;
@ -14,7 +14,7 @@ ratelimit.isFlooding = function (socket) {
socket.callsPerSecond += 1; socket.callsPerSecond += 1;
var now = Date.now(); const now = Date.now();
socket.elapsedTime += now - socket.lastCallTime; socket.elapsedTime += now - socket.lastCallTime;
if (socket.callsPerSecond > allowedCalls && socket.elapsedTime < timeframe) { if (socket.callsPerSecond > allowedCalls && socket.elapsedTime < timeframe) {

@ -1,77 +1,53 @@
'use strict'; 'use strict';
var async = require('async'); const util = require('util');
var nconf = require('nconf'); const nconf = require('nconf');
var validator = require('validator'); const validator = require('validator');
var winston = require('winston'); const winston = require('winston');
var plugins = require('../plugins'); const plugins = require('../plugins');
var meta = require('../meta'); const meta = require('../meta');
var translator = require('../translator'); const translator = require('../translator');
var widgets = require('../widgets'); const widgets = require('../widgets');
var utils = require('../utils'); const utils = require('../utils');
module.exports = function (middleware) { module.exports = function (middleware) {
const renderHeaderFooterAsync = util.promisify(renderHeaderFooter);
middleware.processRender = function processRender(req, res, next) { middleware.processRender = function processRender(req, res, next) {
// res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687 // res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687
var render = res.render; const render = res.render;
res.render = function renderOverride(template, options, fn) { res.render = async function renderOverride(template, options, fn) {
var self = this; const self = this;
var req = this.req; const req = this.req;
var defaultFn = function (err, str) {
if (err) {
return next(err);
}
self.send(str);
};
options = options || {}; options = options || {};
if (typeof options === 'function') { if (typeof options === 'function') {
fn = options; fn = options;
options = {}; options = {};
} }
if (typeof fn !== 'function') {
fn = defaultFn;
}
var ajaxifyData;
var templateToRender;
async.waterfall([
function (next) {
options.loggedIn = req.uid > 0; options.loggedIn = req.uid > 0;
options.relative_path = nconf.get('relative_path'); options.relative_path = nconf.get('relative_path');
options.template = { name: template }; options.template = { name: template, [template]: true };
options.template[template] = true;
options.url = (req.baseUrl + req.path.replace(/^\/api/, '')); options.url = (req.baseUrl + req.path.replace(/^\/api/, ''));
options.bodyClass = buildBodyClass(req, res, options); options.bodyClass = buildBodyClass(req, res, options);
plugins.fireHook('filter:' + template + '.build', { req: req, res: res, templateData: options }, next);
}, const buildResult = await plugins.fireHook('filter:' + template + '.build', { req: req, res: res, templateData: options });
function (data, next) { const templateToRender = buildResult.templateData.templateToRender || template;
templateToRender = data.templateData.templateToRender || template;
plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: data.templateData }, next); const renderResult = await plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: buildResult.templateData });
}, options = renderResult.templateData;
function parseTags(data, next) {
meta.tags.parse(req, data, res.locals.metaTags, res.locals.linkTags, function (err, tags) {
options._header = { options._header = {
tags: tags, tags: await meta.tags.parse(req, renderResult, res.locals.metaTags, res.locals.linkTags),
}; };
next(err, data); options.widgets = await widgets.render(req.uid, {
});
},
function (data, next) {
options = data.templateData;
widgets.render(req.uid, {
template: template + '.tpl', template: template + '.tpl',
url: options.url, url: options.url,
templateData: options, templateData: options,
req: req, req: req,
res: res, res: res,
}, next); });
},
function (data, next) {
options.widgets = data;
res.locals.template = template; res.locals.template = template;
options._locals = undefined; options._locals = undefined;
@ -82,37 +58,32 @@ module.exports = function (middleware) {
req.app.set('json spaces', global.env === 'development' || req.query.pretty ? 4 : 0); req.app.set('json spaces', global.env === 'development' || req.query.pretty ? 4 : 0);
return res.json(options); return res.json(options);
} }
const ajaxifyData = JSON.stringify(options).replace(/<\//g, '<\\/');
const renderAsync = util.promisify((templateToRender, options, next) => render.call(self, templateToRender, options, next));
ajaxifyData = JSON.stringify(options).replace(/<\//g, '<\\/'); const results = await utils.promiseParallel({
header: renderHeaderFooterAsync('renderHeader', req, res, options),
async.parallel({ content: renderAsync(templateToRender, options),
header: function (next) { footer: renderHeaderFooterAsync('renderFooter', req, res, options),
renderHeaderFooter('renderHeader', req, res, options, next); });
},
content: function (next) { const str = results.header +
render.call(self, templateToRender, options, next);
},
footer: function (next) {
renderHeaderFooter('renderFooter', req, res, options, next);
},
}, next);
},
function (results, next) {
var str = results.header +
(res.locals.postHeader || '') + (res.locals.postHeader || '') +
results.content + '<script id="ajaxify-data"></script>' + results.content + '<script id="ajaxify-data"></script>' +
(res.locals.preFooter || '') + (res.locals.preFooter || '') +
results.footer; results.footer;
translate(str, req, res, next); let translated = await translate(str, req, res);
},
function (translated, next) {
translated = translated.replace('<script id="ajaxify-data"></script>', function () { translated = translated.replace('<script id="ajaxify-data"></script>', function () {
return '<script id="ajaxify-data" type="application/json">' + ajaxifyData + '</script>'; return '<script id="ajaxify-data" type="application/json">' + ajaxifyData + '</script>';
}); });
next(null, translated);
}, if (typeof fn !== 'function') {
], fn); self.send(translated);
} else {
fn(null, translated);
}
}; };
next(); next();
@ -128,20 +99,19 @@ module.exports = function (middleware) {
} }
} }
function translate(str, req, res, next) { async function translate(str, req, res) {
var language = (res.locals.config && res.locals.config.userLang) || 'en-GB'; let language = (res.locals.config && res.locals.config.userLang) || 'en-GB';
if (res.locals.renderAdminHeader) { if (res.locals.renderAdminHeader) {
language = (res.locals.config && res.locals.config.acpLang) || 'en-GB'; language = (res.locals.config && res.locals.config.acpLang) || 'en-GB';
} }
language = req.query.lang ? validator.escape(String(req.query.lang)) : language; language = req.query.lang ? validator.escape(String(req.query.lang)) : language;
translator.translate(str, language, function (translated) { const translated = await translator.translate(str, language);
next(null, translator.unescape(translated)); return translator.unescape(translated);
});
} }
function buildBodyClass(req, res, templateData) { function buildBodyClass(req, res, templateData) {
var clean = req.path.replace(/^\/api/, '').replace(/^\/|\/$/g, ''); const clean = req.path.replace(/^\/api/, '').replace(/^\/|\/$/g, '');
var parts = clean.split('/').slice(0, 3); const parts = clean.split('/').slice(0, 3);
parts.forEach(function (p, index) { parts.forEach(function (p, index) {
try { try {
p = utils.slugify(decodeURIComponent(p)); p = utils.slugify(decodeURIComponent(p));

@ -1,17 +1,17 @@
'use strict'; 'use strict';
var async = require('async'); const util = require('util');
var nconf = require('nconf'); const nconf = require('nconf');
var winston = require('winston'); const winston = require('winston');
var meta = require('../meta'); const meta = require('../meta');
var user = require('../user'); const user = require('../user');
var privileges = require('../privileges'); const privileges = require('../privileges');
var plugins = require('../plugins'); const plugins = require('../plugins');
var auth = require('../routes/authentication'); const auth = require('../routes/authentication');
var controllers = { const controllers = {
helpers: require('../controllers/helpers'), helpers: require('../controllers/helpers'),
}; };
@ -49,6 +49,8 @@ module.exports = function (middleware) {
}); });
}; };
const authenticateAsync = util.promisify(middleware.authenticate);
middleware.authenticateOrGuest = function authenticateOrGuest(req, res, next) { middleware.authenticateOrGuest = function authenticateOrGuest(req, res, next) {
authenticate(req, res, next, next); authenticate(req, res, next, next);
}; };
@ -61,30 +63,21 @@ module.exports = function (middleware) {
ensureSelfOrMethod(user.isPrivileged, req, res, next); ensureSelfOrMethod(user.isPrivileged, req, res, next);
}; };
function ensureSelfOrMethod(method, req, res, next) { async function ensureSelfOrMethod(method, req, res, next) {
/* /*
The "self" part of this middleware hinges on you having used The "self" part of this middleware hinges on you having used
middleware.exposeUid prior to invoking this middleware. middleware.exposeUid prior to invoking this middleware.
*/ */
async.waterfall([
function (next) {
if (!req.loggedIn) { if (!req.loggedIn) {
return setImmediate(next, null, false); return controllers.helpers.notAllowed(req, res);
} }
if (req.uid === parseInt(res.locals.uid, 10)) { if (req.uid === parseInt(res.locals.uid, 10)) {
return setImmediate(next, null, true); return setImmediate(next);
} }
const allowed = await method(req.uid);
method(req.uid, next);
},
function (allowed, next) {
if (!allowed) { if (!allowed) {
return controllers.helpers.notAllowed(req, res); return controllers.helpers.notAllowed(req, res);
} }
next();
},
], next);
} }
middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) { middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) {
@ -92,110 +85,73 @@ module.exports = function (middleware) {
middleware.canViewUsers(req, res, next); middleware.canViewUsers(req, res, next);
}; };
middleware.canViewUsers = function canViewUsers(req, res, next) { middleware.canViewUsers = async function canViewUsers(req, res, next) {
if (parseInt(res.locals.uid, 10) === req.uid) { if (parseInt(res.locals.uid, 10) === req.uid) {
return next(); return next();
} }
privileges.global.can('view:users', req.uid, function (err, canView) { const canView = await privileges.global.can('view:users', req.uid);
if (err || canView) { if (canView) {
return next(err); return next();
} }
controllers.helpers.notAllowed(req, res); controllers.helpers.notAllowed(req, res);
});
}; };
middleware.canViewGroups = function canViewGroups(req, res, next) { middleware.canViewGroups = async function canViewGroups(req, res, next) {
privileges.global.can('view:groups', req.uid, function (err, canView) { const canView = await privileges.global.can('view:groups', req.uid);
if (err || canView) { if (canView) {
return next(err); return next();
} }
controllers.helpers.notAllowed(req, res); controllers.helpers.notAllowed(req, res);
});
}; };
middleware.checkAccountPermissions = function checkAccountPermissions(req, res, next) { middleware.checkAccountPermissions = async function checkAccountPermissions(req, res, next) {
// This middleware ensures that only the requested user and admins can pass // This middleware ensures that only the requested user and admins can pass
async.waterfall([ await authenticateAsync(req, res);
function (next) { const uid = await user.getUidByUserslug(req.params.userslug);
middleware.authenticate(req, res, next); let allowed = await privileges.users.canEdit(req.uid, uid);
},
function (next) {
user.getUidByUserslug(req.params.userslug, next);
},
function (uid, next) {
privileges.users.canEdit(req.uid, uid, next);
},
function (allowed, next) {
if (allowed) { if (allowed) {
return next(null, allowed); return next();
} }
// For the account/info page only, allow plain moderators through
if (/user\/.+\/info$/.test(req.path)) { if (/user\/.+\/info$/.test(req.path)) {
privileges.global.can('view:users:info', req.uid, next); allowed = await privileges.global.can('view:users:info', req.uid);
} else {
next(null, false);
} }
},
function (allowed) {
if (allowed) { if (allowed) {
return next(); return next();
} }
controllers.helpers.notAllowed(req, res); controllers.helpers.notAllowed(req, res);
},
], next);
}; };
middleware.redirectToAccountIfLoggedIn = function redirectToAccountIfLoggedIn(req, res, next) { middleware.redirectToAccountIfLoggedIn = async function redirectToAccountIfLoggedIn(req, res, next) {
if (req.session.forceLogin || req.uid <= 0) { if (req.session.forceLogin || req.uid <= 0) {
return next(); return next();
} }
const userslug = await user.getUserField(req.uid, 'userslug');
async.waterfall([
function (next) {
user.getUserField(req.uid, 'userslug', next);
},
function (userslug) {
controllers.helpers.redirect(res, '/user/' + userslug); controllers.helpers.redirect(res, '/user/' + userslug);
},
], next);
}; };
middleware.redirectUidToUserslug = function redirectUidToUserslug(req, res, next) { middleware.redirectUidToUserslug = async function redirectUidToUserslug(req, res, next) {
var uid = parseInt(req.params.uid, 10); const uid = parseInt(req.params.uid, 10);
if (uid <= 0) { if (uid <= 0) {
return next(); return next();
} }
async.waterfall([ const userslug = await user.getUserField(uid, 'userslug');
function (next) {
user.getUserField(uid, 'userslug', next);
},
function (userslug) {
if (!userslug) { if (!userslug) {
return next(); return next();
} }
var path = req.path.replace(/^\/api/, '') const path = req.path.replace(/^\/api/, '')
.replace('uid', 'user') .replace('uid', 'user')
.replace(uid, function () { return userslug; }); .replace(uid, function () { return userslug; });
controllers.helpers.redirect(res, path); controllers.helpers.redirect(res, path);
},
], next);
}; };
middleware.redirectMeToUserslug = function redirectMeToUserslug(req, res, next) { middleware.redirectMeToUserslug = async function redirectMeToUserslug(req, res) {
var uid = req.uid; const userslug = await user.getUserField(req.uid, 'userslug');
async.waterfall([
function (next) {
user.getUserField(uid, 'userslug', next);
},
function (userslug) {
if (!userslug) { if (!userslug) {
return controllers.helpers.notAllowed(req, res); return controllers.helpers.notAllowed(req, res);
} }
var path = req.path.replace(/^(\/api)?\/me/, '/user/' + userslug); const path = req.path.replace(/^(\/api)?\/me/, '/user/' + userslug);
controllers.helpers.redirect(res, path); controllers.helpers.redirect(res, path);
},
], next);
}; };
middleware.isAdmin = async function isAdmin(req, res, next) { middleware.isAdmin = async function isAdmin(req, res, next) {

Loading…
Cancel
Save