refactor: async/await controllers

v1.18.x
Barış Soner Uşaklı 6 years ago
parent a121098513
commit 2c4f04462f

@ -1,16 +1,15 @@
'use strict'; 'use strict';
var async = require('async'); const nconf = require('nconf');
var nconf = require('nconf'); const validator = require('validator');
var validator = require('validator');
var meta = require('../meta'); const meta = require('../meta');
var user = require('../user'); const user = require('../user');
var plugins = require('../plugins'); const plugins = require('../plugins');
var privileges = require('../privileges'); const privileges = require('../privileges');
var helpers = require('./helpers'); const helpers = require('./helpers');
var Controllers = module.exports; const Controllers = module.exports;
Controllers.ping = require('./ping'); Controllers.ping = require('./ping');
Controllers.home = require('./home'); Controllers.home = require('./home');
@ -154,94 +153,77 @@ Controllers.login = function (req, res, next) {
}); });
}; };
Controllers.register = function (req, res, next) { Controllers.register = async function (req, res, next) {
var registrationType = meta.config.registrationType || 'normal'; const registrationType = meta.config.registrationType || 'normal';
if (registrationType === 'disabled') { if (registrationType === 'disabled') {
return setImmediate(next); return setImmediate(next);
} }
var errorText; let errorText;
if (req.query.error === 'csrf-invalid') { if (req.query.error === 'csrf-invalid') {
errorText = '[[error:csrf-invalid]]'; errorText = '[[error:csrf-invalid]]';
} }
try {
async.waterfall([ if (registrationType === 'invite-only' || registrationType === 'admin-invite-only') {
function (next) { await user.verifyInvitation(req.query);
if (registrationType === 'invite-only' || registrationType === 'admin-invite-only') { }
user.verifyInvitation(req.query, next); const termsOfUse = await plugins.fireHook('filter:parse.post', {
} else { postData: {
next(); content: meta.config.termsOfUse || '',
} },
}, });
function (next) { const loginStrategies = require('../routes/authentication').getLoginStrategies();
plugins.fireHook('filter:parse.post', { res.render('register', {
postData: { 'register_window:spansize': loginStrategies.length ? 'col-md-6' : 'col-md-12',
content: meta.config.termsOfUse || '', alternate_logins: !!loginStrategies.length,
}, authentication: loginStrategies,
}, next);
}, minimumUsernameLength: meta.config.minimumUsernameLength,
function (termsOfUse) { maximumUsernameLength: meta.config.maximumUsernameLength,
var loginStrategies = require('../routes/authentication').getLoginStrategies(); minimumPasswordLength: meta.config.minimumPasswordLength,
var data = { minimumPasswordStrength: meta.config.minimumPasswordStrength,
'register_window:spansize': loginStrategies.length ? 'col-md-6' : 'col-md-12', termsOfUse: termsOfUse.postData.content,
alternate_logins: !!loginStrategies.length, breadcrumbs: helpers.buildBreadcrumbs([{
};
data.authentication = loginStrategies;
data.minimumUsernameLength = meta.config.minimumUsernameLength;
data.maximumUsernameLength = meta.config.maximumUsernameLength;
data.minimumPasswordLength = meta.config.minimumPasswordLength;
data.minimumPasswordStrength = meta.config.minimumPasswordStrength;
data.termsOfUse = termsOfUse.postData.content;
data.breadcrumbs = helpers.buildBreadcrumbs([{
text: '[[register:register]]', text: '[[register:register]]',
}]); }]),
data.regFormEntry = []; regFormEntry: [],
data.error = req.flash('error')[0] || errorText; error: req.flash('error')[0] || errorText,
data.title = '[[pages:register]]'; title: '[[pages:register]]',
});
res.render('register', data); } catch (err) {
}, next(err);
], next); }
}; };
Controllers.registerInterstitial = function (req, res, next) { Controllers.registerInterstitial = async function (req, res, next) {
if (!req.session.hasOwnProperty('registration')) { if (!req.session.hasOwnProperty('registration')) {
return res.redirect(nconf.get('relative_path') + '/register'); return res.redirect(nconf.get('relative_path') + '/register');
} }
try {
const data = await plugins.fireHook('filter:register.interstitial', {
userData: req.session.registration,
interstitials: [],
});
async.waterfall([ if (!data.interstitials.length) {
function (next) { // No interstitials, redirect to home
plugins.fireHook('filter:register.interstitial', { const returnTo = req.session.returnTo || req.session.registration.returnTo;
userData: req.session.registration, delete req.session.registration;
interstitials: [], return helpers.redirect(res, returnTo || '/');
}, next); }
},
function (data, next) {
if (!data.interstitials.length) {
// No interstitials, redirect to home
const returnTo = req.session.returnTo || req.session.registration.returnTo;
delete req.session.registration;
return helpers.redirect(res, returnTo || '/');
}
var renders = data.interstitials.map(function (interstitial) {
return async.apply(req.app.render.bind(req.app), interstitial.template, interstitial.data || {});
});
const renders = data.interstitials.map(interstitial => req.app.renderAsync(interstitial.template, interstitial.data || {}));
const sections = await Promise.all(renders);
async.parallel(renders, next); res.render('registerComplete', {
}, title: '[[pages:registration-complete]]',
function (sections) { errors: req.flash('errors'),
var errors = req.flash('errors'); sections: sections,
res.render('registerComplete', { });
title: '[[pages:registration-complete]]', } catch (err) {
errors: errors, next(err);
sections: sections, }
});
},
], next);
}; };
Controllers.confirmEmail = function (req, res) { Controllers.confirmEmail = function (req, res) {

@ -1,159 +1,142 @@
'use strict'; 'use strict';
var async = require('async'); const user = require('../user');
const categories = require('../categories');
var user = require('../user'); const flags = require('../flags');
var categories = require('../categories'); const analytics = require('../analytics');
var flags = require('../flags'); const plugins = require('../plugins');
var analytics = require('../analytics'); const pagination = require('../pagination');
var plugins = require('../plugins'); const utils = require('../utils');
var pagination = require('../pagination');
const adminPostQueueController = require('./admin/postqueue');
var adminPostQueueController = require('./admin/postqueue'); const modsController = module.exports;
var modsController = module.exports;
modsController.flags = {}; modsController.flags = {};
modsController.flags.list = function (req, res, next) { modsController.flags.list = async function (req, res, next) {
var filters; let validFilters = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick', 'page', 'perPage'];
var hasFilter;
var validFilters = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick', 'page', 'perPage'];
// Reset filters if explicitly requested // Reset filters if explicitly requested
if (parseInt(req.query.reset, 10) === 1) { if (parseInt(req.query.reset, 10) === 1) {
delete req.session.flags_filters; delete req.session.flags_filters;
} }
async.waterfall([ const [isAdminOrGlobalMod, moderatedCids, data] = await Promise.all([
function (next) { user.isAdminOrGlobalMod(req.uid),
async.parallel({ user.getModeratedCids(req.uid),
isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), plugins.fireHook('filter:flags.validateFilters', { filters: validFilters }),
moderatedCids: async.apply(user.getModeratedCids, req.uid), ]);
validFilters: async.apply(plugins.fireHook, 'filter:flags.validateFilters', { filters: validFilters }),
}, next);
},
function (results, next) {
if (!(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) {
return next(new Error('[[error:no-privileges]]'));
}
if (!results.isAdminOrGlobalMod && results.moderatedCids.length) { if (!(isAdminOrGlobalMod || !!moderatedCids.length)) {
res.locals.cids = results.moderatedCids; return next(new Error('[[error:no-privileges]]'));
} }
validFilters = results.validFilters.filters; if (!isAdminOrGlobalMod && moderatedCids.length) {
res.locals.cids = moderatedCids;
}
// Parse query string params for filters validFilters = data.filters;
hasFilter = false;
filters = validFilters.reduce(function (memo, cur) { // Parse query string params for filters
if (req.query.hasOwnProperty(cur)) { let filters = validFilters.reduce(function (memo, cur) {
memo[cur] = req.query[cur]; if (req.query.hasOwnProperty(cur)) {
} memo[cur] = req.query[cur];
}
return memo; return memo;
}, {}); }, {});
hasFilter = !!Object.keys(filters).length; let hasFilter = !!Object.keys(filters).length;
if (!hasFilter && req.session.hasOwnProperty('flags_filters')) { if (!hasFilter && req.session.hasOwnProperty('flags_filters')) {
// Load filters from session object // Load filters from session object
filters = req.session.flags_filters; filters = req.session.flags_filters;
hasFilter = true; hasFilter = true;
} }
if (res.locals.cids) { if (res.locals.cids) {
if (!filters.cid) { if (!filters.cid) {
// If mod and no cid filter, add filter for their modded categories // If mod and no cid filter, add filter for their modded categories
filters.cid = res.locals.cids; filters.cid = res.locals.cids;
} else if (Array.isArray(filters.cid)) { } else if (Array.isArray(filters.cid)) {
// Remove cids they do not moderate // Remove cids they do not moderate
filters.cid = filters.cid.filter(function (cid) { filters.cid = filters.cid.filter(cid => res.locals.cids.includes(String(cid)));
return res.locals.cids.includes(String(cid)); } else if (!res.locals.cids.includes(String(filters.cid))) {
}); filters.cid = res.locals.cids;
} else if (!res.locals.cids.includes(String(filters.cid))) { hasFilter = false;
filters.cid = res.locals.cids; }
hasFilter = false; }
}
}
// Pagination doesn't count as a filter // Pagination doesn't count as a filter
if (Object.keys(filters).length === 2 && filters.hasOwnProperty('page') && filters.hasOwnProperty('perPage')) { if (Object.keys(filters).length === 2 && filters.hasOwnProperty('page') && filters.hasOwnProperty('perPage')) {
hasFilter = false; hasFilter = false;
} }
// Save filters into session unless removed // Save filters into session unless removed
req.session.flags_filters = filters; req.session.flags_filters = filters;
async.parallel({ const [flagsData, analyticsData, categoriesData] = await Promise.all([
flags: async.apply(flags.list, filters, req.uid), flags.list(filters, req.uid),
analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30), analytics.getDailyStatsForSet('analytics:flags', Date.now(), 30),
categories: async.apply(categories.buildForSelect, req.uid, 'read'), categories.buildForSelect(req.uid, 'read'),
}, next); ]);
},
function (data) { res.render('flags/list', {
data.categories = filterCategories(res.locals.cids, data.categories); flags: flagsData.flags,
analytics: analyticsData,
res.render('flags/list', { categories: filterCategories(res.locals.cids, categoriesData),
flags: data.flags.flags, hasFilter: hasFilter,
analytics: data.analytics, filters: filters,
categories: data.categories, title: '[[pages:flags]]',
hasFilter: hasFilter, pagination: pagination.create(flagsData.page, flagsData.pageCount, req.query),
filters: filters, });
title: '[[pages:flags]]',
pagination: pagination.create(data.flags.page, data.flags.pageCount, req.query),
});
},
], next);
}; };
modsController.flags.detail = function (req, res, next) { modsController.flags.detail = async function (req, res, next) {
async.parallel({ const results = await utils.promiseParallel({
isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), isAdminOrGlobalMod: user.isAdminOrGlobalMod(req.uid),
moderatedCids: async.apply(user.getModeratedCids, req.uid), moderatedCids: user.getModeratedCids(req.uid),
flagData: async.apply(flags.get, req.params.flagId), flagData: flags.get(req.params.flagId),
assignees: async.apply(user.getAdminsandGlobalModsandModerators), assignees: user.getAdminsandGlobalModsandModerators(),
categories: async.apply(categories.buildForSelect, req.uid, 'read'), categories: categories.buildForSelect(req.uid, 'read'),
}, function (err, results) { });
if (err || !results.flagData) {
return next(err || new Error('[[error:invalid-data]]'));
} else if (!(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) {
return next(new Error('[[error:no-privileges]]'));
}
if (!results.isAdminOrGlobalMod && results.moderatedCids.length) { if (!results.flagData) {
res.locals.cids = results.moderatedCids; return next(new Error('[[error:invalid-data]]'));
} } else if (!(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) {
return next(new Error('[[error:no-privileges]]'));
}
results.categories = filterCategories(res.locals.cids, results.categories); if (!results.isAdminOrGlobalMod && results.moderatedCids.length) {
res.locals.cids = results.moderatedCids;
}
if (results.flagData.type === 'user') { results.categories = filterCategories(res.locals.cids, results.categories);
results.flagData.type_path = 'uid';
} else if (results.flagData.type === 'post') {
results.flagData.type_path = 'post';
}
res.render('flags/detail', Object.assign(results.flagData, { if (results.flagData.type === 'user') {
assignees: results.assignees, results.flagData.type_path = 'uid';
type_bool: ['post', 'user', 'empty'].reduce(function (memo, cur) { } else if (results.flagData.type === 'post') {
if (cur !== 'empty') { results.flagData.type_path = 'post';
memo[cur] = results.flagData.type === cur && (!results.flagData.target || !!Object.keys(results.flagData.target).length); }
} else {
memo[cur] = !Object.keys(results.flagData.target).length; res.render('flags/detail', Object.assign(results.flagData, {
} assignees: results.assignees,
type_bool: ['post', 'user', 'empty'].reduce(function (memo, cur) {
return memo; if (cur !== 'empty') {
}, {}), memo[cur] = results.flagData.type === cur && (!results.flagData.target || !!Object.keys(results.flagData.target).length);
title: '[[pages:flag-details, ' + req.params.flagId + ']]', } else {
categories: results.categories, memo[cur] = !Object.keys(results.flagData.target).length;
})); }
});
return memo;
}, {}),
title: '[[pages:flag-details, ' + req.params.flagId + ']]',
categories: results.categories,
}));
}; };
function filterCategories(moderatedCids, categories) { function filterCategories(moderatedCids, categories) {
// If cids is populated, then slim down the categories list // If cids is populated, then slim down the categories list
if (moderatedCids) { if (moderatedCids) {
categories = categories.filter(function (category) { categories = categories.filter(category => moderatedCids.includes(String(category.cid)));
return moderatedCids.includes(String(category.cid));
});
} }
return categories.reduce(function (memo, cur) { return categories.reduce(function (memo, cur) {
@ -171,16 +154,10 @@ function filterCategories(moderatedCids, categories) {
}, {}); }, {});
} }
modsController.postQueue = function (req, res, next) { modsController.postQueue = async function (req, res, next) {
async.waterfall([ const isPrivileged = await user.isPrivileged(req.uid);
function (next) { if (!isPrivileged) {
user.isPrivileged(req.uid, next); return next();
}, }
function (isPrivileged, next) { await adminPostQueueController.get(req, res, next);
if (!isPrivileged) {
return next();
}
adminPostQueueController.get(req, res, next);
},
], next);
}; };

@ -1,10 +1,10 @@
'use strict'; 'use strict';
var xml = require('xml'); const xml = require('xml');
var nconf = require('nconf'); const nconf = require('nconf');
var plugins = require('../plugins'); const plugins = require('../plugins');
var meta = require('../meta'); const meta = require('../meta');
module.exports.handle = function (req, res, next) { module.exports.handle = function (req, res, next) {
if (plugins.hasListeners('filter:search.query')) { if (plugins.hasListeners('filter:search.query')) {

@ -1,28 +1,18 @@
'use strict'; 'use strict';
var async = require('async'); const sitemap = require('../sitemap');
const meta = require('../meta');
var sitemap = require('../sitemap'); const sitemapController = module.exports;
var meta = require('../meta');
var sitemapController = module.exports; sitemapController.render = async function (req, res, next) {
sitemapController.render = function (req, res, next) {
if (meta.config['feeds:disableSitemap']) { if (meta.config['feeds:disableSitemap']) {
return setImmediate(next); return setImmediate(next);
} }
async.waterfall([ const tplData = await sitemap.render();
function (next) { const xml = await req.app.renderAsync('sitemap', tplData);
sitemap.render(next); res.header('Content-Type', 'application/xml');
}, res.send(xml);
function (tplData, next) {
req.app.render('sitemap', tplData, next);
},
function (xml) {
res.header('Content-Type', 'application/xml');
res.send(xml);
},
], next);
}; };
sitemapController.getPages = function (req, res, next) { sitemapController.getPages = function (req, res, next) {
@ -34,26 +24,19 @@ sitemapController.getCategories = function (req, res, next) {
}; };
sitemapController.getTopicPage = function (req, res, next) { sitemapController.getTopicPage = function (req, res, next) {
sendSitemap(function (callback) { sendSitemap(async function () {
sitemap.getTopicPage(parseInt(req.params[0], 10), callback); return await sitemap.getTopicPage(parseInt(req.params[0], 10));
}, res, next); }, res, next);
}; };
function sendSitemap(method, res, callback) { async function sendSitemap(method, res, callback) {
if (meta.config['feeds:disableSitemap']) { if (meta.config['feeds:disableSitemap']) {
return setImmediate(callback); return setImmediate(callback);
} }
async.waterfall([ const xml = await method();
function (next) { if (!xml) {
method(next); return callback();
}, }
function (xml) { res.header('Content-Type', 'application/xml');
if (!xml) { res.send(xml);
return callback();
}
res.header('Content-Type', 'application/xml');
res.send(xml);
},
], callback);
} }

@ -1,113 +1,91 @@
'use strict'; 'use strict';
var async = require('async'); const nconf = require('nconf');
var nconf = require('nconf'); const querystring = require('querystring');
var querystring = require('querystring');
var meta = require('../meta'); const meta = require('../meta');
var pagination = require('../pagination'); const pagination = require('../pagination');
var user = require('../user'); const user = require('../user');
var categories = require('../categories'); const categories = require('../categories');
var topics = require('../topics'); const topics = require('../topics');
var plugins = require('../plugins'); const plugins = require('../plugins');
var helpers = require('./helpers'); const helpers = require('./helpers');
var unreadController = module.exports; const unreadController = module.exports;
unreadController.get = function (req, res, next) { unreadController.get = async function (req, res, next) {
var page = parseInt(req.query.page, 10) || 1; const cid = req.query.cid;
var results; const filter = req.query.filter || '';
var cid = req.query.cid;
var filter = req.query.filter || '';
var settings;
async.waterfall([ const filterData = await plugins.fireHook('filter:unread.getValidFilters', { filters: { ...helpers.validFilters } });
function (next) { if (!filterData.filters[filter]) {
plugins.fireHook('filter:unread.getValidFilters', { filters: { ...helpers.validFilters } }, next); return next();
}, }
function (data, _next) { const [watchedCategories, userSettings] = await Promise.all([
if (!data.filters[filter]) { getWatchedCategories(req.uid, cid, filter),
return next(); user.getSettings(req.uid),
} ]);
async.parallel({ const page = parseInt(req.query.page, 10) || 1;
watchedCategories: function (next) { const start = Math.max(0, (page - 1) * userSettings.topicsPerPage);
if (plugins.hasListeners('filter:unread.categories')) { const stop = start + userSettings.topicsPerPage - 1;
plugins.fireHook('filter:unread.categories', { uid: req.uid, cid: cid }, next); const cutoff = req.session.unreadCutoff ? req.session.unreadCutoff : topics.unreadCutoff();
} else { const data = await topics.getUnreadTopics({
const states = [categories.watchStates.watching]; cid: cid,
if (filter === 'watched') { uid: req.uid,
states.push(categories.watchStates.notwatching, categories.watchStates.ignoring); start: start,
} stop: stop,
helpers.getCategoriesByStates(req.uid, cid, states, next); filter: filter,
} cutoff: cutoff,
}, });
settings: function (next) {
user.getSettings(req.uid, next);
},
}, _next);
},
function (_results, next) {
results = _results;
settings = results.settings;
var start = Math.max(0, (page - 1) * settings.topicsPerPage);
var stop = start + settings.topicsPerPage - 1;
var cutoff = req.session.unreadCutoff ? req.session.unreadCutoff : topics.unreadCutoff();
topics.getUnreadTopics({
cid: cid,
uid: req.uid,
start: start,
stop: stop,
filter: filter,
cutoff: cutoff,
}, next);
},
function (data) {
data.title = meta.config.homePageTitle || '[[pages:home]]';
data.pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage));
data.pagination = pagination.create(page, data.pageCount, req.query);
if (settings.usePagination && (page < 1 || page > data.pageCount)) { data.title = meta.config.homePageTitle || '[[pages:home]]';
req.query.page = Math.max(1, Math.min(data.pageCount, page)); data.pageCount = Math.max(1, Math.ceil(data.topicCount / userSettings.topicsPerPage));
return helpers.redirect(res, '/unread?' + querystring.stringify(req.query)); data.pagination = pagination.create(page, data.pageCount, req.query);
}
data.categories = results.watchedCategories.categories; if (userSettings.usePagination && (page < 1 || page > data.pageCount)) {
data.allCategoriesUrl = 'unread' + helpers.buildQueryString('', filter, ''); req.query.page = Math.max(1, Math.min(data.pageCount, page));
data.selectedCategory = results.watchedCategories.selectedCategory; return helpers.redirect(res, '/unread?' + querystring.stringify(req.query));
data.selectedCids = results.watchedCategories.selectedCids; }
if (req.originalUrl.startsWith(nconf.get('relative_path') + '/api/unread') || req.originalUrl.startsWith(nconf.get('relative_path') + '/unread')) {
data.title = '[[pages:unread]]';
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[unread:title]]' }]);
}
data.filters = helpers.buildFilters('unread', filter, req.query); data.categories = watchedCategories.categories;
data.allCategoriesUrl = 'unread' + helpers.buildQueryString('', filter, '');
data.selectedCategory = watchedCategories.selectedCategory;
data.selectedCids = watchedCategories.selectedCids;
if (req.originalUrl.startsWith(nconf.get('relative_path') + '/api/unread') || req.originalUrl.startsWith(nconf.get('relative_path') + '/unread')) {
data.title = '[[pages:unread]]';
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[unread:title]]' }]);
}
data.selectedFilter = data.filters.find(function (filter) { data.filters = helpers.buildFilters('unread', filter, req.query);
return filter && filter.selected;
});
res.render('unread', data); data.selectedFilter = data.filters.find(filter => filter && filter.selected);
},
], next); res.render('unread', data);
}; };
unreadController.unreadTotal = function (req, res, next) { async function getWatchedCategories(uid, cid, filter) {
var filter = req.query.filter || ''; if (plugins.hasListeners('filter:unread.categories')) {
return await plugins.fireHook('filter:unread.categories', { uid: uid, cid: cid });
}
const states = [categories.watchStates.watching];
if (filter === 'watched') {
states.push(categories.watchStates.notwatching, categories.watchStates.ignoring);
}
return await helpers.getCategoriesByStates(uid, cid, states);
}
async.waterfall([ unreadController.unreadTotal = async function (req, res, next) {
function (next) { const filter = req.query.filter || '';
plugins.fireHook('filter:unread.getValidFilters', { filters: { ...helpers.validFilters } }, next); try {
}, const data = await plugins.fireHook('filter:unread.getValidFilters', { filters: { ...helpers.validFilters } });
function (data, _next) { if (!data.filters[filter]) {
if (!data.filters[filter]) { return next();
return next(); }
} const unreadCount = await topics.getTotalUnread(req.uid, filter);
topics.getTotalUnread(req.uid, filter, _next); res.json(unreadCount);
}, } catch (err) {
function (data) { next(err);
res.json(data); }
},
], next);
}; };

@ -12,10 +12,9 @@ var meta = require('./meta');
var plugins = require('./plugins'); var plugins = require('./plugins');
var utils = require('./utils'); var utils = require('./utils');
var sitemap = { var sitemap = module.exports;
maps: { sitemap.maps = {
topics: [], topics: [],
},
}; };
sitemap.render = function (callback) { sitemap.render = function (callback) {
@ -174,4 +173,4 @@ sitemap.clearCache = function () {
} }
}; };
module.exports = sitemap; require('./promisify')(sitemap);

@ -115,6 +115,8 @@ function setupExpressApp(app) {
const relativePath = nconf.get('relative_path'); const relativePath = nconf.get('relative_path');
const viewsDir = nconf.get('views_dir'); const viewsDir = nconf.get('views_dir');
app.renderAsync = util.promisify((tpl, data, callback) => app.render(tpl, data, callback));
app.engine('tpl', function (filepath, data, next) { app.engine('tpl', function (filepath, data, next) {
filepath = filepath.replace(/\.tpl$/, '.js'); filepath = filepath.replace(/\.tpl$/, '.js');

Loading…
Cancel
Save