refactor: async/await socket.io/topics

v1.18.x
Barış Soner Uşaklı 5 years ago
parent ebe5ed22bb
commit 5c2afe5eac

@ -1,16 +1,14 @@
'use strict'; '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'); const SocketTopics = module.exports;
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;
require('./topics/unread')(SocketTopics); require('./topics/unread')(SocketTopics);
require('./topics/move')(SocketTopics); require('./topics/move')(SocketTopics);
@ -19,129 +17,99 @@ require('./topics/infinitescroll')(SocketTopics);
require('./topics/tags')(SocketTopics); require('./topics/tags')(SocketTopics);
require('./topics/merge')(SocketTopics); require('./topics/merge')(SocketTopics);
SocketTopics.post = function (socket, data, callback) { SocketTopics.post = async function (socket, data) {
if (!data) { if (!data) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
socketHelpers.setDefaultPostData(data, socket); socketHelpers.setDefaultPostData(data, socket);
await meta.blacklist.test(data.req.ip);
async.waterfall([ const shouldQueue = await posts.shouldQueue(socket.uid, data);
function (next) {
meta.blacklist.test(data.req.ip, next);
},
function (next) {
posts.shouldQueue(socket.uid, data, next);
},
function (shouldQueue, next) {
if (shouldQueue) { if (shouldQueue) {
posts.addToQueue(data, next); return await posts.addToQueue(data);
} else {
postTopic(socket, data, next);
} }
}, return await postTopic(socket, data);
], callback);
}; };
function postTopic(socket, data, callback) { async function postTopic(socket, data) {
async.waterfall([ const result = await topics.post(data);
function (next) {
topics.post(data, next);
},
function (result, next) {
next(null, result.topicData);
socket.emit('event:new_post', { posts: [result.postData] }); socket.emit('event:new_post', { posts: [result.postData] });
socket.emit('event:new_topic', result.topicData); socket.emit('event:new_topic', result.topicData);
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData }); socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
}, return result.topicData;
], callback);
} }
SocketTopics.postcount = function (socket, tid, callback) { SocketTopics.postcount = async function (socket, tid) {
async.waterfall([ const canRead = await privileges.topics.can('topics:read', tid, socket.uid);
function (next) {
privileges.topics.can('topics:read', tid, socket.uid, next);
},
function (canRead, next) {
if (!canRead) { if (!canRead) {
return next(new Error('[[no-privileges]]')); throw new Error('[[no-privileges]]');
} }
return await topics.getTopicField(tid, 'postcount');
topics.getTopicField(tid, 'postcount', next);
},
], callback);
}; };
SocketTopics.bookmark = function (socket, data, callback) { SocketTopics.bookmark = async function (socket, data) {
if (!socket.uid || !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) { 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)) { 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) { 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)) { 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) { SocketTopics.follow = async function (socket, tid) {
followCommand(topics.follow, socket, tid, callback); await followCommand(topics.follow, socket, tid);
}; };
function followCommand(method, socket, tid, callback) { async function followCommand(method, socket, tid) {
if (!socket.uid) { 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) { SocketTopics.isFollowed = async function (socket, tid) {
topics.isFollowing([tid], socket.uid, function (err, isFollowing) { const isFollowing = await topics.isFollowing([tid], socket.uid);
callback(err, Array.isArray(isFollowing) && isFollowing.length ? isFollowing[0] : false); return isFollowing[0];
});
}; };
SocketTopics.search = function (socket, data, callback) { SocketTopics.search = async function (socket, data) {
if (!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) { SocketTopics.isModerator = async function (socket, tid) {
async.waterfall([ const cid = await topics.getTopicField(tid, 'cid');
function (next) { return await user.isModerator(socket.uid, cid);
topics.getTopicField(tid, 'cid', next);
},
function (cid, next) {
user.isModerator(socket.uid, cid, next);
},
], callback);
}; };
SocketTopics.getTopic = function (socket, tid, callback) { SocketTopics.getTopic = async function (socket, tid) {
apiController.getTopicData(tid, socket.uid, callback); return await apiController.getTopicData(tid, socket.uid);
}; };
require('../promisify')(SocketTopics); require('../promisify')(SocketTopics);

@ -1,87 +1,67 @@
'use strict'; 'use strict';
var async = require('async'); const topics = require('../../topics');
var topics = require('../../topics');
const categories = require('../../categories'); const categories = require('../../categories');
var privileges = require('../../privileges'); const privileges = require('../../privileges');
var meta = require('../../meta'); const meta = require('../../meta');
var utils = require('../../utils'); const utils = require('../../utils');
var social = require('../../social'); const social = require('../../social');
module.exports = function (SocketTopics) { 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) { 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]]');
}
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; const [userPrivileges, topicData] = await Promise.all([
privileges.topics.get(data.tid, socket.uid),
topics.getTopicFields(data.tid, ['postcount', 'deleted']),
]);
var set = data.topicPostSort === 'most_votes' ? 'tid:' + data.tid + ':posts:votes' : 'tid:' + data.tid + ':posts'; if (!userPrivileges['topics:read'] || (topicData.deleted && !userPrivileges.view_deleted)) {
var reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes'; throw new Error('[[error:no-privileges]]');
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)); 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) { if (data.direction === -1) {
start -= (infScrollPostsPerPage + 1); start -= (infScrollPostsPerPage + 1);
} }
var stop = start + infScrollPostsPerPage - 1; let stop = start + infScrollPostsPerPage - 1;
start = Math.max(0, start); start = Math.max(0, start);
stop = Math.max(0, stop); stop = Math.max(0, stop);
async.parallel({ const [mainPost, posts, postSharing] = await Promise.all([
mainPost: function (next) { start > 0 ? null : topics.getMainPost(data.tid, socket.uid),
if (start > 0) { topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse),
return next(); social.getActivePostSharing(),
} ]);
topics.getMainPost(data.tid, socket.uid, next);
}, if (mainPost) {
posts: function (next) { topicData.mainPost = mainPost;
topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next); topicData.posts = [mainPost].concat(posts);
}, } else {
postSharing: function (next) { topicData.posts = posts;
social.getActivePostSharing(next);
},
}, next);
},
function (topicData, next) {
if (topicData.mainPost) {
topicData.posts = [topicData.mainPost].concat(topicData.posts);
} }
topicData.privileges = userPrivileges; topicData.privileges = userPrivileges;
topicData.postSharing = postSharing;
topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1; topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1;
topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1; topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1;
topics.modifyPostsByPrivilege(topicData, userPrivileges); topics.modifyPostsByPrivilege(topicData, userPrivileges);
next(null, topicData); return topicData;
},
], callback);
}; };
SocketTopics.loadMoreSortedTopics = function (socket, data, callback) { SocketTopics.loadMoreSortedTopics = async function (socket, data) {
if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { 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 { start, stop } = calculateStartStop(data);
const params = { const params = {
@ -93,42 +73,35 @@ module.exports = function (SocketTopics) {
}; };
if (data.sort === 'unread') { if (data.sort === 'unread') {
params.cid = data.cid; params.cid = data.cid;
topics.getUnreadTopics(params, callback); return await topics.getUnreadTopics(params);
return;
} }
params.cids = data.cid; params.cids = data.cid;
params.sort = data.sort; params.sort = data.sort;
params.term = data.term; 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) { 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); 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) { SocketTopics.loadMoreUserTopics = async function (socket, data) {
async.waterfall([ const cids = await categories.getCidsByPrivilege('categories:cid', socket.uid, 'topics:read');
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'); data.set = cids.map(c => 'cid:' + c + ':uid:' + data.uid + ':tids');
SocketTopics.loadMoreFromSet(socket, data, next); return await SocketTopics.loadMoreFromSet(socket, data);
},
], callback);
}; };
function calculateStartStop(data) { function calculateStartStop(data) {
var itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20); const itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20);
var start = Math.max(0, parseInt(data.after, 10)); let start = Math.max(0, parseInt(data.after, 10));
if (data.direction === -1) { if (data.direction === -1) {
start -= itemsPerPage; 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) }; return { start: Math.max(0, start), stop: Math.max(0, stop) };
} }
}; };

