refactor: remove async.waterfall from remaining upgrade scripts

isekai-main
Barış Soner Uşaklı 4 years ago
parent 04ee5c0dfb
commit 6b34065f2a

@ -1,107 +1,52 @@
'use strict'; /* eslint-disable no-await-in-loop */
'use strict';
const async = require('async');
const winston = require('winston'); const winston = require('winston');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Granting edit/delete/delete topic on existing categories', name: 'Granting edit/delete/delete topic on existing categories',
timestamp: Date.UTC(2016, 7, 7), timestamp: Date.UTC(2016, 7, 7),
method: function (callback) { method: async function () {
const groupsAPI = require('../../groups'); const groupsAPI = require('../../groups');
const privilegesAPI = require('../../privileges'); const privilegesAPI = require('../../privileges');
db.getSortedSetRange('categories:cid', 0, -1, (err, cids) => { const cids = await db.getSortedSetRange('categories:cid', 0, -1);
if (err) {
return callback(err);
}
async.eachSeries(cids, (cid, next) => {
privilegesAPI.categories.list(cid, (err, data) => {
if (err) {
return next(err);
}
const { groups } = data; for (const cid of cids) {
const { users } = data; const data = await privilegesAPI.categories.list(cid);
const { groups, users } = data;
async.waterfall([ for (const group of groups) {
function (next) {
async.eachSeries(groups, (group, next) => {
if (group.privileges['groups:topics:reply']) { if (group.privileges['groups:topics:reply']) {
return async.parallel([ await Promise.all([
async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:edit`, group.name), groupsAPI.join(`cid:${cid}:privileges:groups:posts:edit`, group.name),
async.apply(groupsAPI.join, `cid:${cid}:privileges:groups:posts:delete`, group.name), 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}`); 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']) { if (group.privileges['groups:topics:create']) {
return groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name, (err) => { await groupsAPI.join(`cid:${cid}:privileges:groups:topics:delete`, group.name);
if (!err) {
winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`); winston.verbose(`cid:${cid}:privileges:groups:topics:delete granted to gid: ${group.name}`);
} }
return next(err);
});
} }
next(null); for (const user of users) {
}, next);
},
function (next) {
async.eachSeries(users, (user, next) => {
if (user.privileges['topics:reply']) { if (user.privileges['topics:reply']) {
return async.parallel([ await Promise.all([
async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:edit`, user.uid), groupsAPI.join(`cid:${cid}:privileges:posts:edit`, user.uid),
async.apply(groupsAPI.join, `cid:${cid}:privileges:posts:delete`, user.uid), 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}`); 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']) { if (user.privileges['topics:create']) {
return groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid, (err) => { await groupsAPI.join(`cid:${cid}:privileges:topics:delete`, user.uid);
if (!err) {
winston.verbose(`cid:${cid}:privileges:topics:delete granted to uid: ${user.uid}`); 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`); winston.verbose(`-- cid ${cid} upgraded`);
} }
next(err);
});
});
}, callback);
});
}, },
}; };

@ -1,56 +1,39 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Favourites to Bookmarks', name: 'Favourites to Bookmarks',
timestamp: Date.UTC(2016, 9, 8), timestamp: Date.UTC(2016, 9, 8),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
function upgradePosts(next) {
const batch = require('../../batch'); const batch = require('../../batch');
batch.processSortedSet('posts:pid', (ids, next) => { async function upgradePosts() {
async.each(ids, (id, next) => { await batch.processSortedSet('posts:pid', async (ids) => {
await Promise.all(ids.map(async (id) => {
progress.incr(); progress.incr();
await db.rename(`pid:${id}:users_favourited`, `pid:${id}:users_bookmarked`);
async.waterfall([ const reputation = await db.getObjectField(`post:${id}`, 'reputation');
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)) { if (parseInt(reputation, 10)) {
db.setObjectField(`post:${id}`, 'bookmarks', reputation, next); await db.setObjectField(`post:${id}`, 'bookmarks', reputation);
} else {
next();
} }
}, await db.deleteObjectField(`post:${id}`, 'reputation');
function (next) { }));
db.deleteObjectField(`post:${id}`, 'reputation', next);
},
], next);
}, next);
}, { }, {
progress: progress, progress: progress,
}, next); });
} }
function upgradeUsers(next) { async function upgradeUsers() {
const batch = require('../../batch'); await batch.processSortedSet('users:joindate', async (ids) => {
await Promise.all(ids.map(async (id) => {
batch.processSortedSet('users:joindate', (ids, next) => { await db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`);
async.each(ids, (id, next) => { }));
db.rename(`uid:${id}:favourites`, `uid:${id}:bookmarks`, next); }, {});
}, next);
}, {}, next);
} }
async.series([upgradePosts, upgradeUsers], callback); await upgradePosts();
await upgradeUsers();
}, },
}; };

@ -1,57 +1,37 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Update global and user language keys', name: 'Update global and user language keys',
timestamp: Date.UTC(2016, 10, 22), timestamp: Date.UTC(2016, 10, 22),
method: function (callback) { method: async function () {
const { progress } = this;
const user = require('../../user'); const user = require('../../user');
const meta = require('../../meta'); const meta = require('../../meta');
const batch = require('../../batch'); const batch = require('../../batch');
let newLanguage;
async.parallel([
function (next) {
meta.configs.get('defaultLang', (err, defaultLang) => {
if (err) {
return next(err);
}
if (!defaultLang) { const defaultLang = await meta.configs.get('defaultLang');
return setImmediate(next); if (defaultLang) {
} const newLanguage = defaultLang.replace('_', '-').replace('@', '-x-');
newLanguage = defaultLang.replace('_', '-').replace('@', '-x-');
if (newLanguage !== defaultLang) { if (newLanguage !== defaultLang) {
meta.configs.set('defaultLang', newLanguage, next); await meta.configs.set('defaultLang', newLanguage);
} else {
setImmediate(next);
} }
});
},
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-'); 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) { if (newLanguage !== language) {
user.setSetting(uid, 'userLang', newLanguage, next); await user.setSetting(uid, 'userLang', newLanguage);
} else {
setImmediate(next);
} }
}, }
], next); }));
}, next); }, {
}, next); progress: progress,
}, });
], callback);
}, },
}; };

@ -1,23 +1,22 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database');
const db = require('../../database');
module.exports = { module.exports = {
name: 'Upgrading config urls to use assets route', name: 'Upgrading config urls to use assets route',
timestamp: Date.UTC(2017, 1, 28), timestamp: Date.UTC(2017, 1, 28),
method: function (callback) { method: async function () {
async.waterfall([ const config = await db.getObject('config');
function (cb) { if (config) {
db.getObject('config', cb); const keys = [
}, 'brand:favicon',
function (config, cb) { 'brand:touchicon',
if (!config) { 'og:image',
return cb(); 'brand:logo:url',
} 'defaultAvatar',
'profile:defaultCovers',
const keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers']; ];
keys.forEach((key) => { keys.forEach((key) => {
const oldValue = config[key]; const oldValue = config[key];
@ -29,8 +28,7 @@ module.exports = {
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); await db.setObject('config', config);
}, }
], callback);
}, },
}; };

@ -1,7 +1,5 @@
'use strict'; 'use strict';
const async = require('async');
const nconf = require('nconf'); const nconf = require('nconf');
const db = require('../../database'); const db = require('../../database');
const batch = require('../../batch'); const batch = require('../../batch');
@ -9,7 +7,7 @@ const batch = require('../../batch');
module.exports = { module.exports = {
name: 'Delete accidentally long-lived sessions', name: 'Delete accidentally long-lived sessions',
timestamp: Date.UTC(2017, 3, 16), timestamp: Date.UTC(2017, 3, 16),
method: function (callback) { method: async function () {
let configJSON; let configJSON;
try { try {
configJSON = require('../../../config.json') || { [process.env.database]: true }; configJSON = require('../../../config.json') || { [process.env.database]: true };
@ -20,44 +18,24 @@ module.exports = {
const isRedisSessionStore = configJSON.hasOwnProperty('redis'); const isRedisSessionStore = configJSON.hasOwnProperty('redis');
const { progress } = this; const { progress } = this;
async.waterfall([
function (next) {
if (isRedisSessionStore) { if (isRedisSessionStore) {
const connection = require('../../database/redis/connection'); const connection = require('../../database/redis/connection');
let client; const client = await connection.connect(nconf.get('redis'));
async.waterfall([ const sessionKeys = await client.keys('sess:*');
function (next) {
connection.connect(nconf.get('redis'), next);
},
function (_client, next) {
client = _client;
client.keys('sess:*', next);
},
function (sessionKeys, next) {
progress.total = sessionKeys.length; progress.total = sessionKeys.length;
batch.processArray(sessionKeys, (keys, next) => { await batch.processArray(sessionKeys, async (keys) => {
const multi = client.multi(); const multi = client.multi();
keys.forEach((key) => { keys.forEach((key) => {
progress.incr(); progress.incr();
multi.del(key); multi.del(key);
}); });
multi.exec(next); await multi.exec();
}, { }, {
batch: 1000, batch: 1000,
}, next);
},
], (err) => {
next(err);
}); });
} else if (db.client && db.client.collection) { } else if (db.client && db.client.collection) {
db.client.collection('sessions').deleteMany({}, {}, (err) => { await db.client.collection('sessions').deleteMany({}, {});
next(err);
});
} else {
next();
} }
}, },
], callback);
},
}; };

@ -1,88 +1,56 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Migrating flags to new schema', name: 'Migrating flags to new schema',
timestamp: Date.UTC(2016, 11, 7), timestamp: Date.UTC(2016, 11, 7),
method: function (callback) { method: async function () {
const batch = require('../../batch'); const batch = require('../../batch');
const posts = require('../../posts'); const posts = require('../../posts');
const flags = require('../../flags'); const flags = require('../../flags');
const { progress } = this; const { progress } = this;
batch.processSortedSet('posts:pid', (ids, next) => { await batch.processSortedSet('posts:pid', async (ids) => {
posts.getPostsByPids(ids, 1, (err, posts) => { let postData = await posts.getPostsByPids(ids, 1);
if (err) { postData = postData.filter(post => post.hasOwnProperty('flags'));
return next(err); await Promise.all(postData.map(async (post) => {
}
posts = posts.filter(post => post.hasOwnProperty('flags'));
async.each(posts, (post, next) => {
progress.incr(); progress.incr();
async.parallel({ const [uids, reasons] = await Promise.all([
uids: async.apply(db.getSortedSetRangeWithScores, `pid:${post.pid}:flag:uids`, 0, -1), db.getSortedSetRangeWithScores(`pid:${post.pid}:flag:uids`, 0, -1),
reasons: async.apply(db.getSortedSetRange, `pid:${post.pid}:flag:uid:reason`, 0, -1), db.getSortedSetRange(`pid:${post.pid}:flag:uid:reason`, 0, -1),
}, (err, data) => { ]);
if (err) {
return next(err);
}
// Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) // 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) { if (uids.length && reasons.length) {
return setImmediate(next);
}
// Just take the first entry // Just take the first entry
const datetime = data.uids[0].score; const datetime = uids[0].score;
const reason = data.reasons[0].split(':')[1]; const reason = reasons[0].split(':')[1];
let flagObj;
async.waterfall([ try {
async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), const flagObj = await flags.create('post', post.pid, uids[0].value, reason, datetime);
function (_flagObj, next) {
flagObj = _flagObj;
if (post['flag:state'] || post['flag:assignee']) { if (post['flag:state'] || post['flag:assignee']) {
flags.update(flagObj.flagId, 1, { await flags.update(flagObj.flagId, 1, {
state: post['flag:state'], state: post['flag:state'],
assignee: post['flag:assignee'], assignee: post['flag:assignee'],
datetime: datetime, datetime: datetime,
}, next); });
} else {
setImmediate(next);
} }
},
function (next) {
if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) {
try {
let history = JSON.parse(post['flag:history']); let history = JSON.parse(post['flag:history']);
history = history.filter(event => event.type === 'notes')[0]; history = history.filter(event => event.type === 'notes')[0];
await flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp);
flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next);
} catch (e) {
next(e);
} }
} else { } catch (err) {
setImmediate(next); if (err.message !== '[[error:post-already-flagged]]') {
throw err;
} }
},
], (err) => {
if (err && err.message === '[[error:post-already-flagged]]') {
// Already flagged, no need to parse, but not an error
next();
} else {
next(err);
} }
}); }
}); }));
}, next);
});
}, { }, {
progress: this.progress, progress: this.progress,
}, callback); });
}, },
}; };

@ -1,36 +1,26 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
const batch = require('../../batch'); const batch = require('../../batch');
module.exports = { module.exports = {
name: 'Remove relative_path from uploaded profile cover urls', name: 'Remove relative_path from uploaded profile cover urls',
timestamp: Date.UTC(2017, 3, 26), timestamp: Date.UTC(2017, 3, 26),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
batch.processSortedSet('users:joindate', (ids, done) => { await batch.processSortedSet('users:joindate', async (ids) => {
async.each(ids, (uid, cb) => { await Promise.all(ids.map(async (uid) => {
async.waterfall([ const url = await db.getObjectField(`user:${uid}`, 'cover:url');
function (next) {
db.getObjectField(`user:${uid}`, 'cover:url', next);
},
function (url, next) {
progress.incr(); progress.incr();
if (!url) { if (url) {
return next();
}
const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); const newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/');
db.setObjectField(`user:${uid}`, 'cover:url', newUrl, next); await db.setObjectField(`user:${uid}`, 'cover:url', newUrl);
}, }
], cb); }));
}, done);
}, { }, {
progress: this.progress, progress: this.progress,
}, callback); });
}, },
}; };

@ -1,31 +1,21 @@
'use strict'; 'use strict';
const async = require('async');
const crypto = require('crypto'); const crypto = require('crypto');
const meta = require('../../meta'); const meta = require('../../meta');
module.exports = { module.exports = {
name: 'Clearing stale digest templates that were accidentally saved as custom', name: 'Clearing stale digest templates that were accidentally saved as custom',
timestamp: Date.UTC(2017, 8, 6), timestamp: Date.UTC(2017, 8, 6),
method: function (callback) { method: async function () {
const matches = [ const matches = [
'112e541b40023d6530dd44df4b0d9c5d', // digest @ 75917e25b3b5ad7bed8ed0c36433fb35c9ab33eb '112e541b40023d6530dd44df4b0d9c5d', // digest @ 75917e25b3b5ad7bed8ed0c36433fb35c9ab33eb
'110b8805f70395b0282fd10555059e9f', // digest @ 9b02bb8f51f0e47c6e335578f776ffc17bc03537 '110b8805f70395b0282fd10555059e9f', // digest @ 9b02bb8f51f0e47c6e335578f776ffc17bc03537
'9538e7249edb369b2a25b03f2bd3282b', // digest @ 3314ab4b83138c7ae579ac1f1f463098b8c2d414 '9538e7249edb369b2a25b03f2bd3282b', // digest @ 3314ab4b83138c7ae579ac1f1f463098b8c2d414
]; ];
const fieldset = await meta.configs.getFields(['email:custom:digest']);
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; const hash = fieldset['email:custom:digest'] ? crypto.createHash('md5').update(fieldset['email:custom:digest']).digest('hex') : null;
if (matches.includes(hash)) { if (matches.includes(hash)) {
meta.configs.remove('email:custom:digest', next); await meta.configs.remove('email:custom:digest');
} else {
setImmediate(next);
} }
}, },
], callback);
},
}; };

@ -1,25 +1,13 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Changing ip blacklist storage to object', name: 'Changing ip blacklist storage to object',
timestamp: Date.UTC(2017, 8, 7), timestamp: Date.UTC(2017, 8, 7),
method: function (callback) { method: async function () {
let rules; const rules = await db.get('ip-blacklist-rules');
async.waterfall([ await db.delete('ip-blacklist-rules');
function (next) { await db.setObject('ip-blacklist-rules', { rules: rules });
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);
}, },
}; };

@ -1,35 +1,21 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Fix incorrect robots.txt schema', name: 'Fix incorrect robots.txt schema',
timestamp: Date.UTC(2017, 6, 10), timestamp: Date.UTC(2017, 6, 10),
method: function (callback) { method: async function () {
async.waterfall([ const config = await db.getObject('config');
function (next) { if (config) {
db.getObject('config', next);
},
function (config, next) {
if (!config) {
return callback();
}
// fix mongo nested data // fix mongo nested data
if (config.robots && config.robots.txt) { if (config.robots && config.robots.txt) {
db.setObjectField('config', 'robots:txt', config.robots.txt, next); await db.setObjectField('config', 'robots:txt', config.robots.txt);
} else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) { } else if (typeof config['robots.txt'] === 'string' && config['robots.txt']) {
db.setObjectField('config', 'robots:txt', config['robots.txt'], next); await db.setObjectField('config', 'robots:txt', config['robots.txt']);
} else { }
next(); await db.deleteObjectField('config', 'robots');
await db.deleteObjectField('config', 'robots.txt');
} }
},
function (next) {
db.deleteObjectField('config', 'robots', next);
},
function (next) {
db.deleteObjectField('config', 'robots.txt', next);
},
], callback);
}, },
}; };

@ -1,48 +1,31 @@
'use strict'; 'use strict';
const async = require('async');
const batch = require('../../batch'); const batch = require('../../batch');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Convert old notification digest settings', name: 'Convert old notification digest settings',
timestamp: Date.UTC(2017, 10, 15), timestamp: Date.UTC(2017, 10, 15),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
batch.processSortedSet('users:joindate', (uids, next) => { await batch.processSortedSet('users:joindate', async (uids) => {
async.eachLimit(uids, 500, (uid, next) => { await Promise.all(uids.map(async (uid) => {
progress.incr(); progress.incr();
async.waterfall([ const userSettings = await db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']);
function (next) { if (userSettings) {
db.getObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next);
},
function (userSettings, _next) {
if (!userSettings) {
return next();
}
const tasks = [];
if (parseInt(userSettings.sendChatNotifications, 10) === 1) { if (parseInt(userSettings.sendChatNotifications, 10) === 1) {
tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail')); await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-chat', 'notificationemail');
} }
if (parseInt(userSettings.sendPostNotifications, 10) === 1) { if (parseInt(userSettings.sendPostNotifications, 10) === 1) {
tasks.push(async.apply(db.setObjectField, `user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail')); await db.setObjectField(`user:${uid}:settings`, 'notificationType_new-reply', 'notificationemail');
} }
if (!tasks.length) {
return next();
} }
await db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications']);
async.series(tasks, (err) => { }));
_next(err);
});
},
function (next) {
db.deleteObjectFields(`user:${uid}:settings`, ['sendChatNotifications', 'sendPostNotifications'], next);
},
], next);
}, next);
}, { }, {
progress: progress, progress: progress,
}, callback); batch: 500,
});
}, },
}; };

