You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/user/blocks.js

159 lines
4.5 KiB
JavaScript

'use strict';
var async = require('async');
var LRU = require('lru-cache');
var db = require('../database');
var pubsub = require('../pubsub');
module.exports = function (User) {
User.blocks = {
6 years ago
_cache: new LRU({
max: 100,
length: function () { return 1; },
maxAge: 0,
}),
};
User.blocks.is = function (targetUid, uid, callback) {
User.blocks.list(uid, function (err, blocks) {
callback(err, blocks.includes(parseInt(targetUid, 10)));
});
};
7 years ago
User.blocks.can = function (callerUid, blockerUid, blockeeUid, callback) {
// Guests can't block
if (blockerUid === 0 || blockeeUid === 0) {
return setImmediate(callback, new Error('[[error:cannot-block-guest]]'));
} else if (blockerUid === blockeeUid) {
return setImmediate(callback, new Error('[[error:cannot-block-self]]'));
}
7 years ago
// Administrators and global moderators cannot be blocked
// Only admins/mods can block users as another user
7 years ago
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]]'));
7 years ago
}
if (parseInt(callerUid, 10) !== parseInt(blockerUid, 10) && !results.isCallerAdminOrMod) {
return callback(new Error());
7 years ago
}
next();
7 years ago
},
], callback);
7 years ago
};
User.blocks.list = function (uid, callback) {
7 years ago
if (User.blocks._cache.has(parseInt(uid, 10))) {
return setImmediate(callback, null, 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);
});
};
pubsub.on('user:blocks:cache:del', function (uid) {
User.blocks._cache.del(uid);
});
User.blocks.add = function (targetUid, uid, callback) {
async.waterfall([
Async refactor in place (#7736) * feat: allow both callback&and await * feat: ignore async key * feat: callbackify and promisify in same file * Revert "feat: callbackify and promisify in same file" This reverts commit cea206a9b8e6d8295310074b18cc82a504487862. * feat: no need to store .callbackify * feat: change getTopics to async * feat: remove .async * fix: byScore * feat: rewrite topics/index and social with async/await * fix: rewrite topics/data.js fix issue with async.waterfall, only pass result if its not undefined * feat: add callbackify to redis/psql * feat: psql use await * fix: redis :volcano: * feat: less returns * feat: more await rewrite * fix: redis tests * feat: convert sortedSetAdd rewrite psql transaction to async/await * feat: :dog: * feat: test * feat: log client and query * feat: log bind * feat: more logs * feat: more logs * feat: check perform * feat: dont callbackify transaction * feat: remove logs * fix: main functions * feat: more logs * fix: increment * fix: rename * feat: remove cls * fix: remove console.log * feat: add deprecation message to .async usage * feat: update more dbal methods * fix: redis :voodoo: * feat: fix redis zrem, convert setObject * feat: upgrade getObject methods * fix: psql getObjectField * fix: redis tests * feat: getObjectKeys * feat: getObjectValues * feat: isObjectField * fix: add missing return * feat: delObjectField * feat: incrObjectField * fix: add missing await * feat: remove exposed helpers * feat: list methods * feat: flush/empty * feat: delete * fix: redis delete all * feat: get/set * feat: incr/rename * feat: type * feat: expire * feat: setAdd * feat: setRemove * feat: isSetMember * feat: getSetMembers * feat: setCount, setRemoveRandom * feat: zcard,zcount * feat: sortedSetRank * feat: isSortedSetMember * feat: zincrby * feat: sortedSetLex * feat: processSortedSet * fix: add mising await * feat: debug psql * fix: psql test * fix: test * fix: another test * fix: test fix * fix: psql tests * feat: remove logs * feat: user arrow func use builtin async promises * feat: topic bookmarks * feat: topic.delete * feat: topic.restore * feat: topics.purge * feat: merge * feat: suggested * feat: topics/user.js * feat: topics modules * feat: topics/follow * fix: deprecation msg * feat: fork * feat: topics/posts * feat: sorted/recent * feat: topic/teaser * feat: topics/tools * feat: topics/unread * feat: add back node versions disable deprecation notice wrap async controllers in try/catch * feat: use db directly * feat: promisify in place * fix: redis/psql * feat: deprecation message logs for psql * feat: more logs * feat: more logs * feat: logs again * feat: more logs * fix: call release * feat: restore travis, remove logs * fix: loops * feat: remove .async. usage
6 years ago
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) {
7 years ago
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) {
async.waterfall([
Async refactor in place (#7736) * feat: allow both callback&and await * feat: ignore async key * feat: callbackify and promisify in same file * Revert "feat: callbackify and promisify in same file" This reverts commit cea206a9b8e6d8295310074b18cc82a504487862. * feat: no need to store .callbackify * feat: change getTopics to async * feat: remove .async * fix: byScore * feat: rewrite topics/index and social with async/await * fix: rewrite topics/data.js fix issue with async.waterfall, only pass result if its not undefined * feat: add callbackify to redis/psql * feat: psql use await * fix: redis :volcano: * feat: less returns * feat: more await rewrite * fix: redis tests * feat: convert sortedSetAdd rewrite psql transaction to async/await * feat: :dog: * feat: test * feat: log client and query * feat: log bind * feat: more logs * feat: more logs * feat: check perform * feat: dont callbackify transaction * feat: remove logs * fix: main functions * feat: more logs * fix: increment * fix: rename * feat: remove cls * fix: remove console.log * feat: add deprecation message to .async usage * feat: update more dbal methods * fix: redis :voodoo: * feat: fix redis zrem, convert setObject * feat: upgrade getObject methods * fix: psql getObjectField * fix: redis tests * feat: getObjectKeys * feat: getObjectValues * feat: isObjectField * fix: add missing return * feat: delObjectField * feat: incrObjectField * fix: add missing await * feat: remove exposed helpers * feat: list methods * feat: flush/empty * feat: delete * fix: redis delete all * feat: get/set * feat: incr/rename * feat: type * feat: expire * feat: setAdd * feat: setRemove * feat: isSetMember * feat: getSetMembers * feat: setCount, setRemoveRandom * feat: zcard,zcount * feat: sortedSetRank * feat: isSortedSetMember * feat: zincrby * feat: sortedSetLex * feat: processSortedSet * fix: add mising await * feat: debug psql * fix: psql test * fix: test * fix: another test * fix: test fix * fix: psql tests * feat: remove logs * feat: user arrow func use builtin async promises * feat: topic bookmarks * feat: topic.delete * feat: topic.restore * feat: topics.purge * feat: merge * feat: suggested * feat: topics/user.js * feat: topics modules * feat: topics/follow * fix: deprecation msg * feat: fork * feat: topics/posts * feat: sorted/recent * feat: topic/teaser * feat: topics/tools * feat: topics/unread * feat: add back node versions disable deprecation notice wrap async controllers in try/catch * feat: use db directly * feat: promisify in place * fix: redis/psql * feat: deprecation message logs for psql * feat: more logs * feat: more logs * feat: logs again * feat: more logs * fix: call release * feat: restore travis, remove logs * fix: loops * feat: remove .async. usage
6 years ago
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) {
7 years ago
User.blocks._cache.del(parseInt(uid, 10));
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
setImmediate(next);
},
], callback);
};
7 years ago
User.blocks.applyChecks = function (block, targetUid, uid, callback) {
User.blocks.can(uid, uid, targetUid, function (err) {
if (err) {
return callback(err);
}
7 years ago
User.blocks.is(targetUid, uid, function (err, is) {
callback(err || (is === block ? new Error('[[error:already-' + (block ? 'blocked' : 'unblocked') + ']]') : null));
});
});
};
7 years ago
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);
7 years ago
};
User.blocks.filter = function (uid, property, set, callback) {
// 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;
set = property;
property = 'uid';
}
if (!Array.isArray(set) || !set.length || !set.every((item) => {
7 years ago
if (!item) {
return false;
}
const check = item.hasOwnProperty(property) ? item[property] : item;
7 years ago
return ['number', 'string'].includes(typeof check);
})) {
return callback(null, 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));
});
callback(null, set);
});
};
};