@ -1,27 +1,17 @@
'use strict'; 'use strict';
var async = require('async'); const topics = require('../../topics');
var topics = require('../../topics'); const privileges = require('../../privileges');
var privileges = require('../../privileges');
module.exports = function (SocketTopics) { module.exports = function (SocketTopics) {
SocketTopics.merge = function (socket, tids, callback) { SocketTopics.merge = async function (socket, tids) {
if (!Array.isArray(tids)) { if (!Array.isArray(tids)) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
const allowed = await Promise.all(tids.map(tid => privileges.topics.isAdminOrMod(tid, socket.uid)));
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)) { if (allowed.includes(false)) {
return next(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
topics.merge(tids, socket.uid, next); await topics.merge(tids, socket.uid);
},
], callback);
}; };
}; };

@ -1,70 +1,46 @@
'use strict'; 'use strict';
var async = require('async'); const async = require('async');
var topics = require('../../topics'); const topics = require('../../topics');
var categories = require('../../categories'); const categories = require('../../categories');
var privileges = require('../../privileges'); const privileges = require('../../privileges');
var socketHelpers = require('../helpers'); const socketHelpers = require('../helpers');
module.exports = function (SocketTopics) { module.exports = function (SocketTopics) {
SocketTopics.move = function (socket, data, callback) { SocketTopics.move = async function (socket, data) {
if (!data || !Array.isArray(data.tids) || !data.cid) { 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) { await async.eachLimit(data.tids, 10, async function (tid) {
var topicData; const canMove = await privileges.topics.isAdminOrMod(tid, socket.uid);
async.waterfall([
function (next) {
privileges.topics.isAdminOrMod(tid, socket.uid, next);
},
function (canMove, next) {
if (!canMove) { if (!canMove) {
return next(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
const topicData = await topics.getTopicFields(tid, ['tid', 'cid', 'slug']);
topics.getTopicFields(tid, ['cid', 'slug'], next);
},
function (_topicData, next) {
topicData = _topicData;
topicData.tid = tid;
data.uid = socket.uid; data.uid = socket.uid;
topics.tools.move(tid, data, next); await topics.tools.move(tid, data);
},
function (next) {
socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData); socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData);
socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic'); socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic');
});
next();
},
], next);
}, callback);
}; };
SocketTopics.moveAll = function (socket, data, callback) { SocketTopics.moveAll = async function (socket, data) {
if (!data || !data.cid || !data.currentCid) { 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);
async.waterfall([
function (next) {
privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid, next);
},
function (canMove, next) {
if (!canMove) { if (!canMove) {
return callback(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
categories.getAllTopicIds(data.currentCid, 0, -1, next); const tids = await categories.getAllTopicIds(data.currentCid, 0, -1);
},
function (tids, next) {
data.uid = socket.uid; data.uid = socket.uid;
async.eachLimit(tids, 50, function (tid, next) { await async.eachLimit(tids, 50, async function (tid) {
topics.tools.move(tid, data, next); await topics.tools.move(tid, data);
}, next); });
},
], callback);
}; };
}; };

@ -1,67 +1,49 @@
'use strict'; 'use strict';
var async = require('async'); const topics = require('../../topics');
var db = require('../../database'); const categories = require('../../categories');
var topics = require('../../topics'); const privileges = require('../../privileges');
var privileges = require('../../privileges'); const utils = require('../../utils');
var utils = require('../../utils');
module.exports = function (SocketTopics) { module.exports = function (SocketTopics) {
SocketTopics.isTagAllowed = function (socket, data, callback) { SocketTopics.isTagAllowed = async function (socket, data) {
if (!data || !utils.isNumber(data.cid) || !data.tag) { 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) { const tagWhitelist = await categories.getTagWhitelist([data.cid]);
db.getSortedSetRange('cid:' + data.cid + ':tag:whitelist', 0, -1, next); return !tagWhitelist[0].length || tagWhitelist[0].includes(data.tag);
},
function (tagWhitelist, next) {
next(null, !tagWhitelist.length || tagWhitelist.includes(data.tag));
},
], callback);
}; };
SocketTopics.autocompleteTags = function (socket, data, callback) { SocketTopics.autocompleteTags = async function (socket, data) {
topics.autocompleteTags(data, callback); return await topics.autocompleteTags(data);
}; };
SocketTopics.searchTags = function (socket, data, callback) { SocketTopics.searchTags = async function (socket, data) {
searchTags(socket.uid, topics.searchTags, data, callback); return await searchTags(socket.uid, topics.searchTags, data);
}; };
SocketTopics.searchAndLoadTags = function (socket, data, callback) { SocketTopics.searchAndLoadTags = async function (socket, data) {
searchTags(socket.uid, topics.searchAndLoadTags, data, callback); return await searchTags(socket.uid, topics.searchAndLoadTags, data);
}; };
function searchTags(uid, method, data, callback) { async function searchTags(uid, method, data) {
async.waterfall([ const allowed = await privileges.global.can('search:tags', uid);
function (next) {
privileges.global.can('search:tags', uid, next);
},
function (allowed, next) {
if (!allowed) { if (!allowed) {
return next(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
method(data, next); return await method(data);
},
], callback);
} }
SocketTopics.loadMoreTags = function (socket, data, callback) { SocketTopics.loadMoreTags = async function (socket, data) {
if (!data || !utils.isNumber(data.after)) { 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); const start = parseInt(data.after, 10);
var stop = start + 99; const stop = start + 99;
async.waterfall([ const tags = await topics.getTags(start, stop);
function (next) {
topics.getTags(start, stop, next); return { tags: tags.filter(Boolean), nextStart: stop + 1 };
},
function (tags, next) {
tags = tags.filter(Boolean);
next(null, { tags: tags, nextStart: stop + 1 });
},
], callback);
}; };
}; };

@ -1,127 +1,104 @@
'use strict'; 'use strict';
var async = require('async'); const topics = require('../../topics');
const events = require('../../events');
var topics = require('../../topics'); const privileges = require('../../privileges');
var events = require('../../events'); const plugins = require('../../plugins');
var privileges = require('../../privileges'); const socketHelpers = require('../helpers');
var plugins = require('../../plugins');
var socketHelpers = require('../helpers');
module.exports = function (SocketTopics) { module.exports = function (SocketTopics) {
SocketTopics.loadTopicTools = function (socket, data, callback) { SocketTopics.loadTopicTools = async function (socket, data) {
if (!socket.uid) { if (!socket.uid) {
return callback(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
if (!data) { if (!data) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
async.waterfall([ const [topicData, userPrivileges] = await Promise.all([
function (next) { topics.getTopicData(data.tid),
async.parallel({ privileges.topics.get(data.tid, socket.uid),
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; if (!topicData) {
plugins.fireHook('filter:topic.thread_tools', { topic: results.topic, uid: socket.uid, tools: [] }, next); throw new Error('[[error:no-topic]]');
}, }
function (data, next) { if (!userPrivileges['topics:read']) {
data.topic.thread_tools = data.tools; throw new Error('[[error:no-privileges]]');
next(null, data.topic); }
}, topicData.privileges = userPrivileges;
], callback); 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.delete = async function (socket, data) {
SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data, callback); await SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data);
}; };
SocketTopics.restore = function (socket, data, callback) { SocketTopics.restore = async function (socket, data) {
SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data, callback); await SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data);
}; };
SocketTopics.purge = function (socket, data, callback) { SocketTopics.purge = async function (socket, data) {
SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data, callback); await SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data);
}; };
SocketTopics.lock = function (socket, data, callback) { SocketTopics.lock = async function (socket, data) {
SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data, callback); await SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data);
}; };
SocketTopics.unlock = function (socket, data, callback) { SocketTopics.unlock = async function (socket, data) {
SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data, callback); await SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data);
}; };
SocketTopics.pin = function (socket, data, callback) { SocketTopics.pin = async function (socket, data) {
SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data, callback); await SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data);
}; };
SocketTopics.unpin = function (socket, data, callback) { SocketTopics.unpin = async function (socket, data) {
SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data, callback); await SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data);
}; };
SocketTopics.doTopicAction = function (action, event, socket, data, callback) { SocketTopics.doTopicAction = async function (action, event, socket, data) {
callback = callback || function () {};
if (!socket.uid) { if (!socket.uid) {
return callback(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
if (!data || !Array.isArray(data.tids) || !data.cid) { 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') { if (typeof topics.tools[action] !== 'function') {
return callback(); return;
} }
await Promise.all(data.tids.map(async function (tid) {
async.each(data.tids, function (tid, next) { const title = await topics.getTopicField(tid, 'title');
var title; const data = await topics.tools[action](tid, socket.uid);
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); socketHelpers.emitToTopicAndCategory(event, data);
logTopicAction(action, socket, tid, title, next); await logTopicAction(action, socket, tid, title);
}, }));
], next);
}, callback);
}; };
function logTopicAction(action, socket, tid, title, callback) { async function logTopicAction(action, socket, tid, title) {
var actionsToLog = ['delete', 'restore', 'purge']; var actionsToLog = ['delete', 'restore', 'purge'];
if (!actionsToLog.includes(action)) { if (!actionsToLog.includes(action)) {
return setImmediate(callback); return;
} }
events.log({ await events.log({
type: 'topic-' + action, type: 'topic-' + action,
uid: socket.uid, uid: socket.uid,
ip: socket.ip, ip: socket.ip,
tid: tid, tid: tid,
title: String(title), title: String(title),
}, callback); });
} }
SocketTopics.orderPinnedTopics = function (socket, data, callback) { SocketTopics.orderPinnedTopics = async function (socket, data) {
if (!Array.isArray(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);
}; };
}; };

