fix: #8287, dont readd user after deletion

don't add user uid back to users:* sorted sets if they are deleted
upgrade script to fix users:* sorted sets
v1.18.x
Barış Soner Uşaklı 5 years ago
parent 4d6b2ec3ce
commit 9d153fd388

@ -1,16 +1,16 @@
'use strict';
var async = require('async');
var validator = require('validator');
var _ = require('lodash');
const async = require('async');
const validator = require('validator');
const _ = require('lodash');
const db = require('../database');
var user = require('../user');
const user = require('../user');
const topics = require('../topics');
var groups = require('../groups');
var meta = require('../meta');
var plugins = require('../plugins');
var privileges = require('../privileges');
const groups = require('../groups');
const meta = require('../meta');
const plugins = require('../plugins');
const privileges = require('../privileges');
module.exports = function (Posts) {
Posts.getUserInfoForPosts = async function (uids, uid) {
@ -158,7 +158,7 @@ module.exports = function (Posts) {
db.sortedSetRemoveBulk(bulkRemove),
db.sortedSetAddBulk(bulkAdd),
user.incrementUserPostCountBy(toUid, pids.length),
updateReputation(toUid, repChange),
user.incrementUserReputationBy(toUid, repChange),
handleMainPidOwnerChange(postData, toUid),
reduceCounters(postsByUser),
updateTopicPosters(postData, toUid),
@ -171,7 +171,7 @@ module.exports = function (Posts) {
const repChange = posts.reduce((acc, val) => acc + val.votes, 0);
await Promise.all([
user.incrementUserPostCountBy(uid, -posts.length),
updateReputation(uid, -repChange),
user.incrementUserReputationBy(uid, -repChange),
]);
});
}
@ -187,14 +187,6 @@ module.exports = function (Posts) {
});
}
async function updateReputation(uid, change) {
if (!change) {
return;
}
const newReputation = await user.incrementUserFieldBy(uid, 'reputation', change);
await db.sortedSetAdd('users:reputation', newReputation, uid);
}
async function handleMainPidOwnerChange(postData, toUid) {
const tids = _.uniq(postData.map(p => p.tid));
const topicData = await topics.getTopicsFields(tids, ['mainPid', 'timestamp']);
@ -230,7 +222,10 @@ module.exports = function (Posts) {
async function reduceTopicCounts(postsByUser) {
await async.eachSeries(Object.keys(postsByUser), async function (uid) {
const posts = postsByUser[uid];
await user.incrementUserFieldBy(uid, 'topiccount', -posts.length);
const exists = await user.exists(uid);
if (exists) {
await user.incrementUserFieldBy(uid, 'topiccount', -posts.length);
}
});
}
};

@ -179,10 +179,7 @@ module.exports = function (Posts) {
}
const postData = await Posts.getPostFields(pid, ['pid', 'uid', 'tid']);
const newReputation = await user[type === 'upvote' ? 'incrementUserFieldBy' : 'decrementUserFieldBy'](postData.uid, 'reputation', 1);
if (parseInt(postData.uid, 10)) {
await db.sortedSetAdd('users:reputation', newReputation, postData.uid);
}
const newReputation = user.incrementUserReputationBy(postData.uid, type === 'upvote' ? 1 : -1);
await adjustPostVotes(postData, uid, type, unvote);

@ -0,0 +1,58 @@
'use strict';
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Fix user sorted sets',
timestamp: Date.UTC(2020, 4, 2),
method: async function (callback) {
const progress = this.progress;
const nextUid = await db.getObjectField('global', 'nextUid');
const allUids = [];
for (let i = 1; i <= nextUid; i++) {
allUids.push(i);
}
progress.total = nextUid;
let totalUserCount = 0;
await db.delete('user:null');
await db.sortedSetsRemove([
'users:joindate',
'users:reputation',
'users:postcount',
], 'null');
await batch.processArray(allUids, async function (uids) {
progress.incr(uids.length);
const userData = await db.getObjects(uids.map(id => 'user:' + id));
await Promise.all(userData.map(async function (userData, index) {
if (!userData || !userData.uid) {
await db.sortedSetsRemove([
'users:joindate',
'users:reputation',
'users:postcount',
], uids[index]);
if (userData && !userData.uid) {
await db.delete('user:' + uids[index]);
}
return;
}
totalUserCount += 1;
await db.sortedSetAddBulk([
['users:joindate', userData.joindate, uids[index]],
['users:reputation', userData.reputation, uids[index]],
['users:postcount', userData.postcount, uids[index]],
]);
}));
}, {
progress: progress,
batch: 500,
});
await db.setObjectField('global', 'userCount', totalUserCount);
callback();
},
};

