mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-10 17:42:52 +01:00
feat: upgrade script to remote duplicate remote users and categories as per #13352
This commit is contained in:
89
src/upgrades/4.3.0/fix_duplicate_handles.js
Normal file
89
src/upgrades/4.3.0/fix_duplicate_handles.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
const user = require('../../user');
|
||||
const categories = require('../../categories');
|
||||
const activitypub = require('../../activitypub');
|
||||
|
||||
const db = require('../../database');
|
||||
|
||||
module.exports = {
|
||||
// you should use spaces
|
||||
// the underscores are there so you can double click to select the whole thing
|
||||
name: 'Fix duplicate accounts sharing identical handles',
|
||||
// remember, month is zero-indexed (so January is 0, December is 11)
|
||||
timestamp: Date.UTC(2025, 3, 29),
|
||||
method: async function () {
|
||||
const { progress } = this;
|
||||
|
||||
// Build list of duplicate remote user handles
|
||||
let handles = await db.getSortedSetMembers('ap.preferredUsername:sorted');
|
||||
handles = handles.map(handle => handle.split(':https://')[0]);
|
||||
const duplicateUsers = new Set();
|
||||
handles.forEach((handle, idx) => {
|
||||
if (handles.indexOf(handle) !== idx) {
|
||||
duplicateUsers.add(handle);
|
||||
}
|
||||
});
|
||||
|
||||
// Build list of duplicate remote category handles
|
||||
handles = await db.getSortedSetMembers('categories:name');
|
||||
handles = handles
|
||||
.filter(handle => handle.indexOf('@') !== -1) // zset contains category names too
|
||||
.map(handle => handle.split(':https://')[0]);
|
||||
const duplicateCategories = new Set();
|
||||
handles.forEach((handle, idx) => {
|
||||
if (handles.indexOf(handle) !== idx) {
|
||||
duplicateCategories.add(handle);
|
||||
}
|
||||
});
|
||||
|
||||
progress.total = duplicateUsers.size + duplicateCategories.size;
|
||||
|
||||
// Find real user, fix handle reference, delete the rest
|
||||
await Promise.all(Array.from(duplicateUsers).map(async (handle) => {
|
||||
const max = '(' + handle.substr(0, handle.length - 1) + String.fromCharCode(handle.charCodeAt(handle.length - 1) + 1);
|
||||
let ids = await db.getSortedSetRangeByLex('ap.preferredUsername:sorted', `[${handle}`, max);
|
||||
ids = ids.map(id => id.slice(handle.length + 1));
|
||||
const { actorUri: canonicalId } = await activitypub.helpers.query(handle);
|
||||
|
||||
await Promise.all(ids.map(async (id) => {
|
||||
if (id !== canonicalId) {
|
||||
try {
|
||||
await user.deleteAccount(id);
|
||||
} catch (e) {
|
||||
// User doesn't exist, maybe never did.
|
||||
await db.sortedSetRemove('ap.preferredUsername:sorted', `${handle}:${id}`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Fix handle:uid backreference or delete
|
||||
if (canonicalId) {
|
||||
await db.setObjectField('handle:uid', handle, canonicalId);
|
||||
}
|
||||
|
||||
progress.incr();
|
||||
}));
|
||||
|
||||
// Find real category, fix handle reference, delete the rest
|
||||
await Promise.all(Array.from(duplicateCategories).map(async (handle) => {
|
||||
const max = '(' + handle.substr(0, handle.length - 1) + String.fromCharCode(handle.charCodeAt(handle.length - 1) + 1);
|
||||
let ids = await db.getSortedSetRangeByLex('categories:name', `[${handle}`, max);
|
||||
ids = ids.map(id => id.slice(handle.length + 1));
|
||||
const { actorUri: canonicalId } = await activitypub.helpers.query(handle);
|
||||
|
||||
await Promise.all(ids.map(async (id) => {
|
||||
if (id !== canonicalId) {
|
||||
await categories.purge(id, 0);
|
||||
}
|
||||
}));
|
||||
|
||||
// Fix handle:uid backreference or delete
|
||||
if (canonicalId) {
|
||||
await db.setObjectField('handle:cid', handle, canonicalId);
|
||||
}
|
||||
|
||||
progress.incr();
|
||||
}));
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user