@ -1,122 +1,71 @@
'use strict'; 'use strict';
var async = require('async'); const user = require('../../user');
const topics = require('../../topics');
var user = require('../../user');
var topics = require('../../topics');
module.exports = function (SocketTopics) { module.exports = function (SocketTopics) {
SocketTopics.markAsRead = function (socket, tids, callback) { SocketTopics.markAsRead = async function (socket, tids) {
if (!Array.isArray(tids) || socket.uid <= 0) { if (!Array.isArray(tids) || socket.uid <= 0) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
async.waterfall([ const hasMarked = await topics.markAsRead(tids, socket.uid);
function (next) {
topics.markAsRead(tids, socket.uid, next);
},
function (hasMarked, next) {
if (hasMarked) { if (hasMarked) {
topics.pushUnreadCount(socket.uid); topics.pushUnreadCount(socket.uid);
topics.markTopicNotificationsRead(tids, socket.uid); topics.markTopicNotificationsRead(tids, socket.uid);
} }
next();
},
], callback);
}; };
SocketTopics.markTopicNotificationsRead = function (socket, tids, callback) { SocketTopics.markTopicNotificationsRead = async function (socket, tids) {
if (!Array.isArray(tids) || !socket.uid) { 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) { if (socket.uid <= 0) {
return callback(new Error('[[error:invalid-uid]]')); throw new Error('[[error:invalid-uid]]');
} }
async.waterfall([ await topics.markAllRead(socket.uid);
function (next) {
topics.markAllRead(socket.uid, next);
},
function (next) {
topics.pushUnreadCount(socket.uid); topics.pushUnreadCount(socket.uid);
next();
},
], callback);
}; };
SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) { SocketTopics.markCategoryTopicsRead = async function (socket, cid) {
async.waterfall([ const tids = await topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' });
function (next) { await SocketTopics.markAsRead(socket, tids);
topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' }, next);
},
function (tids, next) {
SocketTopics.markAsRead(socket, tids, next);
},
], callback);
}; };
SocketTopics.markUnread = function (socket, tid, callback) { SocketTopics.markUnread = async function (socket, tid) {
if (!tid || socket.uid <= 0) { if (!tid || socket.uid <= 0) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
async.waterfall([ await topics.markUnread(tid, socket.uid);
function (next) {
topics.markUnread(tid, socket.uid, next);
},
function (next) {
topics.pushUnreadCount(socket.uid); topics.pushUnreadCount(socket.uid);
next();
},
], callback);
}; };
SocketTopics.markAsUnreadForAll = function (socket, tids, callback) { SocketTopics.markAsUnreadForAll = async function (socket, tids) {
if (!Array.isArray(tids)) { if (!Array.isArray(tids)) {
return callback(new Error('[[error:invalid-tid]]')); throw new Error('[[error:invalid-tid]]');
} }
if (socket.uid <= 0) { 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([ await Promise.all(tids.map(async (tid) => {
function (next) { const topicData = await topics.getTopicFields(tid, ['tid', 'cid']);
user.isAdministrator(socket.uid, next); if (!topicData.tid) {
}, throw new Error('[[error:no-topic]]');
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); const isMod = await user.isModerator(socket.uid, topicData.cid);
},
function (cid, next) {
user.isModerator(socket.uid, cid, next);
},
function (isMod, next) {
if (!isAdmin && !isMod) { if (!isAdmin && !isMod) {
return next(new Error('[[error:no-privileges]]')); throw new Error('[[error:no-privileges]]');
} }
topics.markAsUnreadForAll(tid, next); await topics.markAsUnreadForAll(tid);
}, await topics.updateRecent(tid, Date.now());
function (next) { }));
topics.updateRecent(tid, Date.now(), next);
},
], next);
}, next);
},
function (next) {
topics.pushUnreadCount(socket.uid); topics.pushUnreadCount(socket.uid);
next();
},
], callback);
}; };
}; };

