refactor: async/await middleware

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

@ -1,44 +1,38 @@
'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([
function (next) {
user.isAdministrator(req.uid, next);
},
function (isAdmin, next) {
if (isAdmin) {
return callback();
}
res.status(meta.config.maintenanceModeStatus);
data = {
site_title: meta.config.title || 'NodeBB',
message: meta.config.maintenanceModeMessage,
};
if (res.locals.isAPI) { const isAdmin = await user.isAdministrator(req.uid);
return res.json(data); if (isAdmin) {
} return setImmediate(next);
}
res.status(meta.config.maintenanceModeStatus);
middleware.buildHeader(req, res, next); const data = {
}, site_title: meta.config.title || 'NodeBB',
function () { message: meta.config.maintenanceModeMessage,
res.render('503', data); };
},
], callback); if (res.locals.isAPI) {
return res.json(data);
}
const buildHeaderAsync = util.promisify(middleware.buildHeader);
await buildHeaderAsync(req, res);
res.render('503', data);
}; };
}; };

@ -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,118 +1,89 @@
'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; options.loggedIn = req.uid > 0;
options.relative_path = nconf.get('relative_path');
options.template = { name: template, [template]: true };
options.url = (req.baseUrl + req.path.replace(/^\/api/, ''));
options.bodyClass = buildBodyClass(req, res, options);
const buildResult = await plugins.fireHook('filter:' + template + '.build', { req: req, res: res, templateData: options });
const templateToRender = buildResult.templateData.templateToRender || template;
const renderResult = await plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: buildResult.templateData });
options = renderResult.templateData;
options._header = {
tags: await meta.tags.parse(req, renderResult, res.locals.metaTags, res.locals.linkTags),
};
options.widgets = await widgets.render(req.uid, {
template: template + '.tpl',
url: options.url,
templateData: options,
req: req,
res: res,
});
res.locals.template = template;
options._locals = undefined;
if (res.locals.isAPI) {
if (req.route && req.route.path === '/api/') {
options.title = '[[pages:home]]';
}
req.app.set('json spaces', global.env === 'development' || req.query.pretty ? 4 : 0);
return res.json(options);
} }
const ajaxifyData = JSON.stringify(options).replace(/<\//g, '<\\/');
const renderAsync = util.promisify((templateToRender, options, next) => render.call(self, templateToRender, options, next));
const results = await utils.promiseParallel({
header: renderHeaderFooterAsync('renderHeader', req, res, options),
content: renderAsync(templateToRender, options),
footer: renderHeaderFooterAsync('renderFooter', req, res, options),
});
const str = results.header +
(res.locals.postHeader || '') +
results.content + '<script id="ajaxify-data"></script>' +
(res.locals.preFooter || '') +
results.footer;
var ajaxifyData; let translated = await translate(str, req, res);
var templateToRender; translated = translated.replace('<script id="ajaxify-data"></script>', function () {
async.waterfall([ return '<script id="ajaxify-data" type="application/json">' + ajaxifyData + '</script>';
function (next) { });
options.loggedIn = req.uid > 0;
options.relative_path = nconf.get('relative_path'); if (typeof fn !== 'function') {
options.template = { name: template }; self.send(translated);
options.template[template] = true; } else {
options.url = (req.baseUrl + req.path.replace(/^\/api/, '')); fn(null, translated);
options.bodyClass = buildBodyClass(req, res, options); }
plugins.fireHook('filter:' + template + '.build', { req: req, res: res, templateData: options }, next);
},
function (data, next) {
templateToRender = data.templateData.templateToRender || template;
plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: data.templateData }, next);
},
function parseTags(data, next) {
meta.tags.parse(req, data, res.locals.metaTags, res.locals.linkTags, function (err, tags) {
options._header = {
tags: tags,
};
next(err, data);
});
},
function (data, next) {
options = data.templateData;
widgets.render(req.uid, {
template: template + '.tpl',
url: options.url,
templateData: options,
req: req,
res: res,
}, next);
},
function (data, next) {
options.widgets = data;
res.locals.template = template;
options._locals = undefined;
if (res.locals.isAPI) {
if (req.route && req.route.path === '/api/') {
options.title = '[[pages:home]]';
}
req.app.set('json spaces', global.env === 'development' || req.query.pretty ? 4 : 0);
return res.json(options);
}
ajaxifyData = JSON.stringify(options).replace(/<\//g, '<\\/');
async.parallel({
header: function (next) {
renderHeaderFooter('renderHeader', req, res, options, next);
},
content: function (next) {
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 || '') +
results.content + '<script id="ajaxify-data"></script>' +
(res.locals.preFooter || '') +
results.footer;
translate(str, req, res, next);
},
function (translated, next) {
translated = translated.replace('<script id="ajaxify-data"></script>', function () {
return '<script id="ajaxify-data" type="application/json">' + ajaxifyData + '</script>';
});
next(null, translated);
},
], fn);
}; };
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([ if (!req.loggedIn) {
function (next) { return controllers.helpers.notAllowed(req, res);
if (!req.loggedIn) { }
return setImmediate(next, null, false); if (req.uid === parseInt(res.locals.uid, 10)) {
} return setImmediate(next);
}
if (req.uid === parseInt(res.locals.uid, 10)) { const allowed = await method(req.uid);
return setImmediate(next, null, true); if (!allowed) {
} return controllers.helpers.notAllowed(req, res);
}
method(req.uid, next);
},
function (allowed, next) {
if (!allowed) {
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);
}, if (allowed) {
function (next) { return next();
user.getUidByUserslug(req.params.userslug, next); }
},
function (uid, next) {
privileges.users.canEdit(req.uid, uid, next);
},
function (allowed, next) {
if (allowed) {
return next(null, allowed);
}
// For the account/info page only, allow plain moderators through if (/user\/.+\/info$/.test(req.path)) {
if (/user\/.+\/info$/.test(req.path)) { allowed = await privileges.global.can('view:users:info', req.uid);
privileges.global.can('view:users:info', req.uid, next); }
} else { if (allowed) {
next(null, false); return next();
} }
}, controllers.helpers.notAllowed(req, res);
function (allowed) {
if (allowed) {
return next();
}
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([ controllers.helpers.redirect(res, '/user/' + userslug);
function (next) {
user.getUserField(req.uid, 'userslug', next);
},
function (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) { if (!userslug) {
user.getUserField(uid, 'userslug', next); return next();
}, }
function (userslug) { const path = req.path.replace(/^\/api/, '')
if (!userslug) { .replace('uid', 'user')
return next(); .replace(uid, function () { return userslug; });
} controllers.helpers.redirect(res, path);
var path = req.path.replace(/^\/api/, '')
.replace('uid', 'user')
.replace(uid, function () { return userslug; });
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([ if (!userslug) {
function (next) { return controllers.helpers.notAllowed(req, res);
user.getUserField(uid, 'userslug', next); }
}, const path = req.path.replace(/^(\/api)?\/me/, '/user/' + userslug);
function (userslug) { controllers.helpers.redirect(res, path);
if (!userslug) {
return controllers.helpers.notAllowed(req, res);
}
var path = req.path.replace(/^(\/api)?\/me/, '/user/' + userslug);
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