@ -41,7 +41,7 @@ require('./blocks')(User);
require('./uploads')(User);
User.exists = async function (uid) {
return await db.exists('user:' + uid);
return await db.isSortedSetMember('users:joindate', uid);
};
User.existsBySlug = async function (userslug) {

@ -67,12 +67,26 @@ module.exports = function (User) {
};
User.incrementUserPostCountBy = async function (uid, value) {
const newpostcount = await User.incrementUserFieldBy(uid, 'postcount', value);
if (parseInt(uid, 10) <= 0) {
return await incrementUserFieldAndSetBy(uid, 'postcount', 'users:postcount', value);
};
User.incrementUserReputationBy = async function (uid, value) {
return await incrementUserFieldAndSetBy(uid, 'reputation', 'users:reputation', value);
};
async function incrementUserFieldAndSetBy(uid, field, set, value) {
value = parseInt(value, 10);
if (!value || !field || !(parseInt(uid, 10) > 0)) {
return;
}
await db.sortedSetAdd('users:postcount', newpostcount, uid);
};
const exists = await User.exists(uid);
if (!exists) {
return;
}
const newValue = await User.incrementUserFieldBy(uid, field, value);
await db.sortedSetAdd(set, newValue, uid);
return newValue;
}
User.getPostIds = async function (uid, start, stop) {
const pids = await db.getSortedSetRevRange('uid:' + uid + ':posts', start, stop);

@ -11,6 +11,7 @@ var db = require('./mocks/databasemock');
var User = require('../src/user');
var Topics = require('../src/topics');
var Categories = require('../src/categories');
var Posts = require('../src/posts');
var Password = require('../src/password');
var groups = require('../src/groups');
var helpers = require('./helpers');
@ -412,6 +413,40 @@ describe('User', function () {
});
});
});
it('should not re-add user to users:postcount if post is deleted after user deletion', async function () {
const uid = await User.create({ username: 'olduserwithposts' });
assert(await db.isSortedSetMember('users:postcount', uid));
const result = await Topics.post({
uid: uid,
title: 'old user topic',
content: 'old user topic post content',
cid: testCid,
});
assert.equal(await db.sortedSetScore('users:postcount', uid), 1);
await User.deleteAccount(uid);
assert(!await db.isSortedSetMember('users:postcount', uid));
await Posts.purge(result.postData.pid, 1);
assert(!await db.isSortedSetMember('users:postcount', uid));
});
it('should not re-add user to users:reputation if post is upvoted after user deletion', async function () {
const uid = await User.create({ username: 'olduserwithpostsupvote' });
assert(await db.isSortedSetMember('users:reputation', uid));
const result = await Topics.post({
uid: uid,
title: 'old user topic',
content: 'old user topic post content',
cid: testCid,
});
assert.equal(await db.sortedSetScore('users:reputation', uid), 0);
await User.deleteAccount(uid);
assert(!await db.isSortedSetMember('users:reputation', uid));
await Posts.upvote(result.postData.pid, 1);
assert(!await db.isSortedSetMember('users:reputation', uid));
});
});
describe('passwordReset', function () {

Loading…
Cancel
Save