@ -1,13 +1,13 @@
'use strict'; /* eslint-disable no-await-in-loop */
const async = require('async'); 'use strict';
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Change the schema of simple keys so they don\'t use value field (mongodb only)', name: 'Change the schema of simple keys so they don\'t use value field (mongodb only)',
timestamp: Date.UTC(2017, 11, 18), timestamp: Date.UTC(2017, 11, 18),
method: function (callback) { method: async function () {
let configJSON; let configJSON;
try { try {
configJSON = require('../../../config.json') || { [process.env.database]: true, database: process.env.database }; 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 isMongo = configJSON.hasOwnProperty('mongo') && configJSON.database === 'mongo';
const { progress } = this; const { progress } = this;
if (!isMongo) { if (!isMongo) {
return callback(); return;
} }
const { client } = db; const { client } = db;
let cursor; const query = {
async.waterfall([
function (next) {
client.collection('objects').countDocuments({
_key: { $exists: true }, _key: { $exists: true },
value: { $exists: true }, value: { $exists: true },
score: { $exists: false }, score: { $exists: false },
}, next); };
}, progress.total = await client.collection('objects').countDocuments(query);
function (count, next) { const cursor = await client.collection('objects').find(query).batchSize(1000);
progress.total = count;
cursor = client.collection('objects').find({
_key: { $exists: true },
value: { $exists: true },
score: { $exists: false },
}).batchSize(1000);
let done = false; let done = false;
async.whilst( while (!done) {
(next) => { const item = await cursor.next();
next(null, !done);
},
(next) => {
async.waterfall([
function (next) {
cursor.next(next);
},
function (item, next) {
progress.incr(); progress.incr();
if (item === null) { if (item === null) {
done = true; done = true;
return next(); } else {
}
delete item.expireAt; delete item.expireAt;
if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) {
client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } }, next); await client.collection('objects').updateOne({ _key: item._key }, { $rename: { value: 'data' } });
} else { }
next(); }
} }
},
], (err) => {
next(err);
});
},
next
);
},
], callback);
}, },
}; };

