From a51ec591ee91012d6f091382572069e1495ab402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jul 2019 20:44:00 -0400 Subject: [PATCH] feat: #7743, finish user module --- src/notifications.js | 4 +- src/user/reset.js | 261 ++++++++++++++++--------------------------- src/user/search.js | 168 ++++++++++++---------------- src/user/settings.js | 194 +++++++++++++------------------- src/user/topics.js | 17 ++- src/user/uploads.js | 50 +++------ 6 files changed, 268 insertions(+), 426 deletions(-) diff --git a/src/notifications.js b/src/notifications.js index ce2bfe51f3..591d2a6a4b 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -129,8 +129,8 @@ Notifications.push = async function (notification, uids) { } setTimeout(function () { - batch.processArray(uids, function (uids, next) { - pushToUids(uids, notification, next); + batch.processArray(uids, async function (uids) { + await pushToUids(uids, notification); }, { interval: 1000 }, function (err) { if (err) { winston.error(err.stack); diff --git a/src/user/reset.js b/src/user/reset.js index a9864d82d6..7415ae1cb5 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require('async'); var nconf = require('nconf'); var winston = require('winston'); @@ -16,185 +15,119 @@ var UserReset = module.exports; var twoHours = 7200000; -UserReset.validate = function (code, callback) { - async.waterfall([ - function (next) { - db.getObjectField('reset:uid', code, next); - }, - function (uid, next) { - if (!uid) { - return callback(null, false); - } - db.sortedSetScore('reset:issueDate', code, next); - }, - function (issueDate, next) { - next(null, parseInt(issueDate, 10) > Date.now() - twoHours); - }, - ], callback); +UserReset.validate = async function (code) { + const uid = await db.getObjectField('reset:uid', code); + if (!uid) { + return false; + } + const issueDate = await db.sortedSetScore('reset:issueDate', code); + return parseInt(issueDate, 10) > Date.now() - twoHours; }; -UserReset.generate = function (uid, callback) { - var code = utils.generateUUID(); - async.parallel([ - async.apply(db.setObjectField, 'reset:uid', code, uid), - async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code), - ], function (err) { - callback(err, code); +UserReset.generate = async function (uid) { + const code = utils.generateUUID(); + await Promise.all([ + db.setObjectField('reset:uid', code, uid), + db.sortedSetAdd('reset:issueDate', Date.now(), code), + ]); + return code; +}; + +async function canGenerate(uid) { + const score = await db.sortedSetScore('reset:issueDate:uid', uid); + if (score > Date.now() - (1000 * 60)) { + throw new Error('[[error:reset-rate-limited]]'); + } +} + +UserReset.send = async function (email) { + const uid = await user.getUidByEmail(email); + if (!uid) { + throw new Error('[[error:invalid-email]]'); + } + await canGenerate(uid); + await db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid); + const code = await UserReset.generate(uid); + await emailer.send('reset', uid, { + reset_link: nconf.get('url') + '/reset/' + code, + subject: '[[email:password-reset-requested]]', + template: 'reset', + uid: uid, }); }; -function canGenerate(uid, callback) { - async.waterfall([ - function (next) { - db.sortedSetScore('reset:issueDate:uid', uid, next); - }, - function (score, next) { - if (score > Date.now() - (1000 * 60)) { - return next(new Error('[[error:reset-rate-limited]]')); - } - next(); - }, - ], callback); -} +UserReset.commit = async function (code, password) { + await user.isPasswordValid(password); + const validated = await UserReset.validate(code); + if (!validated) { + throw new Error('[[error:reset-code-not-valid]]'); + } + const uid = await db.getObjectField('reset:uid', code); + if (!uid) { + throw new Error('[[error:reset-code-not-valid]]'); + } -UserReset.send = function (email, callback) { - var uid; - async.waterfall([ - function (next) { - user.getUidByEmail(email, next); - }, - function (_uid, next) { - if (!_uid) { - return next(new Error('[[error:invalid-email]]')); - } + const hash = await user.hashPassword(password); - uid = _uid; - canGenerate(uid, next); - }, - function (next) { - db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid, next); - }, - function (next) { - UserReset.generate(uid, next); - }, - function (code, next) { - emailer.send('reset', uid, { - reset_link: nconf.get('url') + '/reset/' + code, - subject: '[[email:password-reset-requested]]', - template: 'reset', - uid: uid, - }, next); - }, - ], callback); + await user.setUserFields(uid, { password: hash, 'email:confirmed': 1 }); + await db.deleteObjectField('reset:uid', code); + await db.sortedSetRemoveBulk([ + ['reset:issueDate', code], + ['reset:issueDate:uid', uid], + ['users:notvalidated', uid], + ]); + await user.reset.updateExpiry(uid); + await user.auth.resetLockout(uid); + await db.delete('uid:' + uid + ':confirm:email:sent'); + await UserReset.cleanByUid(uid); }; -UserReset.commit = function (code, password, callback) { - var uid; - async.waterfall([ - function (next) { - user.isPasswordValid(password, next); - }, - function (next) { - UserReset.validate(code, next); - }, - function (validated, next) { - if (!validated) { - return next(new Error('[[error:reset-code-not-valid]]')); - } - db.getObjectField('reset:uid', code, next); - }, - function (_uid, next) { - uid = _uid; - if (!uid) { - return next(new Error('[[error:reset-code-not-valid]]')); - } - - user.hashPassword(password, next); - }, - function (hash, next) { - async.series([ - async.apply(user.setUserFields, uid, { password: hash, 'email:confirmed': 1 }), - async.apply(db.deleteObjectField, 'reset:uid', code), - async.apply(db.sortedSetRemove, 'reset:issueDate', code), - async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid), - async.apply(user.reset.updateExpiry, uid), - async.apply(user.auth.resetLockout, uid), - async.apply(db.delete, 'uid:' + uid + ':confirm:email:sent'), - async.apply(db.sortedSetRemove, 'users:notvalidated', uid), - async.apply(UserReset.cleanByUid, uid), - ], function (err) { - next(err); - }); - }, - ], callback); +UserReset.updateExpiry = async function (uid) { + const oneDay = 1000 * 60 * 60 * 24; + const expireDays = meta.config.passwordExpiryDays; + const expiry = Date.now() + (oneDay * expireDays); + await user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0); }; -UserReset.updateExpiry = function (uid, callback) { - var oneDay = 1000 * 60 * 60 * 24; - var expireDays = meta.config.passwordExpiryDays; - var expiry = Date.now() + (oneDay * expireDays); +UserReset.clean = async function () { + const [tokens, uids] = await Promise.all([ + db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours), + db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours), + ]); + if (!tokens.length && !uids.length) { + return; + } - callback = callback || function () {}; - user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback); + winston.verbose('[UserReset.clean] Removing ' + tokens.length + ' reset tokens from database'); + await cleanTokensAndUids(tokens, uids); }; -UserReset.clean = function (callback) { - async.waterfall([ - function (next) { - async.parallel({ - tokens: function (next) { - db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours, next); - }, - uids: function (next) { - db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours, next); - }, - }, next); - }, - function (results, next) { - if (!results.tokens.length && !results.uids.length) { - return next(); - } - - winston.verbose('[UserReset.clean] Removing ' + results.tokens.length + ' reset tokens from database'); - async.parallel([ - async.apply(db.deleteObjectFields, 'reset:uid', results.tokens), - async.apply(db.sortedSetRemove, 'reset:issueDate', results.tokens), - async.apply(db.sortedSetRemove, 'reset:issueDate:uid', results.uids), - ], next); - }, - ], callback); -}; - -UserReset.cleanByUid = function (uid, callback) { - var toClean = []; +UserReset.cleanByUid = async function (uid) { + const tokensToClean = []; uid = parseInt(uid, 10); - async.waterfall([ - function (next) { - batch.processSortedSet('reset:issueDate', function (tokens, next) { - db.getObjectFields('reset:uid', tokens, function (err, results) { - for (var code in results) { - if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) { - toClean.push(code); - } - } - - next(err); - }); - }, next); - }, - function (next) { - if (!toClean.length) { - winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').'); - return setImmediate(next); + await batch.processSortedSet('reset:issueDate', async function (tokens) { + const results = await db.getObjectFields('reset:uid', tokens); + for (var code in results) { + if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) { + tokensToClean.push(code); } + } + }, { batch: 500 }); - winston.verbose('[UserReset.cleanByUid] Found ' + toClean.length + ' token(s), removing...'); - async.parallel([ - async.apply(db.deleteObjectFields, 'reset:uid', toClean), - async.apply(db.sortedSetRemove, 'reset:issueDate', toClean), - async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid), - ], next); - }, - ], callback); + if (!tokensToClean.length) { + winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').'); + return; + } + + winston.verbose('[UserReset.cleanByUid] Found ' + tokensToClean.length + ' token(s), removing...'); + await cleanTokensAndUids(tokensToClean, uid); }; + +async function cleanTokensAndUids(tokens, uids) { + await Promise.all([ + db.deleteObjectFields('reset:uid', tokens), + db.sortedSetRemove('reset:issueDate', tokens), + db.sortedSetRemove('reset:issueDate:uid', uids), + ]); +} diff --git a/src/user/search.js b/src/user/search.js index 0d8c2907a3..5e6f8829c1 100644 --- a/src/user/search.js +++ b/src/user/search.js @@ -1,95 +1,78 @@ 'use strict'; -var async = require('async'); -var meta = require('../meta'); -var plugins = require('../plugins'); -var db = require('../database'); +const meta = require('../meta'); +const plugins = require('../plugins'); +const db = require('../database'); module.exports = function (User) { - User.search = function (data, callback) { - var query = data.query || ''; - var searchBy = data.searchBy || 'username'; - var page = data.page || 1; - var uid = data.uid || 0; - var paginate = data.hasOwnProperty('paginate') ? data.paginate : true; + User.search = async function (data) { + const query = data.query || ''; + const searchBy = data.searchBy || 'username'; + const page = data.page || 1; + const uid = data.uid || 0; + const paginate = data.hasOwnProperty('paginate') ? data.paginate : true; - var startTime = process.hrtime(); + const startTime = process.hrtime(); - var searchResult = {}; - async.waterfall([ - function (next) { - if (searchBy === 'ip') { - searchByIP(query, next); - } else if (searchBy === 'uid') { - next(null, [query]); - } else { - var searchMethod = data.findUids || findUids; - searchMethod(query, searchBy, data.hardCap, next); - } - }, - function (uids, next) { - filterAndSortUids(uids, data, next); - }, - function (uids, next) { - plugins.fireHook('filter:users.search', { uids: uids, uid: uid }, next); - }, - function (data, next) { - var uids = data.uids; - searchResult.matchCount = uids.length; + let uids = []; + if (searchBy === 'ip') { + uids = await searchByIP(query); + } else if (searchBy === 'uid') { + uids = [query]; + } else { + const searchMethod = data.findUids || findUids; + uids = await searchMethod(query, searchBy, data.hardCap); + } - if (paginate) { - var resultsPerPage = meta.config.userSearchResultsPerPage; - var start = Math.max(0, page - 1) * resultsPerPage; - var stop = start + resultsPerPage; - searchResult.pageCount = Math.ceil(uids.length / resultsPerPage); - uids = uids.slice(start, stop); - } + uids = await filterAndSortUids(uids, data); + const result = await plugins.fireHook('filter:users.search', { uids: uids, uid: uid }); + uids = result.uids; - User.getUsers(uids, uid, next); - }, - function (userData, next) { - searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2); - searchResult.users = userData; - next(null, searchResult); - }, - ], callback); + const searchResult = { + matchCount: uids.length, + }; + + if (paginate) { + var resultsPerPage = meta.config.userSearchResultsPerPage; + var start = Math.max(0, page - 1) * resultsPerPage; + var stop = start + resultsPerPage; + searchResult.pageCount = Math.ceil(uids.length / resultsPerPage); + uids = uids.slice(start, stop); + } + + const userData = await User.getUsers(uids, uid); + searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2); + searchResult.users = userData; + return searchResult; }; - function findUids(query, searchBy, hardCap, callback) { + async function findUids(query, searchBy, hardCap) { if (!query) { - return callback(null, []); + return []; } - query = query.toLowerCase(); - var min = query; - var max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1); + query = String(query).toLowerCase(); + const min = query; + const max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1); - var resultsPerPage = meta.config.userSearchResultsPerPage; + const resultsPerPage = meta.config.userSearchResultsPerPage; hardCap = hardCap || resultsPerPage * 10; - async.waterfall([ - function (next) { - db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap, next); - }, - function (data, next) { - var uids = data.map(function (data) { - return data.split(':')[1]; - }); - next(null, uids); - }, - ], callback); + const data = await db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap); + const uids = data.map(data => data.split(':')[1]); + return uids; } - function filterAndSortUids(uids, data, callback) { + async function filterAndSortUids(uids, data) { uids = uids.filter(uid => parseInt(uid, 10)); - var fields = []; + const fields = []; if (data.sortBy) { fields.push(data.sortBy); } if (data.onlineOnly) { - fields = fields.concat(['status', 'lastonline']); + fields.push('status', 'lastonline'); } if (data.bannedOnly) { fields.push('banned'); @@ -99,45 +82,34 @@ module.exports = function (User) { } if (!fields.length) { - return callback(null, uids); + return uids; } - fields = ['uid'].concat(fields); + fields.push('uid'); + let userData = await User.getUsersFields(uids, fields); + userData = userData.filter(Boolean); + if (data.onlineOnly) { + userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000)); + } - async.waterfall([ - function (next) { - User.getUsersFields(uids, fields, next); - }, - function (userData, next) { - userData = userData.filter(Boolean); - if (data.onlineOnly) { - userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000)); - } + if (data.bannedOnly) { + userData = userData.filter(user => user.banned); + } - if (data.bannedOnly) { - userData = userData.filter(user => user.banned); - } + if (data.flaggedOnly) { + userData = userData.filter(user => parseInt(user.flags, 10) > 0); + } - if (data.flaggedOnly) { - userData = userData.filter(user => parseInt(user.flags, 10) > 0); - } + if (data.sortBy) { + sortUsers(userData, data.sortBy); + } - if (data.sortBy) { - sortUsers(userData, data.sortBy); - } - - uids = userData.map(user => user.uid); - - next(null, uids); - }, - ], callback); + return userData.map(user => user.uid); } function sortUsers(userData, sortBy) { if (sortBy === 'joindate' || sortBy === 'postcount' || sortBy === 'reputation') { - userData.sort(function (u1, u2) { - return u2[sortBy] - u1[sortBy]; - }); + userData.sort((u1, u2) => u2[sortBy] - u1[sortBy]); } else { userData.sort(function (u1, u2) { if (u1[sortBy] < u2[sortBy]) { @@ -150,7 +122,7 @@ module.exports = function (User) { } } - function searchByIP(ip, callback) { - db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1, callback); + async function searchByIP(ip) { + return await db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1); } }; diff --git a/src/user/settings.js b/src/user/settings.js index ca532e52cb..1b000f79dc 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -1,96 +1,73 @@ 'use strict'; -var async = require('async'); - -var meta = require('../meta'); -var db = require('../database'); -var plugins = require('../plugins'); -var notifications = require('../notifications'); +const meta = require('../meta'); +const db = require('../database'); +const plugins = require('../plugins'); +const notifications = require('../notifications'); module.exports = function (User) { - User.getSettings = function (uid, callback) { + User.getSettings = async function (uid) { if (parseInt(uid, 10) <= 0) { - return onSettingsLoaded(0, {}, callback); + return await onSettingsLoaded(0, {}); } - - async.waterfall([ - function (next) { - db.getObject('user:' + uid + ':settings', next); - }, - function (settings, next) { - settings = settings || {}; - settings.uid = uid; - onSettingsLoaded(uid, settings, next); - }, - ], callback); + let settings = await db.getObject('user:' + uid + ':settings'); + settings = settings || {}; + settings.uid = uid; + return await onSettingsLoaded(uid, settings); }; - User.getMultipleUserSettings = function (uids, callback) { + User.getMultipleUserSettings = async function (uids) { if (!Array.isArray(uids) || !uids.length) { - return callback(null, []); + return []; } - var keys = uids.map(uid => 'user:' + uid + ':settings'); - - async.waterfall([ - function (next) { - db.getObjects(keys, next); - }, - function (settings, next) { - settings = settings.map(function (userSettings, index) { - userSettings = userSettings || {}; - userSettings.uid = uids[index]; - return userSettings; - }); - async.map(settings, function (userSettings, next) { - onSettingsLoaded(userSettings.uid, userSettings, next); - }, next); - }, - ], callback); + const keys = uids.map(uid => 'user:' + uid + ':settings'); + let settings = await db.getObjects(keys); + settings = settings.map(function (userSettings, index) { + userSettings = userSettings || {}; + userSettings.uid = uids[index]; + return userSettings; + }); + return await Promise.all(settings.map(s => onSettingsLoaded(s.uid, s))); + // async.map(settings, function (userSettings, next) { + // onSettingsLoaded(userSettings.uid, userSettings, next); + // }, next); }; - function onSettingsLoaded(uid, settings, callback) { - async.waterfall([ - function (next) { - plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }, next); - }, - function (data, next) { - settings = data.settings; + async function onSettingsLoaded(uid, settings) { + const data = await plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }); + settings = data.settings; - var defaultTopicsPerPage = meta.config.topicsPerPage; - var defaultPostsPerPage = meta.config.postsPerPage; + const defaultTopicsPerPage = meta.config.topicsPerPage; + const defaultPostsPerPage = meta.config.postsPerPage; - settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1; - settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1; - settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; - settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); - settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; - settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); - settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); - settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB'; - settings.acpLang = settings.acpLang || settings.userLang; - settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); - settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); - settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; - settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; - settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all'); - settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1; - settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; - settings.bootswatchSkin = settings.bootswatchSkin || ''; - settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; - settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching'); + settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1; + settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1; + settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; + settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); + settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; + settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); + settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); + settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB'; + settings.acpLang = settings.acpLang || settings.userLang; + settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); + settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); + settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; + settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; + settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all'); + settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1; + settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; + settings.bootswatchSkin = settings.bootswatchSkin || ''; + settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; + settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching'); - notifications.getAllNotificationTypes(next); - }, - function (notificationTypes, next) { - notificationTypes.forEach(function (notificationType) { - settings[notificationType] = getSetting(settings, notificationType, 'notification'); - }); + const notificationTypes = await notifications.getAllNotificationTypes(); + notificationTypes.forEach(function (notificationType) { + settings[notificationType] = getSetting(settings, notificationType, 'notification'); + }); - next(null, settings); - }, - ], callback); + return settings; } function getSetting(settings, key, defaultValue) { @@ -102,22 +79,22 @@ module.exports = function (User) { return defaultValue; } - User.saveSettings = function (uid, data, callback) { + User.saveSettings = async function (uid, data) { var maxPostsPerPage = meta.config.maxPostsPerPage || 20; if (!data.postsPerPage || parseInt(data.postsPerPage, 10) <= 1 || parseInt(data.postsPerPage, 10) > maxPostsPerPage) { - return callback(new Error('[[error:invalid-pagination-value, 2, ' + maxPostsPerPage + ']]')); + throw new Error('[[error:invalid-pagination-value, 2, ' + maxPostsPerPage + ']]'); } - var maxTopicsPerPage = meta.config.maxTopicsPerPage || 20; + const maxTopicsPerPage = meta.config.maxTopicsPerPage || 20; if (!data.topicsPerPage || parseInt(data.topicsPerPage, 10) <= 1 || parseInt(data.topicsPerPage, 10) > maxTopicsPerPage) { - return callback(new Error('[[error:invalid-pagination-value, 2, ' + maxTopicsPerPage + ']]')); + throw new Error('[[error:invalid-pagination-value, 2, ' + maxTopicsPerPage + ']]'); } data.userLang = data.userLang || meta.config.defaultLang; plugins.fireHook('action:user.saveSettings', { uid: uid, settings: data }); - var settings = { + const settings = { showemail: data.showemail, showfullname: data.showfullname, openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, @@ -140,51 +117,30 @@ module.exports = function (User) { bootswatchSkin: data.bootswatchSkin, categoryWatchState: data.categoryWatchState, }; - - async.waterfall([ - function (next) { - notifications.getAllNotificationTypes(next); - }, - function (notificationTypes, next) { - notificationTypes.forEach(function (notificationType) { - if (data[notificationType]) { - settings[notificationType] = data[notificationType]; - } - }); - plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data }, next); - }, - function (result, next) { - db.setObject('user:' + uid + ':settings', result.settings, next); - }, - function (next) { - User.updateDigestSetting(uid, data.dailyDigestFreq, next); - }, - function (next) { - User.getSettings(uid, next); - }, - ], callback); + const notificationTypes = await notifications.getAllNotificationTypes(); + notificationTypes.forEach(function (notificationType) { + if (data[notificationType]) { + settings[notificationType] = data[notificationType]; + } + }); + const result = await plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data }); + await db.setObject('user:' + uid + ':settings', result.settings); + await User.updateDigestSetting(uid, data.dailyDigestFreq); + return await User.getSettings(uid); }; - User.updateDigestSetting = function (uid, dailyDigestFreq, callback) { - async.waterfall([ - function (next) { - db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid, next); - }, - function (next) { - if (['day', 'week', 'month'].includes(dailyDigestFreq)) { - db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid, next); - } else { - next(); - } - }, - ], callback); + User.updateDigestSetting = async function (uid, dailyDigestFreq) { + await db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid); + if (['day', 'week', 'month'].includes(dailyDigestFreq)) { + await db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid); + } }; - User.setSetting = function (uid, key, value, callback) { + User.setSetting = async function (uid, key, value) { if (parseInt(uid, 10) <= 0) { - return setImmediate(callback); + return; } - db.setObjectField('user:' + uid + ':settings', key, value, callback); + await db.setObjectField('user:' + uid + ':settings', key, value); }; }; diff --git a/src/user/topics.js b/src/user/topics.js index bd69e66e6f..470a01a477 100644 --- a/src/user/topics.js +++ b/src/user/topics.js @@ -1,17 +1,16 @@ 'use strict'; -var async = require('async'); -var db = require('../database'); +const db = require('../database'); module.exports = function (User) { - User.getIgnoredTids = function (uid, start, stop, callback) { - db.getSortedSetRevRange('uid:' + uid + ':ignored_tids', start, stop, callback); + User.getIgnoredTids = async function (uid, start, stop) { + return await db.getSortedSetRevRange('uid:' + uid + ':ignored_tids', start, stop); }; - User.addTopicIdToUser = function (uid, tid, timestamp, callback) { - async.parallel([ - async.apply(db.sortedSetAdd, 'uid:' + uid + ':topics', timestamp, tid), - async.apply(User.incrementUserFieldBy, uid, 'topiccount', 1), - ], callback); + User.addTopicIdToUser = async function (uid, tid, timestamp) { + await Promise.all([ + db.sortedSetAdd('uid:' + uid + ':topics', timestamp, tid), + User.incrementUserFieldBy(uid, 'topiccount', 1), + ]); }; }; diff --git a/src/user/uploads.js b/src/user/uploads.js index 450fe96d66..189760f73d 100644 --- a/src/user/uploads.js +++ b/src/user/uploads.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require('async'); var path = require('path'); var nconf = require('nconf'); var winston = require('winston'); @@ -10,40 +9,25 @@ var file = require('../file'); var batch = require('../batch'); module.exports = function (User) { - User.deleteUpload = function (callerUid, uid, uploadName, callback) { - async.waterfall([ - function (next) { - async.parallel({ - isUsersUpload: function (next) { - db.isSortedSetMember('uid:' + callerUid + ':uploads', uploadName, next); - }, - isAdminOrGlobalMod: function (next) { - User.isAdminOrGlobalMod(callerUid, next); - }, - }, next); - }, - function (results, next) { - if (!results.isAdminOrGlobalMod && !results.isUsersUpload) { - return next(new Error('[[error:no-privileges]]')); - } + User.deleteUpload = async function (callerUid, uid, uploadName) { + const [isUsersUpload, isAdminOrGlobalMod] = await Promise.all([ + db.isSortedSetMember('uid:' + callerUid + ':uploads', uploadName), + User.isAdminOrGlobalMod(callerUid), + ]); + if (!isAdminOrGlobalMod && !isUsersUpload) { + throw new Error('[[error:no-privileges]]'); + } - winston.verbose('[user/deleteUpload] Deleting ' + uploadName); - async.parallel([ - async.apply(file.delete, path.join(nconf.get('upload_path'), uploadName)), - async.apply(file.delete, path.join(nconf.get('upload_path'), path.dirname(uploadName), path.basename(uploadName, path.extname(uploadName)) + '-resized' + path.extname(uploadName))), - ], function (err) { - // Only return err, not the parallel'd result set - next(err); - }); - }, - function (next) { - db.sortedSetRemove('uid:' + uid + ':uploads', uploadName, next); - }, - ], callback); + winston.verbose('[user/deleteUpload] Deleting ' + uploadName); + await Promise.all([ + file.delete(path.join(nconf.get('upload_path'), uploadName)), + file.delete(path.join(nconf.get('upload_path'), path.dirname(uploadName), path.basename(uploadName, path.extname(uploadName)) + '-resized' + path.extname(uploadName))), + ]); + await db.sortedSetRemove('uid:' + uid + ':uploads', uploadName); }; - User.collateUploads = function (uid, archive, callback) { - batch.processSortedSet('uid:' + uid + ':uploads', function (files, next) { + User.collateUploads = async function (uid, archive) { + await batch.processSortedSet('uid:' + uid + ':uploads', function (files, next) { files.forEach(function (file) { archive.file(path.join(nconf.get('upload_path'), file), { name: path.basename(file), @@ -51,8 +35,6 @@ module.exports = function (User) { }); setImmediate(next); - }, function (err) { - callback(err); }); }; };