@ -1,12 +1,12 @@
'use strict'; 'use strict';
var db = require('../database'); const db = require('../database');
var posts = require('../posts'); const posts = require('../posts');
var notifications = require('../notifications'); const notifications = require('../notifications');
var privileges = require('../privileges'); const privileges = require('../privileges');
var plugins = require('../plugins'); const plugins = require('../plugins');
var utils = require('../utils'); const utils = require('../utils');
module.exports = function (Topics) { module.exports = function (Topics) {
Topics.toggleFollow = async function (tid, uid) { Topics.toggleFollow = async function (tid, uid) {
@ -90,9 +90,7 @@ module.exports = function (Topics) {
return tids.map(() => ({ following: false, ignoring: false })); return tids.map(() => ({ following: false, ignoring: false }));
} }
const keys = []; const keys = [];
tids.forEach((tid) => { tids.forEach(tid => keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers'));
keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers');
});
const data = await db.isMemberOfSets(keys, uid); const data = await db.isMemberOfSets(keys, uid);
@ -113,7 +111,7 @@ module.exports = function (Topics) {
if (parseInt(uid, 10) <= 0) { if (parseInt(uid, 10) <= 0) {
return tids.map(() => false); 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); return await db.isMemberOfSets(keys, uid);
} }

Loading…
Cancel
Save