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

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

Loading…
Cancel
Save