From 88dfbf213fe38e4baf62a34f95abb805b2c55fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 7 Sep 2019 18:22:03 -0400 Subject: [PATCH] refactor: async/await socket.io/admin --- src/socket.io/admin/groups.js | 44 ++-- src/socket.io/admin/navigation.js | 8 +- src/socket.io/admin/rewards.js | 12 +- src/socket.io/admin/rooms.js | 65 ++---- src/socket.io/admin/social.js | 8 +- src/socket.io/admin/tags.js | 28 +-- src/socket.io/admin/user.js | 329 ++++++++++++------------------ src/socket.io/index.js | 2 +- src/user/auth.js | 10 +- 9 files changed, 204 insertions(+), 302 deletions(-) diff --git a/src/socket.io/admin/groups.js b/src/socket.io/admin/groups.js index bc5804d029..98325c656b 100644 --- a/src/socket.io/admin/groups.js +++ b/src/socket.io/admin/groups.js @@ -1,22 +1,21 @@ 'use strict'; -var async = require('async'); -var groups = require('../../groups'); +const groups = require('../../groups'); -var Groups = module.exports; +const Groups = module.exports; -Groups.create = function (socket, data, callback) { +Groups.create = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } else if (groups.isPrivilegeGroup(data.name)) { - return callback(new Error('[[error:invalid-group-name]]')); + throw new Error('[[error:invalid-group-name]]'); } - groups.create({ + return await groups.create({ name: data.name, description: data.description, ownerUid: socket.uid, - }, callback); + }); }; Groups.join = async (socket, data) => { @@ -32,32 +31,25 @@ Groups.join = async (socket, data) => { return await groups.join(data.groupName, data.uid); }; -Groups.leave = function (socket, data, callback) { +Groups.leave = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } if (socket.uid === parseInt(data.uid, 10) && data.groupName === 'administrators') { - return callback(new Error('[[error:cant-remove-self-as-admin]]')); + throw new Error('[[error:cant-remove-self-as-admin]]'); } - - async.waterfall([ - function (next) { - groups.isMember(data.uid, data.groupName, next); - }, - function (isMember, next) { - if (!isMember) { - return next(new Error('[[error:group-not-member]]')); - } - groups.leave(data.groupName, data.uid, next); - }, - ], callback); + const isMember = await groups.isMember(data.uid, data.groupName); + if (!isMember) { + throw new Error('[[error:group-not-member]]'); + } + await groups.leave(data.groupName, data.uid); }; -Groups.update = function (socket, data, callback) { +Groups.update = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - groups.update(data.groupName, data.values, callback); + await groups.update(data.groupName, data.values); }; diff --git a/src/socket.io/admin/navigation.js b/src/socket.io/admin/navigation.js index d9297f55e1..9898117d99 100644 --- a/src/socket.io/admin/navigation.js +++ b/src/socket.io/admin/navigation.js @@ -1,8 +1,8 @@ 'use strict'; -var navigationAdmin = require('../../navigation/admin'); -var SocketNavigation = module.exports; +const navigationAdmin = require('../../navigation/admin'); +const SocketNavigation = module.exports; -SocketNavigation.save = function (socket, data, callback) { - navigationAdmin.save(data, callback); +SocketNavigation.save = async function (socket, data) { + await navigationAdmin.save(data); }; diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js index b5fac9c670..97a64f1f68 100644 --- a/src/socket.io/admin/rewards.js +++ b/src/socket.io/admin/rewards.js @@ -1,12 +1,12 @@ 'use strict'; -var rewardsAdmin = require('../../rewards/admin'); -var SocketRewards = module.exports; +const rewardsAdmin = require('../../rewards/admin'); +const SocketRewards = module.exports; -SocketRewards.save = function (socket, data, callback) { - rewardsAdmin.save(data, callback); +SocketRewards.save = async function (socket, data) { + await rewardsAdmin.save(data); }; -SocketRewards.delete = function (socket, data, callback) { - rewardsAdmin.delete(data, callback); +SocketRewards.delete = async function (socket, data) { + await rewardsAdmin.delete(data); }; diff --git a/src/socket.io/admin/rooms.js b/src/socket.io/admin/rooms.js index 4c393c5a05..9808e1f23f 100644 --- a/src/socket.io/admin/rooms.js +++ b/src/socket.io/admin/rooms.js @@ -1,30 +1,23 @@ 'use strict'; +const os = require('os'); +const nconf = require('nconf'); -var async = require('async'); -var os = require('os'); -var nconf = require('nconf'); -var winston = require('winston'); +const topics = require('../../topics'); +const pubsub = require('../../pubsub'); +const utils = require('../../utils'); -var topics = require('../../topics'); -var pubsub = require('../../pubsub'); -var utils = require('../../utils'); +const stats = {}; +const totals = {}; -var stats = {}; -var totals = {}; - -var SocketRooms = module.exports; +const SocketRooms = module.exports; SocketRooms.stats = stats; SocketRooms.totals = totals; pubsub.on('sync:stats:start', function () { - SocketRooms.getLocalStats(function (err, stats) { - if (err) { - return winston.error(err); - } - pubsub.publish('sync:stats:end', { stats: stats, id: os.hostname() + ':' + nconf.get('port') }); - }); + const stats = SocketRooms.getLocalStats(); + pubsub.publish('sync:stats:end', { stats: stats, id: os.hostname() + ':' + nconf.get('port') }); }); pubsub.on('sync:stats:end', function (data) { @@ -54,7 +47,7 @@ SocketRooms.getTotalGuestCount = function (callback) { }; -SocketRooms.getAll = function (socket, data, callback) { +SocketRooms.getAll = async function () { pubsub.publish('sync:stats:start'); totals.onlineGuestCount = 0; @@ -92,27 +85,16 @@ SocketRooms.getAll = function (socket, data, callback) { topTenTopics.push({ tid: tid, count: totals.topics[tid].count || 0 }); }); - topTenTopics = topTenTopics.sort(function (a, b) { - return b.count - a.count; - }).slice(0, 10); - - var topTenTids = topTenTopics.map(function (topic) { - return topic.tid; - }); + topTenTopics = topTenTopics.sort((a, b) => b.count - a.count).slice(0, 10); - async.waterfall([ - function (next) { - topics.getTopicsFields(topTenTids, ['title'], next); - }, - function (titles, next) { - totals.topTenTopics = topTenTopics.map(function (topic, index) { - topic.title = titles[index].title; - return topic; - }); + var topTenTids = topTenTopics.map(topic => topic.tid); - next(null, totals); - }, - ], callback); + const titles = await topics.getTopicsFields(topTenTids, ['title']); + totals.topTenTopics = topTenTopics.map(function (topic, index) { + topic.title = titles[index].title; + return topic; + }); + return totals; }; SocketRooms.getOnlineUserCount = function (io) { @@ -129,7 +111,7 @@ SocketRooms.getOnlineUserCount = function (io) { return count; }; -SocketRooms.getLocalStats = function (callback) { +SocketRooms.getLocalStats = function () { var io = require('../index').server; var socketData = { @@ -170,14 +152,11 @@ SocketRooms.getLocalStats = function (callback) { } } - topTenTopics = topTenTopics.sort(function (a, b) { - return b.count - a.count; - }).slice(0, 10); - + topTenTopics = topTenTopics.sort((a, b) => b.count - a.count).slice(0, 10); socketData.topics = topTenTopics; } - callback(null, socketData); + return socketData; }; require('../../promisify')(SocketRooms); diff --git a/src/socket.io/admin/social.js b/src/socket.io/admin/social.js index ce22acdb3c..596f5aea72 100644 --- a/src/socket.io/admin/social.js +++ b/src/socket.io/admin/social.js @@ -1,8 +1,8 @@ 'use strict'; -var social = require('../../social'); -var SocketSocial = module.exports; +const social = require('../../social'); +const SocketSocial = module.exports; -SocketSocial.savePostSharingNetworks = function (socket, data, callback) { - social.setActivePostSharingNetworks(data, callback); +SocketSocial.savePostSharingNetworks = async function (socket, data) { + await social.setActivePostSharingNetworks(data); }; diff --git a/src/socket.io/admin/tags.js b/src/socket.io/admin/tags.js index f3c403e704..698c303224 100644 --- a/src/socket.io/admin/tags.js +++ b/src/socket.io/admin/tags.js @@ -1,37 +1,37 @@ 'use strict'; -var topics = require('../../topics'); +const topics = require('../../topics'); -var Tags = module.exports; +const Tags = module.exports; -Tags.create = function (socket, data, callback) { +Tags.create = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.createEmptyTag(data.tag, callback); + await topics.createEmptyTag(data.tag); }; -Tags.update = function (socket, data, callback) { +Tags.update = async function (socket, data) { if (!Array.isArray(data)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.updateTags(data, callback); + await topics.updateTags(data); }; -Tags.rename = function (socket, data, callback) { +Tags.rename = async function (socket, data) { if (!Array.isArray(data)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.renameTags(data, callback); + await topics.renameTags(data); }; -Tags.deleteTags = function (socket, data, callback) { +Tags.deleteTags = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.deleteTags(data.tags, callback); + await topics.deleteTags(data.tags); }; diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index fdc2f16374..3313bd8d19 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -1,265 +1,192 @@ 'use strict'; -var async = require('async'); -var winston = require('winston'); +const async = require('async'); +const winston = require('winston'); -var db = require('../../database'); -var groups = require('../../groups'); -var user = require('../../user'); -var events = require('../../events'); -var meta = require('../../meta'); -var plugins = require('../../plugins'); +const db = require('../../database'); +const groups = require('../../groups'); +const user = require('../../user'); +const events = require('../../events'); +const meta = require('../../meta'); +const plugins = require('../../plugins'); -var User = module.exports; +const User = module.exports; -User.makeAdmins = function (socket, uids, callback) { +User.makeAdmins = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); + } + const userData = await user.getUsersFields(uids, ['banned']); + userData.forEach((userData) => { + if (userData && userData.banned) { + throw new Error('[[error:cant-make-banned-users-admin]]'); + } + }); + for (const uid of uids) { + /* eslint-disable no-await-in-loop */ + await groups.join('administrators', uid); + await events.log({ + type: 'user-makeAdmin', + uid: socket.uid, + targetUid: uid, + ip: socket.ip, + }); } - - async.waterfall([ - function (next) { - user.getUsersFields(uids, ['banned'], next); - }, - function (userData, next) { - for (var i = 0; i < userData.length; i += 1) { - if (userData[i] && userData[i].banned) { - return callback(new Error('[[error:cant-make-banned-users-admin]]')); - } - } - - async.eachSeries(uids, function (uid, next) { - async.waterfall([ - function (next) { - groups.join('administrators', uid, next); - }, - function (next) { - events.log({ - type: 'user-makeAdmin', - uid: socket.uid, - targetUid: uid, - ip: socket.ip, - }, next); - }, - ], next); - }, next); - }, - ], callback); }; -User.removeAdmins = function (socket, uids, callback) { +User.removeAdmins = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); + } + for (const uid of uids) { + /* eslint-disable no-await-in-loop */ + const count = await groups.getMemberCount('administrators'); + if (count === 1) { + throw new Error('[[error:cant-remove-last-admin]]'); + } + await groups.leave('administrators', uid); + await events.log({ + type: 'user-removeAdmin', + uid: socket.uid, + targetUid: uid, + ip: socket.ip, + }); } - - async.eachSeries(uids, function (uid, next) { - async.waterfall([ - function (next) { - groups.getMemberCount('administrators', next); - }, - function (count, next) { - if (count === 1) { - return next(new Error('[[error:cant-remove-last-admin]]')); - } - - groups.leave('administrators', uid, next); - }, - function (next) { - events.log({ - type: 'user-removeAdmin', - uid: socket.uid, - targetUid: uid, - ip: socket.ip, - }, next); - }, - ], next); - }, callback); }; -User.createUser = function (socket, userData, callback) { +User.createUser = async function (socket, userData) { if (!userData) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - user.create(userData, callback); + return await user.create(userData); }; -User.resetLockouts = function (socket, uids, callback) { +User.resetLockouts = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - - async.each(uids, user.auth.resetLockout, callback); + await Promise.all(uids.map(uid => user.auth.resetLockout(uid))); }; -User.validateEmail = function (socket, uids, callback) { +User.validateEmail = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } uids = uids.filter(uid => parseInt(uid, 10)); - - async.waterfall([ - function (next) { - async.each(uids, function (uid, next) { - user.setUserField(uid, 'email:confirmed', 1, next); - }, next); - }, - function (next) { - db.sortedSetRemove('users:notvalidated', uids, next); - }, - ], callback); + await db.setObjectField(uids.map(uid => 'user:' + uid), 'email:confirmed', 1); + await db.sortedSetRemove('users:notvalidated', uids); }; -User.sendValidationEmail = function (socket, uids, callback) { +User.sendValidationEmail = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } if (!meta.config.requireEmailConfirmation) { - return callback(new Error('[[error:email-confirmations-are-disabled]]')); + throw new Error('[[error:email-confirmations-are-disabled]]'); } - async.eachLimit(uids, 50, function (uid, next) { - user.email.sendValidationEmail(uid, next); - }, callback); + await async.eachLimit(uids, 50, async function (uid) { + await user.email.sendValidationEmail(uid); + }); }; -User.sendPasswordResetEmail = function (socket, uids, callback) { +User.sendPasswordResetEmail = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } uids = uids.filter(uid => parseInt(uid, 10)); - async.each(uids, function (uid, next) { - async.waterfall([ - function (next) { - user.getUserFields(uid, ['email', 'username'], next); - }, - function (userData, next) { - if (!userData.email) { - return next(new Error('[[error:user-doesnt-have-email, ' + userData.username + ']]')); - } - user.reset.send(userData.email, next); - }, - ], next); - }, callback); + await Promise.all(uids.map(async function (uid) { + const userData = await user.getUserFields(uid, ['email', 'username']); + if (!userData.email) { + throw new Error('[[error:user-doesnt-have-email, ' + userData.username + ']]'); + } + await user.reset.send(userData.email); + })); }; -User.forcePasswordReset = function (socket, uids, callback) { +User.forcePasswordReset = async function (socket, uids) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } uids = uids.filter(uid => parseInt(uid, 10)); - async.each(uids, function (uid, next) { - async.waterfall([ - function (next) { - user.setUserField(uid, 'passwordExpiry', Date.now(), next); - }, - function (next) { - user.auth.revokeAllSessions(uid, next); - }, - ], next); - }, callback); + await db.setObjectField(uids.map(uid => 'user:' + uid), 'passwordExpiry', Date.now()); + await user.auth.revokeAllSessions(uids); }; -User.deleteUsers = function (socket, uids, callback) { - deleteUsers(socket, uids, function (uid, next) { - user.deleteAccount(uid, next); - }, callback); +User.deleteUsers = async function (socket, uids) { + deleteUsers(socket, uids, async function (uid) { + await user.deleteAccount(uid); + }); }; -User.deleteUsersAndContent = function (socket, uids, callback) { - deleteUsers(socket, uids, function (uid, next) { - user.delete(socket.uid, uid, next); - }, callback); +User.deleteUsersAndContent = async function (socket, uids) { + deleteUsers(socket, uids, async function (uid) { + await user.delete(socket.uid, uid); + }); }; -function deleteUsers(socket, uids, method, callback) { +async function deleteUsers(socket, uids, method) { if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.waterfall([ - function (next) { - groups.isMembers(uids, 'administrators', next); - }, - function (isMembers, next) { - if (isMembers.includes(true)) { - return callback(new Error('[[error:cant-delete-other-admins]]')); - } - - callback(); + const isMembers = await groups.isMembers(uids, 'administrators'); + if (isMembers.includes(true)) { + throw new Error('[[error:cant-delete-other-admins]]'); + } + async function doDelete(uid) { + const userData = await method(uid); + await events.log({ + type: 'user-delete', + uid: socket.uid, + targetUid: uid, + ip: socket.ip, + username: userData.username, + email: userData.email, + }); + plugins.fireHook('action:user.delete', { + callerUid: socket.uid, + uid: uid, + ip: socket.ip, + }); + } + try { + await Promise.all(uids.map(uid => doDelete(uid))); + } catch (err) { + winston.error(err); + } +} - async.each(uids, function (uid, next) { - async.waterfall([ - function (next) { - method(uid, next); - }, - function (userData, next) { - events.log({ - type: 'user-delete', - uid: socket.uid, - targetUid: uid, - ip: socket.ip, - username: userData.username, - email: userData.email, - }, next); - }, - function (next) { - plugins.fireHook('action:user.delete', { - callerUid: socket.uid, - uid: uid, - ip: socket.ip, - }); - next(); - }, - ], next); - }, next); - }, - ], function (err) { - if (err) { - winston.error(err); - } +User.search = async function (socket, data) { + const searchData = await user.search({ + query: data.query, + searchBy: data.searchBy, + uid: socket.uid, }); -} -User.search = function (socket, data, callback) { - var searchData; - async.waterfall([ - function (next) { - user.search({ - query: data.query, - searchBy: data.searchBy, - uid: socket.uid, - }, next); - }, - function (_searchData, next) { - searchData = _searchData; - if (!searchData.users.length) { - return callback(null, searchData); - } + if (!searchData.users.length) { + return searchData; + } - var uids = searchData.users.map(user => user && user.uid); + const uids = searchData.users.map(user => user && user.uid); + const userInfo = await user.getUsersFields(uids, ['email', 'flags', 'lastonline', 'joindate']); - user.getUsersFields(uids, ['email', 'flags', 'lastonline', 'joindate'], next); - }, - function (userInfo, next) { - searchData.users.forEach(function (user, index) { - if (user && userInfo[index]) { - user.email = userInfo[index].email; - user.flags = userInfo[index].flags || 0; - user.lastonlineISO = userInfo[index].lastonlineISO; - user.joindateISO = userInfo[index].joindateISO; - } - }); - next(null, searchData); - }, - ], callback); + searchData.users.forEach(function (user, index) { + if (user && userInfo[index]) { + user.email = userInfo[index].email; + user.flags = userInfo[index].flags || 0; + user.lastonlineISO = userInfo[index].lastonlineISO; + user.joindateISO = userInfo[index].joindateISO; + } + }); + return searchData; }; -User.restartJobs = function (socket, data, callback) { +User.restartJobs = async function () { user.startJobs(); - callback(); }; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 7110b48dc8..465e4b7fc7 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -97,7 +97,7 @@ function onMessage(socket, payload) { } var eventName = payload.data[0]; - var params = payload.data[1]; + var params = typeof payload.data[1] === 'function' ? {} : payload.data[1]; var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () {}; if (!eventName) { diff --git a/src/user/auth.js b/src/user/auth.js index d501cac0e2..7696ba4580 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -124,9 +124,13 @@ module.exports = function (User) { ]); }; - User.auth.revokeAllSessions = async function (uid) { - const sids = await db.getSortedSetRange('uid:' + uid + ':sessions', 0, -1); - const promises = sids.map(s => User.auth.revokeSession(s, uid)); + User.auth.revokeAllSessions = async function (uids) { + uids = Array.isArray(uids) ? uids : [uids]; + const sids = await db.getSortedSetsMembers(uids.map(uid => 'uid:' + uid + ':sessions')); + const promises = []; + uids.forEach((uid, index) => { + promises.push(sids[index].map(s => User.auth.revokeSession(s, uid))); + }); await Promise.all(promises); };