From 5c2afe5eac237971829cc8617c2e79ac669e5ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Sep 2019 23:27:36 -0400 Subject: [PATCH] refactor: async/await socket.io/topics --- src/socket.io/topics.js | 144 +++++++++------------- src/socket.io/topics/infinitescroll.js | 159 ++++++++++--------------- src/socket.io/topics/merge.js | 28 ++--- src/socket.io/topics/move.js | 82 +++++-------- src/socket.io/topics/tags.js | 74 +++++------- src/socket.io/topics/tools.js | 131 +++++++++----------- src/socket.io/topics/unread.js | 131 +++++++------------- src/topics/follow.js | 18 ++- 8 files changed, 290 insertions(+), 477 deletions(-) diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index f84ca7e83f..b06d302ab1 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -1,16 +1,14 @@ 'use strict'; -var async = require('async'); +const topics = require('../topics'); +const posts = require('../posts'); +const user = require('../user'); +const meta = require('../meta'); +const apiController = require('../controllers/api'); +const privileges = require('../privileges'); +const socketHelpers = require('./helpers'); -var topics = require('../topics'); -var posts = require('../posts'); -var user = require('../user'); -var meta = require('../meta'); -var apiController = require('../controllers/api'); -var privileges = require('../privileges'); -var socketHelpers = require('./helpers'); - -var SocketTopics = module.exports; +const SocketTopics = module.exports; require('./topics/unread')(SocketTopics); require('./topics/move')(SocketTopics); @@ -19,129 +17,99 @@ require('./topics/infinitescroll')(SocketTopics); require('./topics/tags')(SocketTopics); require('./topics/merge')(SocketTopics); -SocketTopics.post = function (socket, data, callback) { +SocketTopics.post = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } socketHelpers.setDefaultPostData(data, socket); - - async.waterfall([ - function (next) { - meta.blacklist.test(data.req.ip, next); - }, - function (next) { - posts.shouldQueue(socket.uid, data, next); - }, - function (shouldQueue, next) { - if (shouldQueue) { - posts.addToQueue(data, next); - } else { - postTopic(socket, data, next); - } - }, - ], callback); + await meta.blacklist.test(data.req.ip); + const shouldQueue = await posts.shouldQueue(socket.uid, data); + if (shouldQueue) { + return await posts.addToQueue(data); + } + return await postTopic(socket, data); }; -function postTopic(socket, data, callback) { - async.waterfall([ - function (next) { - topics.post(data, next); - }, - function (result, next) { - next(null, result.topicData); +async function postTopic(socket, data) { + const result = await topics.post(data); - socket.emit('event:new_post', { posts: [result.postData] }); - socket.emit('event:new_topic', result.topicData); + socket.emit('event:new_post', { posts: [result.postData] }); + socket.emit('event:new_topic', result.topicData); - socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData }); - }, - ], callback); + socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData }); + return result.topicData; } -SocketTopics.postcount = function (socket, tid, callback) { - async.waterfall([ - function (next) { - privileges.topics.can('topics:read', tid, socket.uid, next); - }, - function (canRead, next) { - if (!canRead) { - return next(new Error('[[no-privileges]]')); - } - - topics.getTopicField(tid, 'postcount', next); - }, - ], callback); +SocketTopics.postcount = async function (socket, tid) { + const canRead = await privileges.topics.can('topics:read', tid, socket.uid); + if (!canRead) { + throw new Error('[[no-privileges]]'); + } + return await topics.getTopicField(tid, 'postcount'); }; -SocketTopics.bookmark = function (socket, data, callback) { +SocketTopics.bookmark = async function (socket, data) { if (!socket.uid || !data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.setUserBookmark(data.tid, socket.uid, data.index, callback); + await topics.setUserBookmark(data.tid, socket.uid, data.index); }; -SocketTopics.createTopicFromPosts = function (socket, data, callback) { +SocketTopics.createTopicFromPosts = async function (socket, data) { if (!socket.uid) { - return callback(new Error('[[error:not-logged-in]]')); + throw new Error('[[error:not-logged-in]]'); } if (!data || !data.title || !data.pids || !Array.isArray(data.pids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.createTopicFromPosts(socket.uid, data.title, data.pids, data.fromTid, callback); + return await topics.createTopicFromPosts(socket.uid, data.title, data.pids, data.fromTid); }; -SocketTopics.changeWatching = function (socket, data, callback) { +SocketTopics.changeWatching = async function (socket, data) { if (!data || !data.tid || !data.type) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - var commands = ['follow', 'unfollow', 'ignore']; + const commands = ['follow', 'unfollow', 'ignore']; if (!commands.includes(data.type)) { - return callback(new Error('[[error:invalid-command]]')); + throw new Error('[[error:invalid-command]]'); } - followCommand(topics[data.type], socket, data.tid, callback); + await followCommand(topics[data.type], socket, data.tid); }; -SocketTopics.follow = function (socket, tid, callback) { - followCommand(topics.follow, socket, tid, callback); +SocketTopics.follow = async function (socket, tid) { + await followCommand(topics.follow, socket, tid); }; -function followCommand(method, socket, tid, callback) { +async function followCommand(method, socket, tid) { if (!socket.uid) { - return callback(new Error('[[error:not-logged-in]]')); + throw new Error('[[error:not-logged-in]]'); } - method(tid, socket.uid, callback); + await method(tid, socket.uid); } -SocketTopics.isFollowed = function (socket, tid, callback) { - topics.isFollowing([tid], socket.uid, function (err, isFollowing) { - callback(err, Array.isArray(isFollowing) && isFollowing.length ? isFollowing[0] : false); - }); +SocketTopics.isFollowed = async function (socket, tid) { + const isFollowing = await topics.isFollowing([tid], socket.uid); + return isFollowing[0]; }; -SocketTopics.search = function (socket, data, callback) { +SocketTopics.search = async function (socket, data) { if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.search(data.tid, data.term, callback); + return await topics.search(data.tid, data.term); }; -SocketTopics.isModerator = function (socket, tid, callback) { - async.waterfall([ - function (next) { - topics.getTopicField(tid, 'cid', next); - }, - function (cid, next) { - user.isModerator(socket.uid, cid, next); - }, - ], callback); +SocketTopics.isModerator = async function (socket, tid) { + const cid = await topics.getTopicField(tid, 'cid'); + return await user.isModerator(socket.uid, cid); }; -SocketTopics.getTopic = function (socket, tid, callback) { - apiController.getTopicData(tid, socket.uid, callback); +SocketTopics.getTopic = async function (socket, tid) { + return await apiController.getTopicData(tid, socket.uid); }; require('../promisify')(SocketTopics); diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 9172b21719..361ecefdb6 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -1,87 +1,67 @@ 'use strict'; -var async = require('async'); - -var topics = require('../../topics'); +const topics = require('../../topics'); const categories = require('../../categories'); -var privileges = require('../../privileges'); -var meta = require('../../meta'); -var utils = require('../../utils'); -var social = require('../../social'); +const privileges = require('../../privileges'); +const meta = require('../../meta'); +const utils = require('../../utils'); +const social = require('../../social'); module.exports = function (SocketTopics) { - SocketTopics.loadMore = function (socket, data, callback) { + SocketTopics.loadMore = async function (socket, data) { if (!data || !data.tid || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); + } + + const [userPrivileges, topicData] = await Promise.all([ + privileges.topics.get(data.tid, socket.uid), + topics.getTopicFields(data.tid, ['postcount', 'deleted']), + ]); + + if (!userPrivileges['topics:read'] || (topicData.deleted && !userPrivileges.view_deleted)) { + throw new Error('[[error:no-privileges]]'); + } + + const set = data.topicPostSort === 'most_votes' ? 'tid:' + data.tid + ':posts:votes' : 'tid:' + data.tid + ':posts'; + const reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes'; + let start = Math.max(0, parseInt(data.after, 10)); + + const infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage || 20, parseInt(data.count, 10) || meta.config.postsPerPage || 20)); + + if (data.direction === -1) { + start -= (infScrollPostsPerPage + 1); + } + + let stop = start + infScrollPostsPerPage - 1; + + start = Math.max(0, start); + stop = Math.max(0, stop); + + const [mainPost, posts, postSharing] = await Promise.all([ + start > 0 ? null : topics.getMainPost(data.tid, socket.uid), + topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse), + social.getActivePostSharing(), + ]); + + if (mainPost) { + topicData.mainPost = mainPost; + topicData.posts = [mainPost].concat(posts); + } else { + topicData.posts = posts; } - var userPrivileges; - - async.waterfall([ - function (next) { - async.parallel({ - privileges: function (next) { - privileges.topics.get(data.tid, socket.uid, next); - }, - topic: function (next) { - topics.getTopicFields(data.tid, ['postcount', 'deleted'], next); - }, - }, next); - }, - function (results, next) { - if (!results.privileges['topics:read'] || (results.topic.deleted && !results.privileges.view_deleted)) { - return callback(new Error('[[error:no-privileges]]')); - } - - userPrivileges = results.privileges; - - var set = data.topicPostSort === 'most_votes' ? 'tid:' + data.tid + ':posts:votes' : 'tid:' + data.tid + ':posts'; - var reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes'; - var start = Math.max(0, parseInt(data.after, 10)); - - var infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage || 20, parseInt(data.count, 10) || meta.config.postsPerPage || 20)); - - if (data.direction === -1) { - start -= (infScrollPostsPerPage + 1); - } - - var stop = start + infScrollPostsPerPage - 1; - - start = Math.max(0, start); - stop = Math.max(0, stop); - - async.parallel({ - mainPost: function (next) { - if (start > 0) { - return next(); - } - topics.getMainPost(data.tid, socket.uid, next); - }, - posts: function (next) { - topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next); - }, - postSharing: function (next) { - social.getActivePostSharing(next); - }, - }, next); - }, - function (topicData, next) { - if (topicData.mainPost) { - topicData.posts = [topicData.mainPost].concat(topicData.posts); - } - - topicData.privileges = userPrivileges; - topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1; - topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1; - - topics.modifyPostsByPrivilege(topicData, userPrivileges); - next(null, topicData); - }, - ], callback); + + topicData.privileges = userPrivileges; + topicData.postSharing = postSharing; + topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1; + topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1; + + topics.modifyPostsByPrivilege(topicData, userPrivileges); + return topicData; }; - SocketTopics.loadMoreSortedTopics = function (socket, data, callback) { + SocketTopics.loadMoreSortedTopics = async function (socket, data) { if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } const { start, stop } = calculateStartStop(data); const params = { @@ -93,42 +73,35 @@ module.exports = function (SocketTopics) { }; if (data.sort === 'unread') { params.cid = data.cid; - topics.getUnreadTopics(params, callback); - return; + return await topics.getUnreadTopics(params); } params.cids = data.cid; params.sort = data.sort; params.term = data.term; - topics.getSortedTopics(params, callback); + return await topics.getSortedTopics(params); }; - SocketTopics.loadMoreFromSet = function (socket, data, callback) { + SocketTopics.loadMoreFromSet = async function (socket, data) { if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0 || !data.set) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } const { start, stop } = calculateStartStop(data); - topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); + return await topics.getTopicsFromSet(data.set, socket.uid, start, stop); }; - SocketTopics.loadMoreUserTopics = function (socket, data, callback) { - async.waterfall([ - function (next) { - categories.getCidsByPrivilege('categories:cid', socket.uid, 'topics:read', next); - }, - function (cids, next) { - data.set = cids.map(c => 'cid:' + c + ':uid:' + data.uid + ':tids'); - SocketTopics.loadMoreFromSet(socket, data, next); - }, - ], callback); + SocketTopics.loadMoreUserTopics = async function (socket, data) { + const cids = await categories.getCidsByPrivilege('categories:cid', socket.uid, 'topics:read'); + data.set = cids.map(c => 'cid:' + c + ':uid:' + data.uid + ':tids'); + return await SocketTopics.loadMoreFromSet(socket, data); }; function calculateStartStop(data) { - var itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20); - var start = Math.max(0, parseInt(data.after, 10)); + const itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20); + let start = Math.max(0, parseInt(data.after, 10)); if (data.direction === -1) { start -= itemsPerPage; } - var stop = start + Math.max(0, itemsPerPage - 1); + const stop = start + Math.max(0, itemsPerPage - 1); return { start: Math.max(0, start), stop: Math.max(0, stop) }; } }; diff --git a/src/socket.io/topics/merge.js b/src/socket.io/topics/merge.js index 8b475f2ede..124ede9b32 100644 --- a/src/socket.io/topics/merge.js +++ b/src/socket.io/topics/merge.js @@ -1,27 +1,17 @@ 'use strict'; -var async = require('async'); -var topics = require('../../topics'); -var privileges = require('../../privileges'); +const topics = require('../../topics'); +const privileges = require('../../privileges'); module.exports = function (SocketTopics) { - SocketTopics.merge = function (socket, tids, callback) { + SocketTopics.merge = async function (socket, tids) { if (!Array.isArray(tids)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - - async.waterfall([ - function (next) { - async.map(tids, function (tid, next) { - privileges.topics.isAdminOrMod(tid, socket.uid, next); - }, next); - }, - function (allowed, next) { - if (allowed.includes(false)) { - return next(new Error('[[error:no-privileges]]')); - } - topics.merge(tids, socket.uid, next); - }, - ], callback); + const allowed = await Promise.all(tids.map(tid => privileges.topics.isAdminOrMod(tid, socket.uid))); + if (allowed.includes(false)) { + throw new Error('[[error:no-privileges]]'); + } + await topics.merge(tids, socket.uid); }; }; diff --git a/src/socket.io/topics/move.js b/src/socket.io/topics/move.js index 21d7543fb8..0f309eeac8 100644 --- a/src/socket.io/topics/move.js +++ b/src/socket.io/topics/move.js @@ -1,70 +1,46 @@ 'use strict'; -var async = require('async'); -var topics = require('../../topics'); -var categories = require('../../categories'); -var privileges = require('../../privileges'); -var socketHelpers = require('../helpers'); +const async = require('async'); +const topics = require('../../topics'); +const categories = require('../../categories'); +const privileges = require('../../privileges'); +const socketHelpers = require('../helpers'); module.exports = function (SocketTopics) { - SocketTopics.move = function (socket, data, callback) { + SocketTopics.move = async function (socket, data) { if (!data || !Array.isArray(data.tids) || !data.cid) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.eachLimit(data.tids, 10, function (tid, next) { - var topicData; - async.waterfall([ - function (next) { - privileges.topics.isAdminOrMod(tid, socket.uid, next); - }, - function (canMove, next) { - if (!canMove) { - return next(new Error('[[error:no-privileges]]')); - } + await async.eachLimit(data.tids, 10, async function (tid) { + const canMove = await privileges.topics.isAdminOrMod(tid, socket.uid); + if (!canMove) { + throw new Error('[[error:no-privileges]]'); + } + const topicData = await topics.getTopicFields(tid, ['tid', 'cid', 'slug']); + data.uid = socket.uid; + await topics.tools.move(tid, data); - topics.getTopicFields(tid, ['cid', 'slug'], next); - }, - function (_topicData, next) { - topicData = _topicData; - topicData.tid = tid; - data.uid = socket.uid; - topics.tools.move(tid, data, next); - }, - function (next) { - socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData); + socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData); - socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic'); - - next(); - }, - ], next); - }, callback); + socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic'); + }); }; - SocketTopics.moveAll = function (socket, data, callback) { + SocketTopics.moveAll = async function (socket, data) { if (!data || !data.cid || !data.currentCid) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); + } + const canMove = await privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid); + if (!canMove) { + throw new Error('[[error:no-privileges]]'); } - async.waterfall([ - function (next) { - privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid, next); - }, - function (canMove, next) { - if (!canMove) { - return callback(new Error('[[error:no-privileges]]')); - } - - categories.getAllTopicIds(data.currentCid, 0, -1, next); - }, - function (tids, next) { - data.uid = socket.uid; - async.eachLimit(tids, 50, function (tid, next) { - topics.tools.move(tid, data, next); - }, next); - }, - ], callback); + const tids = await categories.getAllTopicIds(data.currentCid, 0, -1); + data.uid = socket.uid; + await async.eachLimit(tids, 50, async function (tid) { + await topics.tools.move(tid, data); + }); }; }; diff --git a/src/socket.io/topics/tags.js b/src/socket.io/topics/tags.js index 8b08c9dfda..19fc9fc884 100644 --- a/src/socket.io/topics/tags.js +++ b/src/socket.io/topics/tags.js @@ -1,67 +1,49 @@ 'use strict'; -var async = require('async'); -var db = require('../../database'); -var topics = require('../../topics'); -var privileges = require('../../privileges'); -var utils = require('../../utils'); +const topics = require('../../topics'); +const categories = require('../../categories'); +const privileges = require('../../privileges'); +const utils = require('../../utils'); module.exports = function (SocketTopics) { - SocketTopics.isTagAllowed = function (socket, data, callback) { + SocketTopics.isTagAllowed = async function (socket, data) { if (!data || !utils.isNumber(data.cid) || !data.tag) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.waterfall([ - function (next) { - db.getSortedSetRange('cid:' + data.cid + ':tag:whitelist', 0, -1, next); - }, - function (tagWhitelist, next) { - next(null, !tagWhitelist.length || tagWhitelist.includes(data.tag)); - }, - ], callback); + + const tagWhitelist = await categories.getTagWhitelist([data.cid]); + return !tagWhitelist[0].length || tagWhitelist[0].includes(data.tag); }; - SocketTopics.autocompleteTags = function (socket, data, callback) { - topics.autocompleteTags(data, callback); + SocketTopics.autocompleteTags = async function (socket, data) { + return await topics.autocompleteTags(data); }; - SocketTopics.searchTags = function (socket, data, callback) { - searchTags(socket.uid, topics.searchTags, data, callback); + SocketTopics.searchTags = async function (socket, data) { + return await searchTags(socket.uid, topics.searchTags, data); }; - SocketTopics.searchAndLoadTags = function (socket, data, callback) { - searchTags(socket.uid, topics.searchAndLoadTags, data, callback); + SocketTopics.searchAndLoadTags = async function (socket, data) { + return await searchTags(socket.uid, topics.searchAndLoadTags, data); }; - function searchTags(uid, method, data, callback) { - async.waterfall([ - function (next) { - privileges.global.can('search:tags', uid, next); - }, - function (allowed, next) { - if (!allowed) { - return next(new Error('[[error:no-privileges]]')); - } - method(data, next); - }, - ], callback); + async function searchTags(uid, method, data) { + const allowed = await privileges.global.can('search:tags', uid); + if (!allowed) { + throw new Error('[[error:no-privileges]]'); + } + return await method(data); } - SocketTopics.loadMoreTags = function (socket, data, callback) { + SocketTopics.loadMoreTags = async function (socket, data) { if (!data || !utils.isNumber(data.after)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - var start = parseInt(data.after, 10); - var stop = start + 99; - async.waterfall([ - function (next) { - topics.getTags(start, stop, next); - }, - function (tags, next) { - tags = tags.filter(Boolean); - next(null, { tags: tags, nextStart: stop + 1 }); - }, - ], callback); + const start = parseInt(data.after, 10); + const stop = start + 99; + const tags = await topics.getTags(start, stop); + + return { tags: tags.filter(Boolean), nextStart: stop + 1 }; }; }; diff --git a/src/socket.io/topics/tools.js b/src/socket.io/topics/tools.js index d681a0b95e..649887c367 100644 --- a/src/socket.io/topics/tools.js +++ b/src/socket.io/topics/tools.js @@ -1,127 +1,104 @@ 'use strict'; -var async = require('async'); - -var topics = require('../../topics'); -var events = require('../../events'); -var privileges = require('../../privileges'); -var plugins = require('../../plugins'); -var socketHelpers = require('../helpers'); +const topics = require('../../topics'); +const events = require('../../events'); +const privileges = require('../../privileges'); +const plugins = require('../../plugins'); +const socketHelpers = require('../helpers'); module.exports = function (SocketTopics) { - SocketTopics.loadTopicTools = function (socket, data, callback) { + SocketTopics.loadTopicTools = async function (socket, data) { if (!socket.uid) { - return callback(new Error('[[error:no-privileges]]')); + throw new Error('[[error:no-privileges]]'); } if (!data) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.waterfall([ - function (next) { - async.parallel({ - topic: function (next) { - topics.getTopicData(data.tid, next); - }, - privileges: function (next) { - privileges.topics.get(data.tid, socket.uid, next); - }, - }, next); - }, - function (results, next) { - if (!results.topic) { - return next(new Error('[[error:no-topic]]')); - } - - results.topic.privileges = results.privileges; - plugins.fireHook('filter:topic.thread_tools', { topic: results.topic, uid: socket.uid, tools: [] }, next); - }, - function (data, next) { - data.topic.thread_tools = data.tools; - next(null, data.topic); - }, - ], callback); + const [topicData, userPrivileges] = await Promise.all([ + topics.getTopicData(data.tid), + privileges.topics.get(data.tid, socket.uid), + ]); + + if (!topicData) { + throw new Error('[[error:no-topic]]'); + } + if (!userPrivileges['topics:read']) { + throw new Error('[[error:no-privileges]]'); + } + topicData.privileges = userPrivileges; + const result = await plugins.fireHook('filter:topic.thread_tools', { topic: topicData, uid: socket.uid, tools: [] }); + result.topic.thread_tools = result.tools; + return result.topic; }; - SocketTopics.delete = function (socket, data, callback) { - SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data, callback); + SocketTopics.delete = async function (socket, data) { + await SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data); }; - SocketTopics.restore = function (socket, data, callback) { - SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data, callback); + SocketTopics.restore = async function (socket, data) { + await SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data); }; - SocketTopics.purge = function (socket, data, callback) { - SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data, callback); + SocketTopics.purge = async function (socket, data) { + await SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data); }; - SocketTopics.lock = function (socket, data, callback) { - SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data, callback); + SocketTopics.lock = async function (socket, data) { + await SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data); }; - SocketTopics.unlock = function (socket, data, callback) { - SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data, callback); + SocketTopics.unlock = async function (socket, data) { + await SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data); }; - SocketTopics.pin = function (socket, data, callback) { - SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data, callback); + SocketTopics.pin = async function (socket, data) { + await SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data); }; - SocketTopics.unpin = function (socket, data, callback) { - SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data, callback); + SocketTopics.unpin = async function (socket, data) { + await SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data); }; - SocketTopics.doTopicAction = function (action, event, socket, data, callback) { - callback = callback || function () {}; + SocketTopics.doTopicAction = async function (action, event, socket, data) { if (!socket.uid) { - return callback(new Error('[[error:no-privileges]]')); + throw new Error('[[error:no-privileges]]'); } if (!data || !Array.isArray(data.tids) || !data.cid) { - return callback(new Error('[[error:invalid-tid]]')); + throw new Error('[[error:invalid-tid]]'); } if (typeof topics.tools[action] !== 'function') { - return callback(); + return; } - - async.each(data.tids, function (tid, next) { - var title; - async.waterfall([ - function (next) { - topics.getTopicField(tid, 'title', next); - }, - function (_title, next) { - title = _title; - topics.tools[action](tid, socket.uid, next); - }, - function (data, next) { - socketHelpers.emitToTopicAndCategory(event, data); - logTopicAction(action, socket, tid, title, next); - }, - ], next); - }, callback); + await Promise.all(data.tids.map(async function (tid) { + const title = await topics.getTopicField(tid, 'title'); + const data = await topics.tools[action](tid, socket.uid); + socketHelpers.emitToTopicAndCategory(event, data); + await logTopicAction(action, socket, tid, title); + })); }; - function logTopicAction(action, socket, tid, title, callback) { + async function logTopicAction(action, socket, tid, title) { var actionsToLog = ['delete', 'restore', 'purge']; if (!actionsToLog.includes(action)) { - return setImmediate(callback); + return; } - events.log({ + await events.log({ type: 'topic-' + action, uid: socket.uid, ip: socket.ip, tid: tid, title: String(title), - }, callback); + }); } - SocketTopics.orderPinnedTopics = function (socket, data, callback) { + SocketTopics.orderPinnedTopics = async function (socket, data) { if (!Array.isArray(data)) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.tools.orderPinnedTopics(socket.uid, data, callback); + await topics.tools.orderPinnedTopics(socket.uid, data); }; }; diff --git a/src/socket.io/topics/unread.js b/src/socket.io/topics/unread.js index 627264c09f..05a2cad951 100644 --- a/src/socket.io/topics/unread.js +++ b/src/socket.io/topics/unread.js @@ -1,122 +1,71 @@ 'use strict'; -var async = require('async'); - -var user = require('../../user'); -var topics = require('../../topics'); +const user = require('../../user'); +const topics = require('../../topics'); module.exports = function (SocketTopics) { - SocketTopics.markAsRead = function (socket, tids, callback) { + SocketTopics.markAsRead = async function (socket, tids) { if (!Array.isArray(tids) || socket.uid <= 0) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.waterfall([ - function (next) { - topics.markAsRead(tids, socket.uid, next); - }, - function (hasMarked, next) { - if (hasMarked) { - topics.pushUnreadCount(socket.uid); + const hasMarked = await topics.markAsRead(tids, socket.uid); + if (hasMarked) { + topics.pushUnreadCount(socket.uid); - topics.markTopicNotificationsRead(tids, socket.uid); - } - next(); - }, - ], callback); + topics.markTopicNotificationsRead(tids, socket.uid); + } }; - SocketTopics.markTopicNotificationsRead = function (socket, tids, callback) { + SocketTopics.markTopicNotificationsRead = async function (socket, tids) { if (!Array.isArray(tids) || !socket.uid) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - topics.markTopicNotificationsRead(tids, socket.uid, callback); + await topics.markTopicNotificationsRead(tids, socket.uid); }; - SocketTopics.markAllRead = function (socket, data, callback) { + SocketTopics.markAllRead = async function (socket) { if (socket.uid <= 0) { - return callback(new Error('[[error:invalid-uid]]')); + throw new Error('[[error:invalid-uid]]'); } - async.waterfall([ - function (next) { - topics.markAllRead(socket.uid, next); - }, - function (next) { - topics.pushUnreadCount(socket.uid); - next(); - }, - ], callback); + await topics.markAllRead(socket.uid); + topics.pushUnreadCount(socket.uid); }; - SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) { - async.waterfall([ - function (next) { - topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' }, next); - }, - function (tids, next) { - SocketTopics.markAsRead(socket, tids, next); - }, - ], callback); + SocketTopics.markCategoryTopicsRead = async function (socket, cid) { + const tids = await topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' }); + await SocketTopics.markAsRead(socket, tids); }; - SocketTopics.markUnread = function (socket, tid, callback) { + SocketTopics.markUnread = async function (socket, tid) { if (!tid || socket.uid <= 0) { - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - async.waterfall([ - function (next) { - topics.markUnread(tid, socket.uid, next); - }, - function (next) { - topics.pushUnreadCount(socket.uid); - next(); - }, - ], callback); + await topics.markUnread(tid, socket.uid); + topics.pushUnreadCount(socket.uid); }; - SocketTopics.markAsUnreadForAll = function (socket, tids, callback) { + SocketTopics.markAsUnreadForAll = async function (socket, tids) { if (!Array.isArray(tids)) { - return callback(new Error('[[error:invalid-tid]]')); + throw new Error('[[error:invalid-tid]]'); } if (socket.uid <= 0) { - return callback(new Error('[[error:no-privileges]]')); + throw new Error('[[error:no-privileges]]'); } + const isAdmin = await user.isAdministrator(socket.uid); - async.waterfall([ - function (next) { - user.isAdministrator(socket.uid, next); - }, - function (isAdmin, next) { - async.each(tids, function (tid, next) { - async.waterfall([ - function (next) { - topics.exists(tid, next); - }, - function (exists, next) { - if (!exists) { - return next(new Error('[[error:no-topic]]')); - } - topics.getTopicField(tid, 'cid', next); - }, - function (cid, next) { - user.isModerator(socket.uid, cid, next); - }, - function (isMod, next) { - if (!isAdmin && !isMod) { - return next(new Error('[[error:no-privileges]]')); - } - topics.markAsUnreadForAll(tid, next); - }, - function (next) { - topics.updateRecent(tid, Date.now(), next); - }, - ], next); - }, next); - }, - function (next) { - topics.pushUnreadCount(socket.uid); - next(); - }, - ], callback); + await Promise.all(tids.map(async (tid) => { + const topicData = await topics.getTopicFields(tid, ['tid', 'cid']); + if (!topicData.tid) { + throw new Error('[[error:no-topic]]'); + } + const isMod = await user.isModerator(socket.uid, topicData.cid); + if (!isAdmin && !isMod) { + throw new Error('[[error:no-privileges]]'); + } + await topics.markAsUnreadForAll(tid); + await topics.updateRecent(tid, Date.now()); + })); + topics.pushUnreadCount(socket.uid); }; }; diff --git a/src/topics/follow.js b/src/topics/follow.js index 957e97a6de..796840dc42 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -1,12 +1,12 @@ 'use strict'; -var db = require('../database'); -var posts = require('../posts'); -var notifications = require('../notifications'); -var privileges = require('../privileges'); -var plugins = require('../plugins'); -var utils = require('../utils'); +const db = require('../database'); +const posts = require('../posts'); +const notifications = require('../notifications'); +const privileges = require('../privileges'); +const plugins = require('../plugins'); +const utils = require('../utils'); module.exports = function (Topics) { Topics.toggleFollow = async function (tid, uid) { @@ -90,9 +90,7 @@ module.exports = function (Topics) { return tids.map(() => ({ following: false, ignoring: false })); } const keys = []; - tids.forEach((tid) => { - keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers'); - }); + tids.forEach(tid => keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers')); const data = await db.isMemberOfSets(keys, uid); @@ -113,7 +111,7 @@ module.exports = function (Topics) { if (parseInt(uid, 10) <= 0) { return tids.map(() => false); } - var keys = tids.map(tid => 'tid:' + tid + ':' + set); + const keys = tids.map(tid => 'tid:' + tid + ':' + set); return await db.isMemberOfSets(keys, uid); }