feat: #7743 , user/block, user/categories

v1.18.x
Barış Soner Uşaklı 6 years ago
parent 6f738c2b44
commit 4541caa4f8

@ -16,117 +16,84 @@ module.exports = function (User) {
}), }),
}; };
User.blocks.is = function (targetUid, uid, callback) { User.blocks.is = async function (targetUid, uid) {
User.blocks.list(uid, function (err, blocks) { const blocks = await User.blocks.list(uid);
callback(err, blocks.includes(parseInt(targetUid, 10))); return blocks.includes(parseInt(targetUid, 10));
});
}; };
User.blocks.can = function (callerUid, blockerUid, blockeeUid, callback) { User.blocks.can = async function (callerUid, blockerUid, blockeeUid) {
// Guests can't block // Guests can't block
if (blockerUid === 0 || blockeeUid === 0) { if (blockerUid === 0 || blockeeUid === 0) {
return setImmediate(callback, new Error('[[error:cannot-block-guest]]')); throw new Error('[[error:cannot-block-guest]]');
} else if (blockerUid === blockeeUid) { } else if (blockerUid === blockeeUid) {
return setImmediate(callback, new Error('[[error:cannot-block-self]]')); throw new Error('[[error:cannot-block-self]]');
} }
// Administrators and global moderators cannot be blocked // Administrators and global moderators cannot be blocked
// Only admins/mods can block users as another user // Only admins/mods can block users as another user
async.waterfall([ const [isCallerAdminOrMod, isBlockeeAdminOrMod] = await Promise.all([
function (next) { User.isAdminOrGlobalMod(callerUid),
async.parallel({ User.isAdminOrGlobalMod(blockeeUid),
isCallerAdminOrMod: function (next) { ]);
User.isAdminOrGlobalMod(callerUid, next); if (isBlockeeAdminOrMod) {
}, throw new Error('[[error:cannot-block-privileged]]');
isBlockeeAdminOrMod: function (next) {
User.isAdminOrGlobalMod(blockeeUid, next);
},
}, next);
},
function (results, next) {
if (results.isBlockeeAdminOrMod) {
return callback(new Error('[[error:cannot-block-privileged]]'));
} }
if (parseInt(callerUid, 10) !== parseInt(blockerUid, 10) && !results.isCallerAdminOrMod) { if (parseInt(callerUid, 10) !== parseInt(blockerUid, 10) && !isCallerAdminOrMod) {
return callback(new Error()); throw new Error();
} }
next();
},
], callback);
}; };
User.blocks.list = function (uid, callback) { User.blocks.list = async function (uid) {
if (User.blocks._cache.has(parseInt(uid, 10))) { if (User.blocks._cache.has(parseInt(uid, 10))) {
return setImmediate(callback, null, User.blocks._cache.get(parseInt(uid, 10))); return User.blocks._cache.get(parseInt(uid, 10));
}
db.getSortedSetRange('uid:' + uid + ':blocked_uids', 0, -1, function (err, blocked) {
if (err) {
return callback(err);
} }
let blocked = await db.getSortedSetRange('uid:' + uid + ':blocked_uids', 0, -1);
blocked = blocked.map(uid => parseInt(uid, 10)).filter(Boolean); blocked = blocked.map(uid => parseInt(uid, 10)).filter(Boolean);
User.blocks._cache.set(parseInt(uid, 10), blocked); User.blocks._cache.set(parseInt(uid, 10), blocked);
callback(null, blocked); return blocked;
});
}; };
pubsub.on('user:blocks:cache:del', function (uid) { pubsub.on('user:blocks:cache:del', function (uid) {
User.blocks._cache.del(uid); User.blocks._cache.del(uid);
}); });
User.blocks.add = function (targetUid, uid, callback) { User.blocks.add = async function (targetUid, uid) {
async.waterfall([ await User.blocks.applyChecks('block', targetUid, uid);
async.apply(User.blocks.applyChecks, true, targetUid, uid), await db.sortedSetAdd('uid:' + uid + ':blocked_uids', Date.now(), targetUid);
async.apply(db.sortedSetAdd.bind(db), 'uid:' + uid + ':blocked_uids', Date.now(), targetUid), await User.incrementUserFieldBy(uid, 'blocksCount', 1);
async.apply(User.incrementUserFieldBy, uid, 'blocksCount', 1),
function (_blank, next) {
User.blocks._cache.del(parseInt(uid, 10)); User.blocks._cache.del(parseInt(uid, 10));
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
setImmediate(next);
},
], callback);
}; };
User.blocks.remove = function (targetUid, uid, callback) { User.blocks.remove = async function (targetUid, uid) {
async.waterfall([ await User.blocks.applyChecks('unblock', targetUid, uid);
async.apply(User.blocks.applyChecks, false, targetUid, uid), await db.sortedSetRemove('uid:' + uid + ':blocked_uids', targetUid);
async.apply(db.sortedSetRemove.bind(db), 'uid:' + uid + ':blocked_uids', targetUid), await User.decrementUserFieldBy(uid, 'blocksCount', 1);
async.apply(User.decrementUserFieldBy, uid, 'blocksCount', 1),
function (_blank, next) {
User.blocks._cache.del(parseInt(uid, 10)); User.blocks._cache.del(parseInt(uid, 10));
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10)); pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
setImmediate(next);
},
], callback);
}; };
User.blocks.applyChecks = function (block, targetUid, uid, callback) { User.blocks.applyChecks = async function (type, targetUid, uid) {
User.blocks.can(uid, uid, targetUid, function (err) { await User.blocks.can(uid, uid, targetUid);
if (err) { const isBlock = type === 'block';
return callback(err); const is = await User.blocks.is(targetUid, uid);
if (is === isBlock) {
throw new Error('[[error:already-' + (isBlock ? 'blocked' : 'unblocked') + ']]');
} }
User.blocks.is(targetUid, uid, function (err, is) {
callback(err || (is === block ? new Error('[[error:already-' + (block ? 'blocked' : 'unblocked') + ']]') : null));
});
});
}; };
User.blocks.filterUids = function (targetUid, uids, callback) { User.blocks.filterUids = async function (targetUid, uids) {
async.filter(uids, function (uid, next) { return await async.filter(uids, async function (uid) {
User.blocks.is(targetUid, uid, function (err, blocked) { const isBlocked = await User.blocks.is(targetUid, uid);
next(err, !blocked); return !isBlocked;
}); });
}, callback);
}; };
User.blocks.filter = function (uid, property, set, callback) { User.blocks.filter = async function (uid, property, set) {
// Given whatever is passed in, iterates through it, and removes entries made by blocked uids // Given whatever is passed in, iterates through it, and removes entries made by blocked uids
// property is optional // property is optional
if (Array.isArray(property) && typeof set === 'function' && !callback) { if (Array.isArray(property) && typeof set === 'undefined') {
callback = set;
set = property; set = property;
property = 'uid'; property = 'uid';
} }
@ -139,20 +106,17 @@ module.exports = function (User) {
const check = item.hasOwnProperty(property) ? item[property] : item; const check = item.hasOwnProperty(property) ? item[property] : item;
return ['number', 'string'].includes(typeof check); return ['number', 'string'].includes(typeof check);
})) { })) {
return callback(null, set); return set;
} }
const isPlain = typeof set[0] !== 'object'; const isPlain = typeof set[0] !== 'object';
User.blocks.list(uid, function (err, blocked_uids) { const blocked_uids = await User.blocks.list(uid);
if (err) { const blockedSet = new Set(blocked_uids);
return callback(err);
}
set = set.filter(function (item) { set = set.filter(function (item) {
return !blocked_uids.includes(parseInt(isPlain ? item : item[property], 10)); return !blockedSet.has(parseInt(isPlain ? item : item[property], 10));
}); });
callback(null, set); return set;
});
}; };
}; };