@ -1,34 +1,22 @@
'use strict'; 'use strict';
const async = require('async');
const batch = require('../../batch'); const batch = require('../../batch');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Add votes to topics', name: 'Add votes to topics',
timestamp: Date.UTC(2017, 11, 8), timestamp: Date.UTC(2017, 11, 8),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
batch.processSortedSet('topics:tid', (tids, next) => { batch.processSortedSet('topics:tid', async (tids) => {
async.eachLimit(tids, 500, (tid, _next) => { await Promise.all(tids.map(async (tid) => {
progress.incr(); progress.incr();
let topicData; const topicData = await db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned']);
async.waterfall([ if (topicData.mainPid && topicData.cid) {
function (next) { const postData = await db.getObject(`post:${topicData.mainPid}`);
db.getObjectFields(`topic:${tid}`, ['mainPid', 'cid', 'pinned'], next); if (postData) {
},
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 upvotes = parseInt(postData.upvotes, 10) || 0; const upvotes = parseInt(postData.upvotes, 10) || 0;
const downvotes = parseInt(postData.downvotes, 10) || 0; const downvotes = parseInt(postData.downvotes, 10) || 0;
const data = { const data = {
@ -36,29 +24,19 @@ module.exports = {
downvotes: downvotes, downvotes: downvotes,
}; };
const votes = upvotes - downvotes; const votes = upvotes - downvotes;
async.parallel([ await Promise.all([
function (next) { db.setObject(`topic:${tid}`, data),
db.setObject(`topic:${tid}`, data, next); db.sortedSetAdd('topics:votes', votes, tid),
}, ]);
function (next) {
db.sortedSetAdd('topics:votes', votes, tid, next);
},
function (next) {
if (parseInt(topicData.pinned, 10) !== 1) { if (parseInt(topicData.pinned, 10) !== 1) {
db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid);
} else {
next();
} }
}, }
], (err) => { }
next(err); }));
});
},
], _next);
}, next);
}, { }, {
progress: progress, progress: progress,
batch: 500, batch: 500,
}, callback); });
}, },
}; };

