mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-31 19:15:58 +01:00
fix: issues related to adding new reply chains to an existing topic, resolveId method in notes module
This commit is contained in:
@@ -8,24 +8,32 @@ const posts = require('../posts');
|
|||||||
const activitypub = module.parent.exports;
|
const activitypub = module.parent.exports;
|
||||||
const Notes = module.exports;
|
const Notes = module.exports;
|
||||||
|
|
||||||
|
Notes.resolveId = async (uid, id) => {
|
||||||
|
({ id } = await activitypub.get(uid, id));
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
// todo: when asserted, notes aren't added to a global sorted set
|
// todo: when asserted, notes aren't added to a global sorted set
|
||||||
// also, db.exists call is probably expensive
|
// also, db.exists call is probably expensive
|
||||||
Notes.assert = async (uid, input) => {
|
Notes.assert = async (uid, input) => {
|
||||||
// Ensures that each note has been saved to the database
|
// Ensures that each note has been saved to the database
|
||||||
await Promise.all(input.map(async (item) => {
|
await Promise.all(input.map(async (item) => {
|
||||||
const id = activitypub.helpers.isUri(item) ? item : item.id;
|
const id = activitypub.helpers.isUri(item) ? item : item.pid;
|
||||||
const key = `post:${id}`;
|
const key = `post:${id}`;
|
||||||
const exists = await db.exists(key);
|
const exists = await db.exists(key);
|
||||||
winston.verbose(`[activitypub/notes.assert] Asserting note id ${id}`);
|
winston.verbose(`[activitypub/notes.assert] Asserting note id ${id}`);
|
||||||
|
|
||||||
let postData;
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
|
let postData;
|
||||||
winston.verbose(`[activitypub/notes.assert] Not found, saving note to database`);
|
winston.verbose(`[activitypub/notes.assert] Not found, saving note to database`);
|
||||||
const object = activitypub.helpers.isUri(item) ? await activitypub.get(uid, item) : item;
|
if (activitypub.helpers.isUri(item)) {
|
||||||
postData = await activitypub.mocks.post(object);
|
const object = await activitypub.get(uid, item);
|
||||||
if (postData) {
|
postData = await activitypub.mocks.post(object);
|
||||||
await db.setObject(key, postData);
|
} else {
|
||||||
|
postData = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await db.setObject(key, postData);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@@ -59,21 +67,41 @@ Notes.getParentChain = async (uid, input) => {
|
|||||||
return chain;
|
return chain;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Notes.assertParentChain = async (chain) => {
|
||||||
|
const data = [];
|
||||||
|
chain.reduce((child, parent) => {
|
||||||
|
data.push([`pid:${parent.pid}:replies`, child.timestamp, child.pid]);
|
||||||
|
return parent;
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sortedSetAddBulk(data);
|
||||||
|
};
|
||||||
|
|
||||||
Notes.assertTopic = async (uid, id) => {
|
Notes.assertTopic = async (uid, id) => {
|
||||||
// Given the id of any post, traverses up (and soon, down) to cache the entire threaded context
|
/**
|
||||||
|
* Given the id of any post, traverses up to cache the entire threaded context
|
||||||
|
*
|
||||||
|
* Unfortunately, due to limitations and fragmentation of the existing ActivityPub landscape,
|
||||||
|
* retrieving the entire reply tree is not possible at this time.
|
||||||
|
*/
|
||||||
|
|
||||||
const chain = Array.from(await Notes.getParentChain(uid, id));
|
const chain = Array.from(await Notes.getParentChain(uid, id));
|
||||||
const tid = chain[chain.length - 1].pid;
|
const tid = chain[chain.length - 1].pid;
|
||||||
|
|
||||||
const sorted = chain.sort((a, b) => a.timestamp - b.timestamp);
|
const members = await db.isSortedSetMembers(`tidRemote:${tid}:posts`, chain.map(p => p.pid));
|
||||||
const [ids, timestamps] = [
|
if (members.every(Boolean)) {
|
||||||
sorted.map(n => n.id),
|
// All cached, return early.
|
||||||
sorted.map(n => n.timestamp),
|
winston.info('[notes/assertTopic] No new notes to process.');
|
||||||
];
|
return tid;
|
||||||
|
}
|
||||||
|
|
||||||
const postercount = chain.reduce((set, cur) => {
|
const unprocessed = chain.filter((p, idx) => !members[idx]);
|
||||||
set.add(cur.uid);
|
winston.info(`[notes/assertTopic] ${unprocessed.length} new notes found.`);
|
||||||
return set;
|
|
||||||
}, new Set());
|
const [ids, timestamps] = [
|
||||||
|
unprocessed.map(n => n.pid),
|
||||||
|
unprocessed.map(n => n.timestamp),
|
||||||
|
];
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
db.setObject(`topicRemote:${tid}`, {
|
db.setObject(`topicRemote:${tid}`, {
|
||||||
@@ -83,16 +111,32 @@ Notes.assertTopic = async (uid, id) => {
|
|||||||
mainPid: tid,
|
mainPid: tid,
|
||||||
title: 'TBD',
|
title: 'TBD',
|
||||||
slug: `remote?resource=${encodeURIComponent(tid)}`,
|
slug: `remote?resource=${encodeURIComponent(tid)}`,
|
||||||
postcount: sorted.length,
|
|
||||||
postercount,
|
|
||||||
}),
|
}),
|
||||||
db.sortedSetAdd(`tidRemote:${tid}:posts`, timestamps, ids),
|
db.sortedSetAdd(`tidRemote:${tid}:posts`, timestamps, ids),
|
||||||
Notes.assert(uid, chain),
|
Notes.assert(uid, unprocessed),
|
||||||
|
]);
|
||||||
|
await Promise.all([ // must be done after .assert()
|
||||||
|
Notes.assertParentChain(chain),
|
||||||
|
Notes.updateTopicCounts(tid),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return tid;
|
return tid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Notes.updateTopicCounts = async function (tid) {
|
||||||
|
const pids = await db.getSortedSetMembers(`tidRemote:${tid}:posts`);
|
||||||
|
let uids = await db.getObjectsFields(pids.map(p => `post:${p}`), ['uid']);
|
||||||
|
uids = uids.reduce((set, { uid }) => {
|
||||||
|
set.add(uid);
|
||||||
|
return set;
|
||||||
|
}, new Set());
|
||||||
|
|
||||||
|
db.setObject(`topicRemote:${tid}`, {
|
||||||
|
postercount: uids.size,
|
||||||
|
postcount: pids.length,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Notes.getTopicPosts = async (tid, uid, start, stop) => {
|
Notes.getTopicPosts = async (tid, uid, start, stop) => {
|
||||||
const pids = await db.getSortedSetRange(`tidRemote:${tid}:posts`, start, stop);
|
const pids = await db.getSortedSetRange(`tidRemote:${tid}:posts`, start, stop);
|
||||||
return await posts.getPostsByPids(pids, uid);
|
return await posts.getPostsByPids(pids, uid);
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ const topics = require('../../topics');
|
|||||||
const { notes } = require('../../activitypub');
|
const { notes } = require('../../activitypub');
|
||||||
// const helpers = require('../helpers');
|
// const helpers = require('../helpers');
|
||||||
const pagination = require('../../pagination');
|
const pagination = require('../../pagination');
|
||||||
|
const helpers = require('../helpers');
|
||||||
|
|
||||||
const controller = module.exports;
|
const controller = module.exports;
|
||||||
|
|
||||||
controller.get = async function (req, res, next) {
|
controller.get = async function (req, res, next) {
|
||||||
|
const pid = await notes.resolveId(req.uid, req.query.resource);
|
||||||
|
if (pid !== req.query.resource) {
|
||||||
|
return helpers.redirect(res, `/topic/remote?resource=${pid}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
const tid = await notes.assertTopic(req.uid, req.query.resource);
|
const tid = await notes.assertTopic(req.uid, req.query.resource);
|
||||||
|
|
||||||
let postIndex = await db.sortedSetRank(`tidRemote:${tid}:posts`, req.query.resource);
|
let postIndex = await db.sortedSetRank(`tidRemote:${tid}:posts`, req.query.resource);
|
||||||
|
|||||||
Reference in New Issue
Block a user