@ -1,89 +1,64 @@
'use strict'; 'use strict';
const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
const db = require('../database'); const db = require('../database');
const categories = require('../categories'); const categories = require('../categories');
module.exports = function (User) { module.exports = function (User) {
User.setCategoryWatchState = function (uid, cid, state, callback) { User.setCategoryWatchState = async function (uid, cid, state) {
if (!(parseInt(uid, 10) > 0)) { if (!(parseInt(uid, 10) > 0)) {
return setImmediate(callback); return;
} }
const isStateValid = Object.keys(categories.watchStates).some(key => categories.watchStates[key] === parseInt(state, 10)); const isStateValid = Object.values(categories.watchStates).includes(parseInt(state, 10));
if (!isStateValid) { if (!isStateValid) {
return setImmediate(callback, new Error('[[error:invalid-watch-state]]')); throw new Error('[[error:invalid-watch-state]]');
} }
async.waterfall([ const exists = await categories.exists(cid);
function (next) {
categories.exists(cid, next);
},
function (exists, next) {
if (!exists) { if (!exists) {
return next(new Error('[[error:no-category]]')); throw new Error('[[error:no-category]]');
} }
await db.sortedSetAdd('cid:' + cid + ':uid:watch:state', state, uid);
db.sortedSetAdd('cid:' + cid + ':uid:watch:state', state, uid, next);
},
], callback);
}; };
User.getCategoryWatchState = function (uid, callback) { User.getCategoryWatchState = async function (uid) {
if (parseInt(uid, 10) <= 0) { if (!(parseInt(uid, 10) > 0)) {
return setImmediate(callback, null, {}); return {};
} }
let cids; const cids = await categories.getAllCidsFromSet('categories:cid');
async.waterfall([ const states = await categories.getWatchState(cids, uid);
function (next) { return _.zipObject(cids, states);
categories.getAllCidsFromSet('categories:cid', next);
},
function (_cids, next) {
cids = _cids;
categories.getWatchState(cids, uid, next);
},
function (states, next) {
next(null, _.zipObject(cids, states));
},
], callback);
}; };
User.getIgnoredCategories = function (uid, callback) { User.getIgnoredCategories = async function (uid) {
if (parseInt(uid, 10) <= 0) { if (!(parseInt(uid, 10) > 0)) {
return setImmediate(callback, null, []); return [];
} }
User.getCategoriesByStates(uid, [categories.watchStates.ignoring], callback); return await User.getCategoriesByStates(uid, [categories.watchStates.ignoring]);
}; };
User.getWatchedCategories = function (uid, callback) { User.getWatchedCategories = async function (uid) {
if (parseInt(uid, 10) <= 0) { if (!(parseInt(uid, 10) > 0)) {
return setImmediate(callback, null, []); return [];
} }
User.getCategoriesByStates(uid, [categories.watchStates.watching], callback); return await User.getCategoriesByStates(uid, [categories.watchStates.watching]);
}; };
User.getCategoriesByStates = function (uid, states, callback) { User.getCategoriesByStates = async function (uid, states) {
if (!(parseInt(uid, 10) > 0)) { if (!(parseInt(uid, 10) > 0)) {
return categories.getAllCidsFromSet('categories:cid', callback); return await categories.getAllCidsFromSet('categories:cid');
} }
const userState = await User.getCategoryWatchState(uid);
async.waterfall([
function (next) {
User.getCategoryWatchState(uid, next);
},
function (userState, next) {
const cids = Object.keys(userState); const cids = Object.keys(userState);
next(null, cids.filter(cid => states.includes(userState[cid]))); return cids.filter(cid => states.includes(userState[cid]));
},
], callback);
}; };
User.ignoreCategory = function (uid, cid, callback) { User.ignoreCategory = async function (uid, cid) {
User.setCategoryWatchState(uid, cid, categories.watchStates.ignoring, callback); await User.setCategoryWatchState(uid, cid, categories.watchStates.ignoring);
}; };
User.watchCategory = function (uid, cid, callback) { User.watchCategory = async function (uid, cid) {
User.setCategoryWatchState(uid, cid, categories.watchStates.watching, callback); await User.setCategoryWatchState(uid, cid, categories.watchStates.watching);
}; };
}; };

Loading…
Cancel
Save