@ -1,53 +1,31 @@
'use strict'; 'use strict';
const async = require('async');
const batch = require('../../batch'); const batch = require('../../batch');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Fix sort by votes for moved topics', name: 'Fix sort by votes for moved topics',
timestamp: Date.UTC(2018, 0, 8), timestamp: Date.UTC(2018, 0, 8),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
batch.processSortedSet('topics:tid', (tids, next) => { await batch.processSortedSet('topics:tid', async (tids) => {
async.eachLimit(tids, 500, (tid, _next) => { await Promise.all(tids.map(async (tid) => {
progress.incr(); progress.incr();
let topicData; const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'oldCid', 'upvotes', 'downvotes', 'pinned']);
async.waterfall([ if (topicData.cid && topicData.oldCid) {
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 upvotes = parseInt(topicData.upvotes, 10) || 0;
const downvotes = parseInt(topicData.downvotes, 10) || 0; const downvotes = parseInt(topicData.downvotes, 10) || 0;
const votes = upvotes - downvotes; const votes = upvotes - downvotes;
await db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid);
async.series([
function (next) {
db.sortedSetRemove(`cid:${topicData.oldCid}:tids:votes`, tid, next);
},
function (next) {
if (parseInt(topicData.pinned, 10) !== 1) { if (parseInt(topicData.pinned, 10) !== 1) {
db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid, next); await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid);
} else {
next();
} }
}, }
], (err) => { }));
next(err);
});
},
], _next);
}, next);
}, { }, {
progress: progress, progress: progress,
batch: 500, batch: 500,
}, callback); });
}, },
}; };

