From c9250a01a2c96adc608f7c7524079f01f8ad4f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 14 Aug 2019 16:27:58 -0400 Subject: [PATCH] refactor: async/await, remove dupe code for homepage routes --- src/controllers/accounts/settings.js | 47 +------- src/controllers/admin/errors.js | 45 ++++--- src/controllers/admin/events.js | 73 +++++------- src/controllers/admin/groups.js | 125 ++++++++------------ src/controllers/admin/homepage.js | 58 +-------- src/controllers/admin/hooks.js | 8 +- src/controllers/admin/info.js | 81 ++++++------- src/controllers/admin/languages.js | 32 ++--- src/controllers/admin/logger.js | 2 +- src/controllers/admin/logs.js | 32 +++-- src/controllers/admin/navigation.js | 56 ++++----- src/controllers/admin/plugins.js | 111 ++++++++---------- src/controllers/admin/postqueue.js | 169 +++++++++++---------------- src/controllers/admin/privileges.js | 98 +++++++--------- src/controllers/helpers.js | 42 +++++++ src/meta/logs.js | 20 ++-- src/socket.io/admin/rooms.js | 2 + 17 files changed, 411 insertions(+), 590 deletions(-) diff --git a/src/controllers/accounts/settings.js b/src/controllers/accounts/settings.js index 1f0645fd77..002cbf55e1 100644 --- a/src/controllers/accounts/settings.js +++ b/src/controllers/accounts/settings.js @@ -10,8 +10,6 @@ const user = require('../../user'); const languages = require('../../languages'); const meta = require('../../meta'); const plugins = require('../../plugins'); -const privileges = require('../../privileges'); -const categories = require('../../categories'); const notifications = require('../../notifications'); const db = require('../../database'); const helpers = require('../helpers'); @@ -241,49 +239,12 @@ async function getNotificationSettings(userData) { } async function getHomePageRoutes(userData) { - let cids = await categories.getAllCidsFromSet('cid:0:children'); - cids = await privileges.categories.filterCids('find', cids, userData.uid); - let categoryData = await categories.getCategoriesFields(cids, ['name', 'slug']); - - categoryData = categoryData.map(function (category) { - return { - route: 'category/' + category.slug, - name: 'Category: ' + category.name, - }; - }); - - const data = await plugins.fireHook('filter:homepage.get', { routes: [ - { - route: 'categories', - name: 'Categories', - }, - { - route: 'unread', - name: 'Unread', - }, - { - route: 'recent', - name: 'Recent', - }, - { - route: 'top', - name: 'Top', - }, - { - route: 'popular', - name: 'Popular', - }, - ].concat(categoryData, [ - { - route: 'custom', - name: 'Custom', - }, - ]) }); + let routes = await helpers.getHomePageRoutes(userData.uid); // Set selected for each route var customIdx; var hasSelected = false; - data.routes = data.routes.map(function (route, idx) { + routes = routes.map(function (route, idx) { if (route.route === userData.settings.homePageRoute) { route.selected = true; hasSelected = true; @@ -299,8 +260,8 @@ async function getHomePageRoutes(userData) { }); if (!hasSelected && customIdx && userData.settings.homePageRoute !== 'none') { - data.routes[customIdx].selected = true; + routes[customIdx].selected = true; } - return data.routes; + return routes; } diff --git a/src/controllers/admin/errors.js b/src/controllers/admin/errors.js index 55f790edab..accefbdae2 100644 --- a/src/controllers/admin/errors.js +++ b/src/controllers/admin/errors.js @@ -1,33 +1,28 @@ 'use strict'; -var async = require('async'); -var json2csv = require('json-2-csv').json2csv; +const json2csv = require('json-2-csv').json2csv; +const util = require('util'); -var meta = require('../../meta'); -var analytics = require('../../analytics'); +const meta = require('../../meta'); +const analytics = require('../../analytics'); +const utils = require('../../utils'); -var errorsController = module.exports; +const errorsController = module.exports; -errorsController.get = function (req, res, next) { - async.waterfall([ - function (next) { - async.parallel({ - 'not-found': async.apply(meta.errors.get, true), - analytics: async.apply(analytics.getErrorAnalytics), - }, next); - }, - function (data) { - res.render('admin/advanced/errors', data); - }, - ], next); +errorsController.get = async function (req, res) { + const data = await utils.promiseParallel({ + 'not-found': meta.errors.get(true), + analytics: analytics.getErrorAnalytics(), + }); + res.render('admin/advanced/errors', data); }; -errorsController.export = function (req, res, next) { - async.waterfall([ - async.apply(meta.errors.get, false), - async.apply(json2csv), - function (csv) { - res.set('Content-Type', 'text/csv').set('Content-Disposition', 'attachment; filename="404.csv"').send(csv); - }, - ], next); +const json2csvAsync = util.promisify(function (data, callback) { + json2csv(data, (err, output) => callback(err, output)); +}); + +errorsController.export = async function (req, res) { + const data = await meta.errors.get(false); + const csv = await json2csvAsync(data); + res.set('Content-Type', 'text/csv').set('Content-Disposition', 'attachment; filename="404.csv"').send(csv); }; diff --git a/src/controllers/admin/events.js b/src/controllers/admin/events.js index 080f72f336..7a6c5a3c99 100644 --- a/src/controllers/admin/events.js +++ b/src/controllers/admin/events.js @@ -1,55 +1,44 @@ 'use strict'; -var async = require('async'); +const db = require('../../database'); +const events = require('../../events'); +const pagination = require('../../pagination'); -var db = require('../../database'); -var events = require('../../events'); -var pagination = require('../../pagination'); +const eventsController = module.exports; -var eventsController = module.exports; - -eventsController.get = function (req, res, next) { - var page = parseInt(req.query.page, 10) || 1; - var itemsPerPage = parseInt(req.query.perPage, 10) || 20; - var start = (page - 1) * itemsPerPage; - var stop = start + itemsPerPage - 1; +eventsController.get = async function (req, res) { + const page = parseInt(req.query.page, 10) || 1; + const itemsPerPage = parseInt(req.query.perPage, 10) || 20; + const start = (page - 1) * itemsPerPage; + const stop = start + itemsPerPage - 1; // Limit by date - var from = req.query.start ? new Date(req.query.start) || undefined : undefined; - var to = req.query.end ? new Date(req.query.end) || undefined : new Date(); + let from = req.query.start ? new Date(req.query.start) || undefined : undefined; + let to = req.query.end ? new Date(req.query.end) || undefined : new Date(); from = from && from.setHours(0, 0, 0, 0); // setHours returns a unix timestamp (Number, not Date) to = to && to.setHours(23, 59, 59, 999); // setHours returns a unix timestamp (Number, not Date) - var currentFilter = req.query.type || ''; + const currentFilter = req.query.type || ''; - async.waterfall([ - function (next) { - async.parallel({ - eventCount: function (next) { - db.sortedSetCount('events:time' + (currentFilter ? ':' + currentFilter : ''), from || '-inf', to, next); - }, - events: function (next) { - events.getEvents(currentFilter, start, stop, from || '-inf', to, next); - }, - }, next); - }, - function (results) { - var types = [''].concat(events.types).map(function (type) { - return { - value: type, - name: type || 'all', - selected: type === currentFilter, - }; - }); + const [eventCount, eventData] = await Promise.all([ + db.sortedSetCount('events:time' + (currentFilter ? ':' + currentFilter : ''), from || '-inf', to), + events.getEvents(currentFilter, start, stop, from || '-inf', to), + ]); - var pageCount = Math.max(1, Math.ceil(results.eventCount / itemsPerPage)); + const types = [''].concat(events.types).map(function (type) { + return { + value: type, + name: type || 'all', + selected: type === currentFilter, + }; + }); - res.render('admin/advanced/events', { - events: results.events, - pagination: pagination.create(page, pageCount, req.query), - types: types, - query: req.query, - }); - }, - ], next); + const pageCount = Math.max(1, Math.ceil(eventCount / itemsPerPage)); + + res.render('admin/advanced/events', { + events: eventData, + pagination: pagination.create(page, pageCount, req.query), + types: types, + query: req.query, + }); }; diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js index 3ae86ceb6c..33e3971e8c 100644 --- a/src/controllers/admin/groups.js +++ b/src/controllers/admin/groups.js @@ -1,91 +1,62 @@ 'use strict'; -var async = require('async'); -var validator = require('validator'); +const validator = require('validator'); -var db = require('../../database'); -var groups = require('../../groups'); -var meta = require('../../meta'); -var pagination = require('../../pagination'); +const db = require('../../database'); +const groups = require('../../groups'); +const meta = require('../../meta'); +const pagination = require('../../pagination'); -var groupsController = module.exports; +const groupsController = module.exports; -groupsController.list = function (req, res, next) { - var page = parseInt(req.query.page, 10) || 1; - var groupsPerPage = 20; - var pageCount = 0; +groupsController.list = async function (req, res) { + const page = parseInt(req.query.page, 10) || 1; + const groupsPerPage = 20; - async.waterfall([ - function (next) { - getGroupNames(next); - }, - function (groupNames, next) { - pageCount = Math.ceil(groupNames.length / groupsPerPage); + let groupNames = await getGroupNames(); + const pageCount = Math.ceil(groupNames.length / groupsPerPage); + const start = (page - 1) * groupsPerPage; + const stop = start + groupsPerPage - 1; - var start = (page - 1) * groupsPerPage; - var stop = start + groupsPerPage - 1; - - groupNames = groupNames.slice(start, stop + 1); - groups.getGroupsData(groupNames, next); - }, - function (groupData) { - res.render('admin/manage/groups', { - groups: groupData, - pagination: pagination.create(page, pageCount), - yourid: req.uid, - }); - }, - ], next); + groupNames = groupNames.slice(start, stop + 1); + const groupData = await groups.getGroupsData(groupNames); + res.render('admin/manage/groups', { + groups: groupData, + pagination: pagination.create(page, pageCount), + yourid: req.uid, + }); }; -groupsController.get = function (req, res, callback) { - var groupName = req.params.name; - async.waterfall([ - function (next) { - async.parallel({ - groupNames: function (next) { - getGroupNames(next); - }, - group: function (next) { - groups.get(groupName, { uid: req.uid, truncateUserList: true, userListCount: 20 }, next); - }, - }, next); - }, - function (result) { - if (!result.group) { - return callback(); - } - result.group.isOwner = true; +groupsController.get = async function (req, res, next) { + const groupName = req.params.name; + const [groupNames, group] = await Promise.all([ + getGroupNames(), + groups.get(groupName, { uid: req.uid, truncateUserList: true, userListCount: 20 }), + ]); - result.groupNames = result.groupNames.map(function (name) { - return { - encodedName: encodeURIComponent(name), - displayName: validator.escape(String(name)), - selected: name === groupName, - }; - }); + if (!group) { + return next(); + } + group.isOwner = true; - res.render('admin/manage/group', { - group: result.group, - groupNames: result.groupNames, - allowPrivateGroups: meta.config.allowPrivateGroups, - maximumGroupNameLength: meta.config.maximumGroupNameLength, - maximumGroupTitleLength: meta.config.maximumGroupTitleLength, - }); - }, - ], callback); + const groupNameData = groupNames.map(function (name) { + return { + encodedName: encodeURIComponent(name), + displayName: validator.escape(String(name)), + selected: name === groupName, + }; + }); + + res.render('admin/manage/group', { + group: group, + groupNames: groupNameData, + allowPrivateGroups: meta.config.allowPrivateGroups, + maximumGroupNameLength: meta.config.maximumGroupNameLength, + maximumGroupTitleLength: meta.config.maximumGroupTitleLength, + }); }; -function getGroupNames(callback) { - async.waterfall([ - function (next) { - db.getSortedSetRange('groups:createtime', 0, -1, next); - }, - function (groupNames, next) { - groupNames = groupNames.filter(function (name) { - return name !== 'registered-users' && !groups.isPrivilegeGroup(name); - }); - next(null, groupNames); - }, - ], callback); +async function getGroupNames() { + const groupNames = await db.getSortedSetRange('groups:createtime', 0, -1); + return groupNames.filter(name => name !== 'registered-users' && !groups.isPrivilegeGroup(name)); } diff --git a/src/controllers/admin/homepage.js b/src/controllers/admin/homepage.js index 16678ba086..30bbc1855c 100644 --- a/src/controllers/admin/homepage.js +++ b/src/controllers/admin/homepage.js @@ -1,58 +1,10 @@ 'use strict'; -var async = require('async'); +const helpers = require('../helpers'); -var categories = require('../../categories'); -var privileges = require('../../privileges'); -var plugins = require('../../plugins'); +const homePageController = module.exports; -var homePageController = module.exports; - -homePageController.get = function (req, res, next) { - async.waterfall([ - function (next) { - categories.getAllCidsFromSet('categories:cid', next); - }, - function (cids, next) { - privileges.categories.filterCids('find', cids, 0, next); - }, - function (cids, next) { - categories.getCategoriesFields(cids, ['name', 'slug'], next); - }, - function (categoryData, next) { - categoryData = categoryData.map(function (category) { - return { - route: 'category/' + category.slug, - name: 'Category: ' + category.name, - }; - }); - - plugins.fireHook('filter:homepage.get', { routes: [ - { - route: 'categories', - name: 'Categories', - }, - { - route: 'recent', - name: 'Recent', - }, - { - route: 'top', - name: 'Top', - }, - { - route: 'popular', - name: 'Popular', - }, - ].concat(categoryData) }, next); - }, - function (data) { - data.routes.push({ - route: '', - name: 'Custom', - }); - - res.render('admin/general/homepage', data); - }, - ], next); +homePageController.get = async function (req, res) { + const routes = await helpers.getHomePageRoutes(req.uid); + res.render('admin/general/homepage', { routes: routes }); }; diff --git a/src/controllers/admin/hooks.js b/src/controllers/admin/hooks.js index f3440d5689..35a2600553 100644 --- a/src/controllers/admin/hooks.js +++ b/src/controllers/admin/hooks.js @@ -1,14 +1,14 @@ 'use strict'; const validator = require('validator'); -var plugins = require('../../plugins'); +const plugins = require('../../plugins'); -var hooksController = module.exports; +const hooksController = module.exports; hooksController.get = function (req, res) { - var hooks = []; + const hooks = []; Object.keys(plugins.loadedHooks).forEach(function (key, hookIndex) { - var current = { + const current = { hookName: key, methods: [], index: 'hook-' + hookIndex, diff --git a/src/controllers/admin/info.js b/src/controllers/admin/info.js index 2649c5c1ee..4a93fb2089 100644 --- a/src/controllers/admin/info.js +++ b/src/controllers/admin/info.js @@ -1,27 +1,24 @@ 'use strict'; -var async = require('async'); -var os = require('os'); -var winston = require('winston'); -var nconf = require('nconf'); -var exec = require('child_process').exec; +const os = require('os'); +const winston = require('winston'); +const nconf = require('nconf'); +const exec = require('child_process').exec; -var pubsub = require('../../pubsub'); -var rooms = require('../../socket.io/admin/rooms'); +const pubsub = require('../../pubsub'); +const rooms = require('../../socket.io/admin/rooms'); -var infoController = module.exports; +const infoController = module.exports; -var info = {}; +let info = {}; infoController.get = function (req, res) { info = {}; pubsub.publish('sync:node:info:start'); - var timeoutMS = 1000; + const timeoutMS = 1000; setTimeout(function () { - var data = []; - Object.keys(info).forEach(function (key) { - data.push(info[key]); - }); + const data = []; + Object.keys(info).forEach(key => data.push(info[key])); data.sort(function (a, b) { if (a.id < b.id) { return -1; @@ -42,22 +39,22 @@ infoController.get = function (req, res) { }, timeoutMS); }; -pubsub.on('sync:node:info:start', function () { - getNodeInfo(function (err, data) { - if (err) { - return winston.error(err); - } +pubsub.on('sync:node:info:start', async function () { + try { + const data = await getNodeInfo(); data.id = os.hostname() + ':' + nconf.get('port'); pubsub.publish('sync:node:info:end', { data: data, id: data.id }); - }); + } catch (err) { + winston.error(err); + } }); pubsub.on('sync:node:info:end', function (data) { info[data.id] = data.data; }); -function getNodeInfo(callback) { - var data = { +async function getNodeInfo() { + const data = { process: { port: nconf.get('port'), pid: process.pid, @@ -82,26 +79,16 @@ function getNodeInfo(callback) { data.process.cpuUsage.system = data.process.cpuUsage.system.toFixed(2); data.process.memoryUsage.humanReadable = (data.process.memoryUsage.rss / (1024 * 1024)).toFixed(2); - async.waterfall([ - function (next) { - async.parallel({ - stats: function (next) { - rooms.getLocalStats(next); - }, - gitInfo: function (next) { - getGitInfo(next); - }, - }, next); - }, - function (results, next) { - data.git = results.gitInfo; - data.stats = results.stats; - next(null, data); - }, - ], callback); + const [stats, gitInfo] = await Promise.all([ + rooms.getLocalStats(), + getGitInfo(), + ]); + data.git = gitInfo; + data.stats = stats; + return data; } -function getGitInfo(callback) { +async function getGitInfo() { function get(cmd, callback) { exec(cmd, function (err, stdout) { if (err) { @@ -110,12 +97,10 @@ function getGitInfo(callback) { callback(null, stdout ? stdout.replace(/\n$/, '') : 'no-git-info'); }); } - async.parallel({ - hash: function (next) { - get('git rev-parse HEAD', next); - }, - branch: function (next) { - get('git rev-parse --abbrev-ref HEAD', next); - }, - }, callback); + const getAsync = require('util').promisify(get); + const [hash, branch] = await Promise.all([ + getAsync('git rev-parse HEAD'), + getAsync('git rev-parse --abbrev-ref HEAD'), + ]); + return { hash: hash, branch: branch }; } diff --git a/src/controllers/admin/languages.js b/src/controllers/admin/languages.js index 6276b4c08a..3fc85ed341 100644 --- a/src/controllers/admin/languages.js +++ b/src/controllers/admin/languages.js @@ -1,26 +1,18 @@ 'use strict'; -var async = require('async'); +const languages = require('../../languages'); +const meta = require('../../meta'); -var languages = require('../../languages'); -var meta = require('../../meta'); +const languagesController = module.exports; -var languagesController = module.exports; +languagesController.get = async function (req, res) { + const languageData = await languages.list(); + languageData.forEach(function (language) { + language.selected = language.code === meta.config.defaultLang; + }); -languagesController.get = function (req, res, next) { - async.waterfall([ - function (next) { - languages.list(next); - }, - function (languages) { - languages.forEach(function (language) { - language.selected = language.code === meta.config.defaultLang; - }); - - res.render('admin/general/languages', { - languages: languages, - autoDetectLang: meta.config.autoDetectLang, - }); - }, - ], next); + res.render('admin/general/languages', { + languages: languageData, + autoDetectLang: meta.config.autoDetectLang, + }); }; diff --git a/src/controllers/admin/logger.js b/src/controllers/admin/logger.js index 4d8991e88c..ad4c83e738 100644 --- a/src/controllers/admin/logger.js +++ b/src/controllers/admin/logger.js @@ -1,6 +1,6 @@ 'use strict'; -var loggerController = module.exports; +const loggerController = module.exports; loggerController.get = function (req, res) { res.render('admin/development/logger', {}); diff --git a/src/controllers/admin/logs.js b/src/controllers/admin/logs.js index 1765cfc352..bbdd20cca8 100644 --- a/src/controllers/admin/logs.js +++ b/src/controllers/admin/logs.js @@ -1,24 +1,20 @@ 'use strict'; -var async = require('async'); -var validator = require('validator'); +const validator = require('validator'); +const winston = require('winston'); -var meta = require('../../meta'); +const meta = require('../../meta'); -var logsController = module.exports; +const logsController = module.exports; -logsController.get = function (req, res, next) { - async.waterfall([ - function (next) { - meta.logs.get(next); - }, - function (logs) { - res.render('admin/advanced/logs', { - data: validator.escape(logs), - }); - }, - ], next); +logsController.get = async function (req, res) { + let logs = ''; + try { + logs = await meta.logs.get(); + } catch (err) { + winston.error(err); + } + res.render('admin/advanced/logs', { + data: validator.escape(logs), + }); }; - - -module.exports = logsController; diff --git a/src/controllers/admin/navigation.js b/src/controllers/admin/navigation.js index a5dc623d77..258f962926 100644 --- a/src/controllers/admin/navigation.js +++ b/src/controllers/admin/navigation.js @@ -1,43 +1,37 @@ 'use strict'; -var async = require('async'); -var navigationAdmin = require('../../navigation/admin'); +const navigationAdmin = require('../../navigation/admin'); const groups = require('../../groups'); -var navigationController = module.exports; +const navigationController = module.exports; -navigationController.get = function (req, res, next) { - async.waterfall([ - function (next) { - async.parallel({ - admin: async.apply(navigationAdmin.getAdmin), - groups: async.apply(groups.getNonPrivilegeGroups, 'groups:createtime', 0, -1), - }, next); - }, - function (result) { - result.groups.sort((a, b) => b.system - a.system); - result.groups = result.groups.map(group => ({ name: group.name, displayName: group.displayName })); +navigationController.get = async function (req, res) { + const [admin, allGroups] = await Promise.all([ + navigationAdmin.getAdmin(), + groups.getNonPrivilegeGroups('groups:createtime', 0, -1), + ]); - result.admin.enabled.forEach(function (enabled, index) { - enabled.index = index; - enabled.selected = index === 0; + allGroups.sort((a, b) => b.system - a.system); + const groupsData = allGroups.map(group => ({ name: group.name, displayName: group.displayName })); - enabled.groups = result.groups.map(function (group) { - return { - displayName: group.displayName, - selected: enabled.groups.includes(group.name), - }; - }); - }); + admin.enabled.forEach(function (enabled, index) { + enabled.index = index; + enabled.selected = index === 0; - result.admin.available.forEach(function (available) { - available.groups = result.groups; - }); + enabled.groups = groupsData.map(function (group) { + return { + displayName: group.displayName, + selected: enabled.groups.includes(group.name), + }; + }); + }); - result.admin.navigation = result.admin.enabled.slice(); + admin.available.forEach(function (available) { + available.groups = groups; + }); - res.render('admin/general/navigation', result.admin); - }, - ], next); + admin.navigation = admin.enabled.slice(); + + res.render('admin/general/navigation', admin); }; diff --git a/src/controllers/admin/plugins.js b/src/controllers/admin/plugins.js index e12803cf86..4bfefff5b4 100644 --- a/src/controllers/admin/plugins.js +++ b/src/controllers/admin/plugins.js @@ -1,67 +1,58 @@ 'use strict'; -var async = require('async'); -var nconf = require('nconf'); -var plugins = require('../../plugins'); -var meta = require('../../meta'); +const nconf = require('nconf'); +const winston = require('winston'); +const plugins = require('../../plugins'); +const meta = require('../../meta'); -var pluginsController = module.exports; +const pluginsController = module.exports; -pluginsController.get = function (req, res, next) { - async.waterfall([ - function (next) { - async.parallel({ - compatible: function (next) { - plugins.list(function (err, plugins) { - if (err || !Array.isArray(plugins)) { - plugins = []; - } +pluginsController.get = async function (req, res) { + const [compatible, all] = await Promise.all([ + getCompatiblePluigns(), + getAllPlugins(), + ]); - next(null, plugins); - }); - }, - all: function (next) { - plugins.list(false, function (err, plugins) { - if (err || !Array.isArray(plugins)) { - plugins = []; - } + const compatiblePkgNames = compatible.map(pkgData => pkgData.name); + const installedPlugins = compatible.filter(plugin => plugin && plugin.installed); + const activePlugins = all.filter(plugin => plugin && plugin.installed && plugin.active); - next(null, plugins); - }); - }, - }, next); - }, - function (payload) { - var compatiblePkgNames = payload.compatible.map(function (pkgData) { - return pkgData.name; - }); - var installedPlugins = payload.compatible.filter(function (plugin) { - return plugin && plugin.installed; - }); - var activePlugins = payload.all.filter(function (plugin) { - return plugin && plugin.installed && plugin.active; - }); - - res.render('admin/extend/plugins', { - installed: installedPlugins, - installedCount: installedPlugins.length, - activeCount: activePlugins.length, - inactiveCount: Math.max(0, installedPlugins.length - activePlugins.length), - upgradeCount: payload.compatible.reduce(function (count, current) { - if (current.installed && current.outdated) { - count += 1; - } - return count; - }, 0), - download: payload.compatible.filter(function (plugin) { - return !plugin.installed; - }), - incompatible: payload.all.filter(function (plugin) { - return !compatiblePkgNames.includes(plugin.name); - }), - submitPluginUsage: meta.config.submitPluginUsage, - version: nconf.get('version'), - }); - }, - ], next); + res.render('admin/extend/plugins', { + installed: installedPlugins, + installedCount: installedPlugins.length, + activeCount: activePlugins.length, + inactiveCount: Math.max(0, installedPlugins.length - activePlugins.length), + upgradeCount: compatible.reduce(function (count, current) { + if (current.installed && current.outdated) { + count += 1; + } + return count; + }, 0), + download: compatible.filter(function (plugin) { + return !plugin.installed; + }), + incompatible: all.filter(function (plugin) { + return !compatiblePkgNames.includes(plugin.name); + }), + submitPluginUsage: meta.config.submitPluginUsage, + version: nconf.get('version'), + }); }; + +async function getCompatiblePluigns() { + return await getPlugins(true); +} + +async function getAllPlugins() { + return await getPlugins(false); +} + +async function getPlugins(matching) { + try { + const pluginsData = await plugins.list(matching); + return pluginsData || []; + } catch (err) { + winston.error(err); + return []; + } +} diff --git a/src/controllers/admin/postqueue.js b/src/controllers/admin/postqueue.js index 1cb66ff0c8..1d3eba0f71 100644 --- a/src/controllers/admin/postqueue.js +++ b/src/controllers/admin/postqueue.js @@ -1,115 +1,76 @@ 'use strict'; -var async = require('async'); -var validator = require('validator'); +const validator = require('validator'); -var db = require('../../database'); -var user = require('../../user'); -var topics = require('../../topics'); -var categories = require('../../categories'); -var pagination = require('../../pagination'); -var plugins = require('../../plugins'); -var utils = require('../../utils'); +const db = require('../../database'); +const user = require('../../user'); +const topics = require('../../topics'); +const categories = require('../../categories'); +const pagination = require('../../pagination'); +const plugins = require('../../plugins'); +const utils = require('../../utils'); -var postQueueController = module.exports; +const postQueueController = module.exports; -postQueueController.get = function (req, res, next) { - var page = parseInt(req.query.page, 10) || 1; - var postsPerPage = 20; - var results; - async.waterfall([ - function (next) { - async.parallel({ - ids: function (next) { - db.getSortedSetRange('post:queue', 0, -1, next); - }, - isAdminOrGlobalMod: function (next) { - user.isAdminOrGlobalMod(req.uid, next); - }, - moderatedCids: function (next) { - user.getModeratedCids(req.uid, next); - }, - }, next); - }, - function (_results, next) { - results = _results; - getQueuedPosts(results.ids, next); - }, - function (postData) { - postData = postData.filter(function (postData) { - return postData && (results.isAdminOrGlobalMod || results.moderatedCids.includes(String(postData.category.cid))); - }); +postQueueController.get = async function (req, res) { + const page = parseInt(req.query.page, 10) || 1; + const postsPerPage = 20; - var pageCount = Math.max(1, Math.ceil(postData.length / postsPerPage)); - var start = (page - 1) * postsPerPage; - var stop = start + postsPerPage - 1; - postData = postData.slice(start, stop + 1); + const [ids, isAdminOrGlobalMod, moderatedCids] = await Promise.all([ + db.getSortedSetRange('post:queue', 0, -1), + user.isAdminOrGlobalMod(req.uid), + user.getModeratedCids(req.uid), + ]); - res.render('admin/manage/post-queue', { - title: '[[pages:post-queue]]', - posts: postData, - pagination: pagination.create(page, pageCount), - }); - }, - ], next); + let postData = await getQueuedPosts(ids); + postData = postData.filter(p => p && (isAdminOrGlobalMod || moderatedCids.includes(String(p.category.cid)))); + + const pageCount = Math.max(1, Math.ceil(postData.length / postsPerPage)); + const start = (page - 1) * postsPerPage; + const stop = start + postsPerPage - 1; + postData = postData.slice(start, stop + 1); + + res.render('admin/manage/post-queue', { + title: '[[pages:post-queue]]', + posts: postData, + pagination: pagination.create(page, pageCount), + }); }; -function getQueuedPosts(ids, callback) { - var postData; - async.waterfall([ - function (next) { - const keys = ids.map(id => 'post:queue:' + id); - db.getObjects(keys, next); - }, - function (data, next) { - postData = data; - data.forEach(function (data) { - if (data) { - data.data = JSON.parse(data.data); - data.data.timestampISO = utils.toISOString(data.data.timestamp); - } - return data; - }); - const uids = data.map(data => data && data.uid); - user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); - }, - function (userData, next) { - postData.forEach(function (postData, index) { - if (postData) { - postData.user = userData[index]; - } - }); +async function getQueuedPosts(ids) { + const keys = ids.map(id => 'post:queue:' + id); + const postData = await db.getObjects(keys); + postData.forEach(function (data) { + if (data) { + data.data = JSON.parse(data.data); + data.data.timestampISO = utils.toISOString(data.data.timestamp); + } + }); + const uids = postData.map(data => data && data.uid); + const userData = await user.getUsersFields(uids, ['username', 'userslug', 'picture']); + postData.forEach(function (postData, index) { + if (postData) { + postData.user = userData[index]; + postData.data.rawContent = validator.escape(String(postData.data.content)); + postData.data.title = validator.escape(String(postData.data.title || '')); + } + }); - async.map(postData, function (postData, next) { - if (!postData) { - return next(null, postData); - } - postData.data.rawContent = validator.escape(String(postData.data.content)); - postData.data.title = validator.escape(String(postData.data.title || '')); - async.waterfall([ - function (next) { - if (postData.data.cid) { - next(null, { cid: postData.data.cid }); - } else if (postData.data.tid) { - topics.getTopicFields(postData.data.tid, ['title', 'cid'], next); - } else { - next(null, { cid: 0 }); - } - }, - function (topicData, next) { - postData.topic = topicData; - categories.getCategoryData(topicData.cid, next); - }, - function (categoryData, next) { - postData.category = categoryData; - plugins.fireHook('filter:parse.post', { postData: postData.data }, next); - }, - function (result, next) { - postData.data.content = result.postData.content; - next(null, postData); - }, - ], next); - }, next); - }, - ], callback); + await Promise.all(postData.map(p => addMetaData(p))); + return postData; +} + +async function addMetaData(postData) { + if (!postData) { + return; + } + postData.topic = { cid: 0 }; + if (postData.data.cid) { + postData.topic = { cid: postData.data.cid }; + } else if (postData.data.tid) { + postData.topic = await topics.getTopicFields(postData.data.tid, ['title', 'cid']); + } + postData.category = await categories.getCategoryData(postData.topic.cid); + const result = await plugins.fireHook('filter:parse.post', { postData: postData.data }); + postData.data.content = result.postData.content; } diff --git a/src/controllers/admin/privileges.js b/src/controllers/admin/privileges.js index accbc8b67d..3003ace653 100644 --- a/src/controllers/admin/privileges.js +++ b/src/controllers/admin/privileges.js @@ -1,62 +1,52 @@ 'use strict'; -var async = require('async'); +const categories = require('../../categories'); +const privileges = require('../../privileges'); -var categories = require('../../categories'); -var privileges = require('../../privileges'); +const privilegesController = module.exports; -var privilegesController = module.exports; +privilegesController.get = async function (req, res) { + const cid = req.params.cid ? parseInt(req.params.cid, 10) : 0; + const [privilegesData, categoriesData] = await Promise.all([ + getPrivileges(cid), + getCategories(req.uid), + ]); -privilegesController.get = function (req, res, callback) { - var cid = req.params.cid ? parseInt(req.params.cid, 10) : 0; - async.waterfall([ - function (next) { - async.parallel({ - privileges: function (next) { - if (!cid) { - privileges.global.list(next); - } else { - privileges.categories.list(cid, next); - } - }, - categories: function (next) { - async.waterfall([ - function (next) { - categories.getAllCidsFromSet('categories:cid', next); - }, - function (cids, next) { - categories.getCategories(cids, req.uid, next); - }, - function (categoriesData, next) { - categoriesData = categories.getTree(categoriesData); - categories.buildForSelectCategories(categoriesData, next); - }, - ], next); - }, - }, next); - }, - function (data) { - data.categories.unshift({ - cid: 0, - name: '[[admin/manage/privileges:global]]', - icon: 'fa-list', - }); - data.categories.forEach(function (category) { - if (category) { - category.selected = category.cid === cid; + categoriesData.unshift({ + cid: 0, + name: '[[admin/manage/privileges:global]]', + icon: 'fa-list', + }); - if (category.selected) { - data.selected = category; - } - } - }); + let selectedCategory; + categoriesData.forEach(function (category) { + if (category) { + category.selected = category.cid === cid; - res.render('admin/manage/privileges', { - privileges: data.privileges, - categories: data.categories, - selectedCategory: data.selected, - cid: cid, - }); - }, - ], callback); + if (category.selected) { + selectedCategory = category; + } + } + }); + + res.render('admin/manage/privileges', { + privileges: privilegesData, + categories: categoriesData, + selectedCategory: selectedCategory, + cid: cid, + }); }; + +async function getPrivileges(cid) { + if (!cid) { + return await privileges.global.list(); + } + return await privileges.categories.list(cid); +} + +async function getCategories(uid) { + const cids = await categories.getAllCidsFromSet('categories:cid'); + const categoriesData = await categories.getCategories(cids, uid); + const tree = categories.getTree(categoriesData); + return await categories.buildForSelectCategories(tree); +} diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 10e9e6dd00..30f6433e07 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -323,4 +323,46 @@ function recursive(category, categoriesData, level) { } } +helpers.getHomePageRoutes = async function (uid) { + let cids = await categories.getAllCidsFromSet('categories:cid'); + cids = await privileges.categories.filterCids('find', cids, uid); + const categoryData = await categories.getCategoriesFields(cids, ['name', 'slug']); + + const categoryRoutes = categoryData.map(function (category) { + return { + route: 'category/' + category.slug, + name: 'Category: ' + category.name, + }; + }); + const routes = [ + { + route: 'categories', + name: 'Categories', + }, + { + route: 'unread', + name: 'Unread', + }, + { + route: 'recent', + name: 'Recent', + }, + { + route: 'top', + name: 'Top', + }, + { + route: 'popular', + name: 'Popular', + }, + ].concat(categoryRoutes, [ + { + route: 'custom', + name: 'Custom', + }, + ]); + const data = await plugins.fireHook('filter:homepage.get', { routes: routes }); + return data.routes; +}; + helpers.async = require('../promisify')(helpers); diff --git a/src/meta/logs.js b/src/meta/logs.js index 1f950d6057..72f478da75 100644 --- a/src/meta/logs.js +++ b/src/meta/logs.js @@ -1,18 +1,18 @@ 'use strict'; -var path = require('path'); -var fs = require('fs'); - -var Logs = module.exports; +const path = require('path'); +const fs = require('fs'); +const util = require('util'); +const readFileAsync = util.promisify(fs.readFile); +const truncateAsync = util.promisify(fs.truncate); +const Logs = module.exports; Logs.path = path.join(__dirname, '..', '..', 'logs', 'output.log'); -Logs.get = function (callback) { - fs.readFile(Logs.path, { - encoding: 'utf-8', - }, callback); +Logs.get = async function () { + return await readFileAsync(Logs.path, 'utf-8'); }; -Logs.clear = function (callback) { - fs.truncate(Logs.path, 0, callback); +Logs.clear = async function () { + return await truncateAsync(Logs.path, 0); }; diff --git a/src/socket.io/admin/rooms.js b/src/socket.io/admin/rooms.js index 936a74199e..4c393c5a05 100644 --- a/src/socket.io/admin/rooms.js +++ b/src/socket.io/admin/rooms.js @@ -179,3 +179,5 @@ SocketRooms.getLocalStats = function (callback) { callback(null, socketData); }; + +require('../../promisify')(SocketRooms);