diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js index 0ebfd4b274..a6aa26ba4d 100644 --- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js +++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js @@ -1,107 +1,52 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ +'use strict'; -const async = require('async'); const winston = require('winston'); const db = require('../../database'); module.exports = { name: 'Granting edit/delete/delete topic on existing categories', timestamp: Date.UTC(2016, 7, 7), - method: function (callback) { + method: async function () { const groupsAPI = require('../../groups'); const privilegesAPI = require('../../privileges'); - db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => { - if (err) { - return callback(err); + const cids = await db.getSortedSetRange('categories:cid', 0, -1); + + for (const cid of cids) { + const data = await privilegesAPI.categories.list(cid); + const { groups, users } = data; + + for (const group of groups) { + if (group.privileges['groups:topics:reply']) { + await Promise.all([ + groupsAPI.join(`cid:${cid}:privileges:groups:posts:edit`, group.name), + groupsAPI.join(`cid:${cid}:privileges:groups:posts:delete`, group.name), + ]); + winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`); + } + + if (group.privileges['groups:topics:create']) { + await groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name); + winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`); + } } - async.eachSeries(cids, (cid, next) => { - privilegesAPI.categories.list(cid, (err, data) => { - if (err) { - return next(err); - } - - const { groups } = data; - const { users } = data; - - async.waterfall([ - function (next) { - async.eachSeries(groups, (group, next) => { - if (group.privileges['groups:topics:reply']) { - return async.parallel([ - async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:edit`, group.name), - async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:delete`, group.name), - ], (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:groups:posts:edit, cid:${cid}:privileges:groups:posts:delete granted to gid: ${group.name}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(groups, (group, next) => { - if (group.privileges['groups:topics:create']) { - return groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(users, (user, next) => { - if (user.privileges['topics:reply']) { - return async.parallel([ - async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:edit`, user.uid), - async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:delete`, user.uid), - ], (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - function (next) { - async.eachSeries(users, (user, next) => { - if (user.privileges['topics:create']) { - return groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid, (err) => { - if (!err) { - winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`); - } - - return next(err); - }); - } - - next(null); - }, next); - }, - ], (err) => { - if (!err) { - winston.verbose(`-- cid ${cid} upgraded`); - } - - next(err); - }); - }); - }, callback); - }); + for (const user of users) { + if (user.privileges['topics:reply']) { + await Promise.all([ + groupsAPI.join(`cid:${cid}:privileges:posts:edit`, user.uid), + groupsAPI.join(`cid:${cid}:privileges:posts:delete`, user.uid), + ]); + winston.verbose(`cid:${cid}:privileges:posts:edit, cid:${cid}:privileges:posts:delete granted to uid: ${user.uid}`); + } + if (user.privileges['topics:create']) { + await groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid); + winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`); + } + } + winston.verbose(`-- cid ${cid} upgraded`); + } }, }; diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 782b4774db..a08aa6a5d3 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -1,56 +1,39 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Favourites to Bookmarks', timestamp: Date.UTC(2016, 9, 8), - method: function (callback) { + method: async function () { const { progress } = this; + const batch = require('../../batch'); - function upgradePosts(next) { - const batch = require('../../batch'); - - batch.processSortedSet('posts:pid', (ids, next) => { - async.each(ids, (id, next) => { + async function upgradePosts() { + await batch.processSortedSet('posts:pid', async (ids) => { + await Promise.all(ids.map(async (id) => { progress.incr(); - - async.waterfall([ - function (next) { - db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`, next); - }, - function (next) { - db.getObjectField(`post:${id}`, 'reputation', next); - }, - function (reputation, next) { - if (parseInt(reputation, 10)) { - db.setObjectField(`post:${id}`, 'bookmarks', reputation, next); - } else { - next(); - } - }, - function (next) { - db.deleteObjectField(`post:${id}`, 'reputation', next); - }, - ], next); - }, next); + await db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`); + const reputation = await db.getObjectField(`post:${id}`, 'reputation'); + if (parseInt(reputation, 10)) { + await db.setObjectField(`post:${id}`, 'bookmarks', reputation); + } + await db.deleteObjectField(`post:${id}`, 'reputation'); + })); }, { progress: progress, - }, next); + }); } - function upgradeUsers(next) { - const batch = require('../../batch'); - - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (id, next) => { - db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`, next); - }, next); - }, {}, next); + async function upgradeUsers() { + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (id) => { + await db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`); + })); + }, {}); } - async.series([upgradePosts, upgradeUsers], callback); + await upgradePosts(); + await upgradeUsers(); }, }; diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js index f565d6c423..e18442e8f8 100644 --- a/src/upgrades/1.4.0/global_and_user_language_keys.js +++ b/src/upgrades/1.4.0/global_and_user_language_keys.js @@ -1,57 +1,37 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Update global and user language keys', timestamp: Date.UTC(2016, 10, 22), - method: function (callback) { + method: async function () { + const { progress } = this; const user = require('../../user'); const meta = require('../../meta'); const batch = require('../../batch'); - let newLanguage; - async.parallel([ - function (next) { - meta.configs.get('defaultLang', (err, defaultLang) => { - if (err) { - return next(err); - } - if (!defaultLang) { - return setImmediate(next); - } + const defaultLang = await meta.configs.get('defaultLang'); + if (defaultLang) { + const newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== defaultLang) { + await meta.configs.set('defaultLang', newLanguage); + } + } - newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== defaultLang) { - meta.configs.set('defaultLang', newLanguage, next); - } else { - setImmediate(next); + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (uid) => { + progress.incr(); + const language = await db.getObjectField(`user:${uid}:settings`, 'userLang'); + if (language) { + const newLanguage = language.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== language) { + await user.setSetting(uid, 'userLang', newLanguage); } - }); - }, - function (next) { - batch.processSortedSet('users:joindate', (ids, next) => { - async.each(ids, (uid, next) => { - async.waterfall([ - async.apply(db.getObjectField, `user:${uid}:settings`, 'userLang'), - function (language, next) { - if (!language) { - return setImmediate(next); - } - - newLanguage = language.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== language) { - user.setSetting(uid, 'userLang', newLanguage, next); - } else { - setImmediate(next); - } - }, - ], next); - }, next); - }, next); - }, - ], callback); + } + })); + }, { + progress: progress, + }); }, }; diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js index 5bf80c14fa..97b3c95670 100644 --- a/src/upgrades/1.4.4/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -1,36 +1,34 @@ 'use strict'; -const async = require('async'); -const db = require('../../database'); +const db = require('../../database'); module.exports = { name: 'Upgrading config urls to use assets route', timestamp: Date.UTC(2017, 1, 28), - method: function (callback) { - async.waterfall([ - function (cb) { - db.getObject('config', cb); - }, - function (config, cb) { - if (!config) { - return cb(); + method: async function () { + const config = await db.getObject('config'); + if (config) { + const keys = [ + 'brand:favicon', + 'brand:touchicon', + 'og:image', + 'brand:logo:url', + 'defaultAvatar', + 'profile:defaultCovers', + ]; + + keys.forEach((key) => { + const oldValue = config[key]; + + if (!oldValue || typeof oldValue !== 'string') { + return; } - const keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers']; - - keys.forEach((key) => { - const oldValue = config[key]; - - if (!oldValue || typeof oldValue !== 'string') { - return; - } - - config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); - }); + config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); + }); - db.setObject('config', config, cb); - }, - ], callback); + await db.setObject('config', config); + } }, }; diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index a257da1725..dc3a1f465c 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -1,7 +1,5 @@ 'use strict'; -const async = require('async'); - const nconf = require('nconf'); const db = require('../../database'); const batch = require('../../batch'); @@ -9,7 +7,7 @@ const batch = require('../../batch'); module.exports = { name: 'Delete accidentally long-lived sessions', timestamp: Date.UTC(2017, 3, 16), - method: function (callback) { + method: async function () { let configJSON; try { configJSON = require('../../../config.json') || { [process.env.database]: true }; @@ -20,44 +18,24 @@ module.exports = { const isRedisSessionStore = configJSON.hasOwnProperty('redis'); const { progress } = this; - async.waterfall([ - function (next) { - if (isRedisSessionStore) { - const connection = require('../../database/redis/connection'); - let client; - async.waterfall([ - function (next) { - connection.connect(nconf.get('redis'), next); - }, - function (_client, next) { - client = _client; - client.keys('sess:*', next); - }, - function (sessionKeys, next) { - progress.total = sessionKeys.length; + if (isRedisSessionStore) { + const connection = require('../../database/redis/connection'); + const client = await connection.connect(nconf.get('redis')); + const sessionKeys = await client.keys('sess:*'); + progress.total = sessionKeys.length; - batch.processArray(sessionKeys, (keys, next) => { - const multi = client.multi(); - keys.forEach((key) => { - progress.incr(); - multi.del(key); - }); - multi.exec(next); - }, { - batch: 1000, - }, next); - }, - ], (err) => { - next(err); - }); - } else if (db.client && db.client.collection) { - db.client.collection('sessions').deleteMany({}, {}, (err) => { - next(err); - }); - } else { - next(); - } - }, - ], callback); + await batch.processArray(sessionKeys, async (keys) => { + const multi = client.multi(); + keys.forEach((key) => { + progress.incr(); + multi.del(key); + }); + await multi.exec(); + }, { + batch: 1000, + }); + } else if (db.client && db.client.collection) { + await db.client.collection('sessions').deleteMany({}, {}); + } }, }; diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index f16d9a5224..dfbb28d70d 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -1,88 +1,56 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Migrating flags to new schema', timestamp: Date.UTC(2016, 11, 7), - method: function (callback) { + method: async function () { const batch = require('../../batch'); const posts = require('../../posts'); const flags = require('../../flags'); const { progress } = this; - batch.processSortedSet('posts:pid', (ids, next) => { - posts.getPostsByPids(ids, 1, (err, posts) => { - if (err) { - return next(err); - } - - posts = posts.filter(post => post.hasOwnProperty('flags')); - - async.each(posts, (post, next) => { - progress.incr(); - - async.parallel({ - uids: async.apply(db.getSortedSetRangeWithScores, `pid:${post.pid}:flag:uids`, 0, -1), - reasons: async.apply(db.getSortedSetRange, `pid:${post.pid}:flag:uid:reason`, 0, -1), - }, (err, data) => { - if (err) { - return next(err); + await batch.processSortedSet('posts:pid', async (ids) => { + let postData = await posts.getPostsByPids(ids, 1); + postData = postData.filter(post => post.hasOwnProperty('flags')); + await Promise.all(postData.map(async (post) => { + progress.incr(); + + const [uids, reasons] = await Promise.all([ + db.getSortedSetRangeWithScores(`pid:${post.pid}:flag:uids`, 0, -1), + db.getSortedSetRange(`pid:${post.pid}:flag:uid:reason`, 0, -1), + ]); + + // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) + if (uids.length && reasons.length) { + // Just take the first entry + const datetime = uids[0].score; + const reason = reasons[0].split(':')[1]; + + try { + const flagObj = await flags.create('post', post.pid, uids[0].value, reason, datetime); + if (post['flag:state'] || post['flag:assignee']) { + await flags.update(flagObj.flagId, 1, { + state: post['flag:state'], + assignee: post['flag:assignee'], + datetime: datetime, + }); } - - // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) - if (!data.uids.length || !data.reasons.length) { - return setImmediate(next); + if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { + let history = JSON.parse(post['flag:history']); + history = history.filter(event => event.type === 'notes')[0]; + await flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp); } - - // Just take the first entry - const datetime = data.uids[0].score; - const reason = data.reasons[0].split(':')[1]; - let flagObj; - - async.waterfall([ - async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), - function (_flagObj, next) { - flagObj = _flagObj; - if (post['flag:state'] || post['flag:assignee']) { - flags.update(flagObj.flagId, 1, { - state: post['flag:state'], - assignee: post['flag:assignee'], - datetime: datetime, - }, next); - } else { - setImmediate(next); - } - }, - function (next) { - if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { - try { - let history = JSON.parse(post['flag:history']); - history = history.filter(event => event.type === 'notes')[0]; - - flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next); - } catch (e) { - next(e); - } - } else { - setImmediate(next); - } - }, - ], (err) => { - if (err && err.message === '[[error:post-already-flagged]]') { - // Already flagged, no need to parse, but not an error - next(); - } else { - next(err); - } - }); - }); - }, next); - }); + } catch (err) { + if (err.message !== '[[error:post-already-flagged]]') { + throw err; + } + } + } + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js index 9cb34c5986..769ca20247 100644 --- a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js +++ b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js @@ -1,36 +1,26 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); const batch = require('../../batch'); - module.exports = { name: 'Remove relative_path from uploaded profile cover urls', timestamp: Date.UTC(2017, 3, 26), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (ids, done) => { - async.each(ids, (uid, cb) => { - async.waterfall([ - function (next) { - db.getObjectField(`user:${uid}`, 'cover:url', next); - }, - function (url, next) { - progress.incr(); - - if (!url) { - return next(); - } + await batch.processSortedSet('users:joindate', async (ids) => { + await Promise.all(ids.map(async (uid) => { + const url = await db.getObjectField(`user:${uid}`, 'cover:url'); + progress.incr(); - const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); - db.setObjectField(`user:${uid}`, 'cover:url', newUrl, next); - }, - ], cb); - }, done); + if (url) { + const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); + await db.setObjectField(`user:${uid}`, 'cover:url', newUrl); + } + })); }, { progress: this.progress, - }, callback); + }); }, }; diff --git a/src/upgrades/1.6.0/clear-stale-digest-template.js b/src/upgrades/1.6.0/clear-stale-digest-template.js index ff34668691..3bcd11b49c 100644 --- a/src/upgrades/1.6.0/clear-stale-digest-template.js +++ b/src/upgrades/1.6.0/clear-stale-digest-template.js @@ -1,31 +1,21 @@ 'use strict'; - -const async = require('async'); const crypto = require('crypto'); const meta = require('../../meta'); module.exports = { name: 'Clearing stale digest templates that were accidentally saved as custom', timestamp: Date.UTC(2017, 8, 6), - method: function (callback) { + method: async function () { const matches = [ '112e541b40023d6530dd44df4b0d9c5d', // digest @ 75917e25b3b5ad7bed8ed0c36433fb35c9ab33eb '110b8805f70395b0282fd10555059e9f', // digest @ 9b02bb8f51f0e47c6e335578f776ffc17bc03537 '9538e7249edb369b2a25b03f2bd3282b', // digest @ 3314ab4b83138c7ae579ac1f1f463098b8c2d414 ]; - - async.waterfall([ - async.apply(meta.configs.getFields, ['email:custom:digest']), - function (fieldset, next) { - const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null; - - if (matches.includes(hash)) { - meta.configs.remove('email:custom:digest', next); - } else { - setImmediate(next); - } - }, - ], callback); + const fieldset = await meta.configs.getFields(['email:custom:digest']); + const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null; + if (matches.includes(hash)) { + await meta.configs.remove('email:custom:digest'); + } }, }; diff --git a/src/upgrades/1.6.0/ipblacklist-fix.js b/src/upgrades/1.6.0/ipblacklist-fix.js index 7e3f13d506..000de231ba 100644 --- a/src/upgrades/1.6.0/ipblacklist-fix.js +++ b/src/upgrades/1.6.0/ipblacklist-fix.js @@ -1,25 +1,13 @@ 'use strict'; -const async = require('async'); - const db = require('../../database'); module.exports = { name: 'Changing ip blacklist storage to object', timestamp: Date.UTC(2017, 8, 7), - method: function (callback) { - let rules; - async.waterfall([ - function (next) { - db.get('ip-blacklist-rules', next); - }, - function (_rules, next) { - rules = _rules; - db.delete('ip-blacklist-rules', rules ? next : callback); - }, - function (next) { - db.setObject('ip-blacklist-rules', { rules: rules }, next); - }, - ], callback); + method: async function () { + const rules = await db.get('ip-blacklist-rules'); + await db.delete('ip-blacklist-rules'); + await db.setObject('ip-blacklist-rules', { rules: rules }); }, }; diff --git a/src/upgrades/1.6.0/robots-config-change.js b/src/upgrades/1.6.0/robots-config-change.js index 7efc789357..7e787389e7 100644 --- a/src/upgrades/1.6.0/robots-config-change.js +++ b/src/upgrades/1.6.0/robots-config-change.js @@ -1,35 +1,21 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Fix incorrect robots.txt schema', timestamp: Date.UTC(2017, 6, 10), - method: function (callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (config, next) { - if (!config) { - return callback(); - } - // fix mongo nested data - if (config.robots && config.robots.txt) { - db.setObjectField('config', 'robots:txt', config.robots.txt, next); - } else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) { - db.setObjectField('config', 'robots:txt', config['robots.txt'], next); - } else { - next(); - } - }, - function (next) { - db.deleteObjectField('config', 'robots', next); - }, - function (next) { - db.deleteObjectField('config', 'robots.txt', next); - }, - ], callback); + method: async function () { + const config = await db.getObject('config'); + if (config) { + // fix mongo nested data + if (config.robots && config.robots.txt) { + await db.setObjectField('config', 'robots:txt', config.robots.txt); + } else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) { + await db.setObjectField('config', 'robots:txt', config['robots.txt']); + } + await db.deleteObjectField('config', 'robots'); + await db.deleteObjectField('config', 'robots.txt'); + } }, }; diff --git a/src/upgrades/1.7.1/notification-settings.js b/src/upgrades/1.7.1/notification-settings.js index e7a455feb9..fed592effb 100644 --- a/src/upgrades/1.7.1/notification-settings.js +++ b/src/upgrades/1.7.1/notification-settings.js @@ -1,48 +1,31 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Convert old notification digest settings', timestamp: Date.UTC(2017, 10, 15), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('users:joindate', (uids, next) => { - async.eachLimit(uids, 500, (uid, next) => { + await batch.processSortedSet('users:joindate', async (uids) => { + await Promise.all(uids.map(async (uid) => { progress.incr(); - async.waterfall([ - function (next) { - db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next); - }, - function (userSettings, _next) { - if (!userSettings) { - return next(); - } - const tasks = []; - if (parseInt(userSettings.sendChatNotifications, 10) === 1) { - tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail')); - } - if (parseInt(userSettings.sendPostNotifications, 10) === 1) { - tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail')); - } - if (!tasks.length) { - return next(); - } - - async.series(tasks, (err) => { - _next(err); - }); - }, - function (next) { - db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next); - }, - ], next); - }, next); + const userSettings = await db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']); + if (userSettings) { + if (parseInt(userSettings.sendChatNotifications, 10) === 1) { + await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail'); + } + if (parseInt(userSettings.sendPostNotifications, 10) === 1) { + await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail'); + } + } + await db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']); + })); }, { progress: progress, - }, callback); + batch: 500, + }); }, }; diff --git a/src/upgrades/1.7.3/key_value_schema_change.js b/src/upgrades/1.7.3/key_value_schema_change.js index 997db02551..0fb38f4c44 100644 --- a/src/upgrades/1.7.3/key_value_schema_change.js +++ b/src/upgrades/1.7.3/key_value_schema_change.js @@ -1,13 +1,13 @@ -'use strict'; +/* eslint-disable no-await-in-loop */ -const async = require('async'); +'use strict'; const db = require('../../database'); module.exports = { name: 'Change the schema of simple keys so they don\'t use value field (mongodb only)', timestamp: Date.UTC(2017, 11, 18), - method: function (callback) { + method: async function () { let configJSON; try { configJSON = require('../../../config.json') || { [process.env.database]: true, database: process.env.database }; @@ -17,56 +17,29 @@ module.exports = { const isMongo = configJSON.hasOwnProperty('mongo') && configJSON.database === 'mongo'; const { progress } = this; if (!isMongo) { - return callback(); + return; } const { client } = db; - let cursor; - async.waterfall([ - function (next) { - client.collection('objects').countDocuments({ - _key: { $exists: true }, - value: { $exists: true }, - score: { $exists: false }, - }, next); - }, - function (count, next) { - progress.total = count; - cursor = client.collection('objects').find({ - _key: { $exists: true }, - value: { $exists: true }, - score: { $exists: false }, - }).batchSize(1000); + const query = { + _key: { $exists: true }, + value: { $exists: true }, + score: { $exists: false }, + }; + progress.total = await client.collection('objects').countDocuments(query); + const cursor = await client.collection('objects').find(query).batchSize(1000); - let done = false; - async.whilst( - (next) => { - next(null, !done); - }, - (next) => { - async.waterfall([ - function (next) { - cursor.next(next); - }, - function (item, next) { - progress.incr(); - if (item === null) { - done = true; - return next(); - } - delete item.expireAt; - if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { - client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - next - ); - }, - ], callback); + let done = false; + while (!done) { + const item = await cursor.next(); + progress.incr(); + if (item === null) { + done = true; + } else { + delete item.expireAt; + if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { + await client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }); + } + } + } }, }; diff --git a/src/upgrades/1.7.3/topic_votes.js b/src/upgrades/1.7.3/topic_votes.js index f7c5d0c122..008aaece0a 100644 --- a/src/upgrades/1.7.3/topic_votes.js +++ b/src/upgrades/1.7.3/topic_votes.js @@ -1,34 +1,22 @@ 'use strict'; -const async = require('async'); + const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Add votes to topics', timestamp: Date.UTC(2017, 11, 8), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.mainPid || !topicData.cid) { - return _next(); - } - db.getObject(`post:${topicData.mainPid}`, next); - }, - function (postData, next) { - if (!postData) { - return _next(); - } + const topicData = await db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned']); + if (topicData.mainPid && topicData.cid) { + const postData = await db.getObject(`post:${topicData.mainPid}`); + if (postData) { const upvotes = parseInt(postData.upvotes, 10) || 0; const downvotes = parseInt(postData.downvotes, 10) || 0; const data = { @@ -36,29 +24,19 @@ module.exports = { downvotes: downvotes, }; const votes = upvotes - downvotes; - async.parallel([ - function (next) { - db.setObject(`topic:${tid}`, data, next); - }, - function (next) { - db.sortedSetAdd('topics:votes', votes, tid, next); - }, - function (next) { - if (parseInt(topicData.pinned, 10) !== 1) { - db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - ], _next); - }, next); + await Promise.all([ + db.setObject(`topic:${tid}`, data), + db.sortedSetAdd('topics:votes', votes, tid), + ]); + if (parseInt(topicData.pinned, 10) !== 1) { + await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid); + } + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js index bb183557ef..33aafcb70d 100644 --- a/src/upgrades/1.7.4/fix_moved_topics_byvotes.js +++ b/src/upgrades/1.7.4/fix_moved_topics_byvotes.js @@ -1,53 +1,31 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Fix sort by votes for moved topics', timestamp: Date.UTC(2018, 0, 8), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + await batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.cid || !topicData.oldCid) { - return _next(); - } - - const upvotes = parseInt(topicData.upvotes, 10) || 0; - const downvotes = parseInt(topicData.downvotes, 10) || 0; - const votes = upvotes - downvotes; - - async.series([ - function (next) { - db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid, next); - }, - function (next) { - if (parseInt(topicData.pinned, 10) !== 1) { - db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); - } else { - next(); - } - }, - ], (err) => { - next(err); - }); - }, - ], _next); - }, next); + const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned']); + if (topicData.cid && topicData.oldCid) { + const upvotes = parseInt(topicData.upvotes, 10) || 0; + const downvotes = parseInt(topicData.downvotes, 10) || 0; + const votes = upvotes - downvotes; + await db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid); + if (parseInt(topicData.pinned, 10) !== 1) { + await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid); + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.4/fix_user_topics_per_category.js b/src/upgrades/1.7.4/fix_user_topics_per_category.js index 73b6671864..5ee19fb41f 100644 --- a/src/upgrades/1.7.4/fix_user_topics_per_category.js +++ b/src/upgrades/1.7.4/fix_user_topics_per_category.js @@ -1,52 +1,29 @@ 'use strict'; -const async = require('async'); const batch = require('../../batch'); const db = require('../../database'); module.exports = { name: 'Fix topics in categories per user if they were moved', timestamp: Date.UTC(2018, 0, 22), - method: function (callback) { + method: async function () { const { progress } = this; - batch.processSortedSet('topics:tid', (tids, next) => { - async.eachLimit(tids, 500, (tid, _next) => { + await batch.processSortedSet('topics:tid', async (tids) => { + await Promise.all(tids.map(async (tid) => { progress.incr(); - let topicData; - async.waterfall([ - function (next) { - db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp'], next); - }, - function (_topicData, next) { - topicData = _topicData; - if (!topicData.cid || !topicData.oldCid) { - return _next(); - } - - db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid, next); - }, - function (isMember, next) { - if (isMember) { - async.series([ - function (next) { - db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid, next); - }, - function (next) { - db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid, next); - }, - ], (err) => { - next(err); - }); - } else { - next(); - } - }, - ], _next); - }, next); + const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp']); + if (topicData.cid && topicData.oldCid) { + const isMember = await db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid); + if (isMember) { + await db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid); + await db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid); + } + } + })); }, { progress: progress, batch: 500, - }, callback); + }); }, }; diff --git a/src/upgrades/1.7.6/flatten_navigation_data.js b/src/upgrades/1.7.6/flatten_navigation_data.js index e5cb5932b8..96dc6408ae 100644 --- a/src/upgrades/1.7.6/flatten_navigation_data.js +++ b/src/upgrades/1.7.6/flatten_navigation_data.js @@ -1,38 +1,24 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Flatten navigation data', timestamp: Date.UTC(2018, 1, 17), - method: function (callback) { - async.waterfall([ - function (next) { - db.getSortedSetRangeWithScores('navigation:enabled', 0, -1, next); - }, - function (data, next) { - const order = []; - const items = []; - data.forEach((item) => { - let navItem = JSON.parse(item.value); - const keys = Object.keys(navItem); - if (keys.length && parseInt(keys[0], 10) >= 0) { - navItem = navItem[keys[0]]; - } - order.push(item.score); - items.push(JSON.stringify(navItem)); - }); - - async.series([ - function (next) { - db.delete('navigation:enabled', next); - }, - function (next) { - db.sortedSetAdd('navigation:enabled', order, items, next); - }, - ], next); - }, - ], callback); + method: async function () { + const data = await db.getSortedSetRangeWithScores('navigation:enabled', 0, -1); + const order = []; + const items = []; + data.forEach((item) => { + let navItem = JSON.parse(item.value); + const keys = Object.keys(navItem); + if (keys.length && parseInt(keys[0], 10) >= 0) { + navItem = navItem[keys[0]]; + } + order.push(item.score); + items.push(JSON.stringify(navItem)); + }); + await db.delete('navigation:enabled'); + await db.sortedSetAdd('navigation:enabled', order, items); }, }; diff --git a/src/upgrades/1.7.6/notification_types.js b/src/upgrades/1.7.6/notification_types.js index 8d022a157b..42d59cdd38 100644 --- a/src/upgrades/1.7.6/notification_types.js +++ b/src/upgrades/1.7.6/notification_types.js @@ -1,28 +1,21 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); module.exports = { name: 'Add default settings for notification delivery types', timestamp: Date.UTC(2018, 1, 14), - method: function (callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (config, next) { - const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none'; - const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none'; - db.setObject('config', { - notificationType_upvote: config.notificationType_upvote || 'notification', - 'notificationType_new-topic': config['notificationType_new-topic'] || 'notification', - 'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications, - notificationType_follow: config.notificationType_follow || 'notification', - 'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications, - 'notificationType_group-invite': config['notificationType_group-invite'] || 'notification', - }, next); - }, - ], callback); + method: async function () { + const config = await db.getObject('config'); + const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none'; + const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none'; + await db.setObject('config', { + notificationType_upvote: config.notificationType_upvote || 'notification', + 'notificationType_new-topic': config['notificationType_new-topic'] || 'notification', + 'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications, + notificationType_follow: config.notificationType_follow || 'notification', + 'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications, + 'notificationType_group-invite': config['notificationType_group-invite'] || 'notification', + }); }, }; diff --git a/src/upgrades/1.7.6/update_min_pass_strength.js b/src/upgrades/1.7.6/update_min_pass_strength.js index 5afdb4f3ec..b207e0c166 100644 --- a/src/upgrades/1.7.6/update_min_pass_strength.js +++ b/src/upgrades/1.7.6/update_min_pass_strength.js @@ -1,22 +1,14 @@ 'use strict'; -const async = require('async'); const db = require('../../database'); - module.exports = { name: 'Revising minimum password strength to 1 (from 0)', timestamp: Date.UTC(2018, 1, 21), - method: function (callback) { - async.waterfall([ - async.apply(db.getObjectField.bind(db), 'config', 'minimumPasswordStrength'), - function (strength, next) { - if (!strength) { - return db.setObjectField('config', 'minimumPasswordStrength', 1, next); - } - - setImmediate(next); - }, - ], callback); + method: async function () { + const strength = await db.getObjectField('config', 'minimumPasswordStrength'); + if (!strength) { + await db.setObjectField('config', 'minimumPasswordStrength', 1); + } }, };