@ -1,52 +1,29 @@
'use strict'; 'use strict';
const async = require('async');
const batch = require('../../batch'); const batch = require('../../batch');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Fix topics in categories per user if they were moved', name: 'Fix topics in categories per user if they were moved',
timestamp: Date.UTC(2018, 0, 22), timestamp: Date.UTC(2018, 0, 22),
method: function (callback) { method: async function () {
const { progress } = this; const { progress } = this;
batch.processSortedSet('topics:tid', (tids, next) => { await batch.processSortedSet('topics:tid', async (tids) => {
async.eachLimit(tids, 500, (tid, _next) => { await Promise.all(tids.map(async (tid) => {
progress.incr(); progress.incr();
let topicData; const topicData = await db.getObjectFields(`topic:${tid}`, ['cid', 'tid', 'uid', 'oldCid', 'timestamp']);
async.waterfall([ if (topicData.cid && topicData.oldCid) {
function (next) { const isMember = await db.isSortedSetMember(`cid:${topicData.oldCid}:uid:${topicData.uid}`, topicData.tid);
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) { if (isMember) {
async.series([ await db.sortedSetRemove(`cid:${topicData.oldCid}:uid:${topicData.uid}:tids`, tid);
function (next) { await db.sortedSetAdd(`cid:${topicData.cid}:uid:${topicData.uid}:tids`, topicData.timestamp, tid);
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);
}, { }, {
progress: progress, progress: progress,
batch: 500, batch: 500,
}, callback); });
}, },
}; };

@ -1,17 +1,12 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Flatten navigation data', name: 'Flatten navigation data',
timestamp: Date.UTC(2018, 1, 17), timestamp: Date.UTC(2018, 1, 17),
method: function (callback) { method: async function () {
async.waterfall([ const data = await db.getSortedSetRangeWithScores('navigation:enabled', 0, -1);
function (next) {
db.getSortedSetRangeWithScores('navigation:enabled', 0, -1, next);
},
function (data, next) {
const order = []; const order = [];
const items = []; const items = [];
data.forEach((item) => { data.forEach((item) => {
@ -23,16 +18,7 @@ module.exports = {
order.push(item.score); order.push(item.score);
items.push(JSON.stringify(navItem)); items.push(JSON.stringify(navItem));
}); });
await db.delete('navigation:enabled');
async.series([ await db.sortedSetAdd('navigation:enabled', order, items);
function (next) {
db.delete('navigation:enabled', next);
},
function (next) {
db.sortedSetAdd('navigation:enabled', order, items, next);
},
], next);
},
], callback);
}, },
}; };

@ -1,28 +1,21 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Add default settings for notification delivery types', name: 'Add default settings for notification delivery types',
timestamp: Date.UTC(2018, 1, 14), timestamp: Date.UTC(2018, 1, 14),
method: function (callback) { method: async function () {
async.waterfall([ const config = await db.getObject('config');
function (next) {
db.getObject('config', next);
},
function (config, next) {
const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none'; const postNotifications = parseInt(config.sendPostNotifications, 10) === 1 ? 'notification' : 'none';
const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none'; const chatNotifications = parseInt(config.sendChatNotifications, 10) === 1 ? 'notification' : 'none';
db.setObject('config', { await db.setObject('config', {
notificationType_upvote: config.notificationType_upvote || 'notification', notificationType_upvote: config.notificationType_upvote || 'notification',
'notificationType_new-topic': config['notificationType_new-topic'] || 'notification', 'notificationType_new-topic': config['notificationType_new-topic'] || 'notification',
'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications, 'notificationType_new-reply': config['notificationType_new-reply'] || postNotifications,
notificationType_follow: config.notificationType_follow || 'notification', notificationType_follow: config.notificationType_follow || 'notification',
'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications, 'notificationType_new-chat': config['notificationType_new-chat'] || chatNotifications,
'notificationType_group-invite': config['notificationType_group-invite'] || 'notification', 'notificationType_group-invite': config['notificationType_group-invite'] || 'notification',
}, next); });
},
], callback);
}, },
}; };

@ -1,22 +1,14 @@
'use strict'; 'use strict';
const async = require('async');
const db = require('../../database'); const db = require('../../database');
module.exports = { module.exports = {
name: 'Revising minimum password strength to 1 (from 0)', name: 'Revising minimum password strength to 1 (from 0)',
timestamp: Date.UTC(2018, 1, 21), timestamp: Date.UTC(2018, 1, 21),
method: function (callback) { method: async function () {
async.waterfall([ const strength = await db.getObjectField('config', 'minimumPasswordStrength');
async.apply(db.getObjectField.bind(db), 'config', 'minimumPasswordStrength'),
function (strength, next) {
if (!strength) { if (!strength) {
return db.setObjectField('config', 'minimumPasswordStrength', 1, next); await db.setObjectField('config', 'minimumPasswordStrength', 1);
} }
setImmediate(next);
},
], callback);
}, },
}; };

Loading…
Cancel
Save