fix: issues related to adding new reply chains to an existing topic, resolveId method in notes module

This commit is contained in:
Julian Lam
2024-01-16 10:44:47 -05:00
parent 7e89eadb36
commit 2981f663ce
2 changed files with 69 additions and 19 deletions

View File

@@ -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)) {
const object = await activitypub.get(uid, item);
postData = await activitypub.mocks.post(object); postData = await activitypub.mocks.post(object);
if (postData) { } else {
await db.setObject(key, postData); 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);

View File

@@ -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);