performance improvements (#8795)

* perf: nconf/winston/render

cache nconf.get calls
modify middleware.pageView to call next earlier
don't call winston.verbose on every hook see https://github.com/winstonjs/winston/issues/1669
translate header/footer separately and cache results for guests

* fix: copy paste fail

* refactor: style and fire hook only log in dev mode

* fix: cache key, header changes based on template

* perf: change replace

* fix: add missing await

* perf: category

* perf: lodash clone

* perf: remove escapeRegexChars
This commit is contained in:
Barış Soner Uşaklı
2020-10-26 10:43:18 -04:00
committed by GitHub
parent 822c13f199
commit a05905f196
34 changed files with 230 additions and 193 deletions

View File

@@ -1,6 +1,5 @@
'use strict';
const util = require('util');
const nconf = require('nconf');
const validator = require('validator');
const winston = require('winston');
@@ -11,6 +10,9 @@ const translator = require('../translator');
const widgets = require('../widgets');
const utils = require('../utils');
const slugify = require('../slugify');
const cache = require('../cache');
const relative_path = nconf.get('relative_path');
module.exports = function (middleware) {
middleware.processRender = function processRender(req, res, next) {
@@ -27,7 +29,7 @@ module.exports = function (middleware) {
}
options.loggedIn = req.uid > 0;
options.relative_path = nconf.get('relative_path');
options.relative_path = relative_path;
options.template = { name: template, [template]: true };
options.url = (req.baseUrl + req.path.replace(/^\/api/, ''));
options.bodyClass = buildBodyClass(req, res, options);
@@ -57,52 +59,75 @@ module.exports = function (middleware) {
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: renderHeaderFooter('renderHeader', req, res, options),
content: renderAsync(templateToRender, options),
content: renderContent(render, templateToRender, req, res, options),
footer: renderHeaderFooter('renderFooter', req, res, options),
});
const str = results.header +
(res.locals.postHeader || '') +
results.content + '<script id="ajaxify-data"></script>' +
results.content +
'<script id="ajaxify-data" type="application/json">' +
JSON.stringify(options).replace(/<\//g, '<\\/') +
'</script>' +
(res.locals.preFooter || '') +
results.footer;
let translated = await translate(str, req, res);
translated = translated.replace('<script id="ajaxify-data"></script>', function () {
return '<script id="ajaxify-data" type="application/json">' + ajaxifyData + '</script>';
});
if (typeof fn !== 'function') {
self.send(translated);
self.send(str);
} else {
fn(null, translated);
fn(null, str);
}
};
next();
};
async function renderHeaderFooter(method, req, res, options) {
if (res.locals.renderHeader) {
return await middleware[method](req, res, options);
} else if (res.locals.renderAdminHeader) {
return await middleware.admin[method](req, res, options);
}
return '';
async function renderContent(render, tpl, req, res, options) {
return new Promise(function (resolve, reject) {
render.call(res, tpl, options, async function (err, str) {
if (err) reject(err);
else resolve(await translate(str, getLang(req, res)));
});
});
}
async function translate(str, req, res) {
async function renderHeaderFooter(method, req, res, options) {
let str = '';
const lang = getLang(req, res);
if (req.uid === 0 && res.locals.renderHeader) {
str = cache.get('render' + options.template.name + lang + method);
if (str) {
return str;
}
}
if (!str) {
if (res.locals.renderHeader) {
str = await middleware[method](req, res, options);
} else if (res.locals.renderAdminHeader) {
str = await middleware.admin[method](req, res, options);
} else {
str = '';
}
}
const translated = await translate(str, lang);
if (req.uid === 0 && res.locals.renderHeader) {
cache.set('render' + options.template.name + lang + method, translated, 300000);
}
return translated;
}
function getLang(req, res) {
let language = (res.locals.config && res.locals.config.userLang) || 'en-GB';
if (res.locals.renderAdminHeader) {
language = (res.locals.config && res.locals.config.acpLang) || 'en-GB';
}
language = req.query.lang ? validator.escape(String(req.query.lang)) : language;
return req.query.lang ? validator.escape(String(req.query.lang)) : language;
}
async function translate(str, language) {
const translated = await translator.translate(str, language);
return translator.unescape(translated);
}