mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
chore: move assertTopic to top of file, rename to assert
... despite the diff, that's all this commit did
This commit is contained in:
@@ -15,6 +15,122 @@ const utils = require('../utils');
|
|||||||
const activitypub = module.parent.exports;
|
const activitypub = module.parent.exports;
|
||||||
const Notes = module.exports;
|
const Notes = module.exports;
|
||||||
|
|
||||||
|
Notes.assert = async (uid, id) => {
|
||||||
|
/**
|
||||||
|
* 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));
|
||||||
|
if (!chain.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainPost = chain[chain.length - 1];
|
||||||
|
let { pid: mainPid, tid, uid: authorId, timestamp, name, content } = mainPost;
|
||||||
|
const hasTid = !!tid;
|
||||||
|
|
||||||
|
const members = await db.isSortedSetMembers(`tid:${tid}:posts`, chain.slice(0, -1).map(p => p.pid));
|
||||||
|
members.push(await posts.exists(mainPid));
|
||||||
|
if (tid && members.every(Boolean)) {
|
||||||
|
// All cached, return early.
|
||||||
|
winston.verbose('[notes/assert] No new notes to process.');
|
||||||
|
return tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cid;
|
||||||
|
let title;
|
||||||
|
if (hasTid) {
|
||||||
|
({ cid, mainPid } = await topics.getTopicFields(tid, ['tid', 'cid', 'mainPid']));
|
||||||
|
} else {
|
||||||
|
// mainPid ok to leave as-is
|
||||||
|
cid = -1;
|
||||||
|
title = name || utils.decodeHTMLEntities(utils.stripHTMLTags(content));
|
||||||
|
if (title.length > meta.config.maximumTitleLength) {
|
||||||
|
title = `${title.slice(0, meta.config.maximumTitleLength - 3)}...`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainPid = utils.isNumber(mainPid) ? parseInt(mainPid, 10) : mainPid;
|
||||||
|
|
||||||
|
// Privilege check for local categories
|
||||||
|
const privilege = `topics:${tid ? 'reply' : 'create'}`;
|
||||||
|
const allowed = await privileges.categories.can(privilege, cid, activitypub._constants.uid);
|
||||||
|
if (!allowed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tid = tid || utils.generateUUID();
|
||||||
|
mainPost.tid = tid;
|
||||||
|
|
||||||
|
const unprocessed = chain.map((post) => {
|
||||||
|
post.tid = tid; // add tid to post hash
|
||||||
|
return post;
|
||||||
|
}).filter((p, idx) => !members[idx]);
|
||||||
|
const count = unprocessed.length;
|
||||||
|
winston.verbose(`[notes/assert] ${count} new note(s) found.`);
|
||||||
|
|
||||||
|
const [ids, timestamps] = [
|
||||||
|
unprocessed.map(n => (utils.isNumber(n.pid) ? parseInt(n.pid, 10) : n.pid)),
|
||||||
|
unprocessed.map(n => n.timestamp),
|
||||||
|
];
|
||||||
|
|
||||||
|
// mainPid doesn't belong in posts zset
|
||||||
|
if (ids.includes(mainPid)) {
|
||||||
|
const idx = ids.indexOf(mainPid);
|
||||||
|
ids.splice(idx, 1);
|
||||||
|
timestamps.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags;
|
||||||
|
if (!hasTid) {
|
||||||
|
const { to, cc, attachment } = mainPost._activitypub;
|
||||||
|
const systemTags = (meta.config.systemTags || '').split(',');
|
||||||
|
const maxTags = await categories.getCategoryField(cid, 'maxTags');
|
||||||
|
tags = (mainPost._activitypub.tag || [])
|
||||||
|
.filter(o => o.type === 'Hashtag' && !systemTags.includes(o.name.slice(1)))
|
||||||
|
.map(o => o.name.slice(1));
|
||||||
|
|
||||||
|
if (maxTags && tags.length > maxTags) {
|
||||||
|
tags.length = maxTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
topics.post({
|
||||||
|
tid,
|
||||||
|
uid: authorId,
|
||||||
|
cid,
|
||||||
|
pid: mainPid,
|
||||||
|
title,
|
||||||
|
timestamp,
|
||||||
|
tags,
|
||||||
|
content: mainPost.content,
|
||||||
|
_activitypub: mainPost._activitypub,
|
||||||
|
}),
|
||||||
|
Notes.updateLocalRecipients(mainPid, { to, cc }),
|
||||||
|
Notes.saveAttachments(mainPid, attachment),
|
||||||
|
]);
|
||||||
|
unprocessed.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
unprocessed.reverse();
|
||||||
|
for (const post of unprocessed) {
|
||||||
|
const { to, cc, attachment } = post._activitypub;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await Promise.all([
|
||||||
|
topics.reply(post),
|
||||||
|
Notes.updateLocalRecipients(post.pid, { to, cc }),
|
||||||
|
Notes.saveAttachments(post.pid, attachment),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Notes.syncUserInboxes(tid);
|
||||||
|
|
||||||
|
return { tid, count };
|
||||||
|
};
|
||||||
|
|
||||||
Notes.updateLocalRecipients = async (id, { to, cc }) => {
|
Notes.updateLocalRecipients = async (id, { to, cc }) => {
|
||||||
const recipients = new Set([...(to || []), ...(cc || [])]);
|
const recipients = new Set([...(to || []), ...(cc || [])]);
|
||||||
const uids = new Set();
|
const uids = new Set();
|
||||||
@@ -123,122 +239,6 @@ Notes.getParentChain = async (uid, input) => {
|
|||||||
return chain;
|
return chain;
|
||||||
};
|
};
|
||||||
|
|
||||||
Notes.assertTopic = async (uid, id) => {
|
|
||||||
/**
|
|
||||||
* 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));
|
|
||||||
if (!chain.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainPost = chain[chain.length - 1];
|
|
||||||
let { pid: mainPid, tid, uid: authorId, timestamp, name, content } = mainPost;
|
|
||||||
const hasTid = !!tid;
|
|
||||||
|
|
||||||
const members = await db.isSortedSetMembers(`tid:${tid}:posts`, chain.slice(0, -1).map(p => p.pid));
|
|
||||||
members.push(await posts.exists(mainPid));
|
|
||||||
if (tid && members.every(Boolean)) {
|
|
||||||
// All cached, return early.
|
|
||||||
winston.verbose('[notes/assertTopic] No new notes to process.');
|
|
||||||
return tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cid;
|
|
||||||
let title;
|
|
||||||
if (hasTid) {
|
|
||||||
({ cid, mainPid } = await topics.getTopicFields(tid, ['tid', 'cid', 'mainPid']));
|
|
||||||
} else {
|
|
||||||
// mainPid ok to leave as-is
|
|
||||||
cid = -1;
|
|
||||||
title = name || utils.decodeHTMLEntities(utils.stripHTMLTags(content));
|
|
||||||
if (title.length > meta.config.maximumTitleLength) {
|
|
||||||
title = `${title.slice(0, meta.config.maximumTitleLength - 3)}...`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mainPid = utils.isNumber(mainPid) ? parseInt(mainPid, 10) : mainPid;
|
|
||||||
|
|
||||||
// Privilege check for local categories
|
|
||||||
const privilege = `topics:${tid ? 'reply' : 'create'}`;
|
|
||||||
const allowed = await privileges.categories.can(privilege, cid, activitypub._constants.uid);
|
|
||||||
if (!allowed) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tid = tid || utils.generateUUID();
|
|
||||||
mainPost.tid = tid;
|
|
||||||
|
|
||||||
const unprocessed = chain.map((post) => {
|
|
||||||
post.tid = tid; // add tid to post hash
|
|
||||||
return post;
|
|
||||||
}).filter((p, idx) => !members[idx]);
|
|
||||||
const count = unprocessed.length;
|
|
||||||
winston.verbose(`[notes/assertTopic] ${count} new note(s) found.`);
|
|
||||||
|
|
||||||
const [ids, timestamps] = [
|
|
||||||
unprocessed.map(n => (utils.isNumber(n.pid) ? parseInt(n.pid, 10) : n.pid)),
|
|
||||||
unprocessed.map(n => n.timestamp),
|
|
||||||
];
|
|
||||||
|
|
||||||
// mainPid doesn't belong in posts zset
|
|
||||||
if (ids.includes(mainPid)) {
|
|
||||||
const idx = ids.indexOf(mainPid);
|
|
||||||
ids.splice(idx, 1);
|
|
||||||
timestamps.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tags;
|
|
||||||
if (!hasTid) {
|
|
||||||
const { to, cc, attachment } = mainPost._activitypub;
|
|
||||||
const systemTags = (meta.config.systemTags || '').split(',');
|
|
||||||
const maxTags = await categories.getCategoryField(cid, 'maxTags');
|
|
||||||
tags = (mainPost._activitypub.tag || [])
|
|
||||||
.filter(o => o.type === 'Hashtag' && !systemTags.includes(o.name.slice(1)))
|
|
||||||
.map(o => o.name.slice(1));
|
|
||||||
|
|
||||||
if (maxTags && tags.length > maxTags) {
|
|
||||||
tags.length = maxTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
topics.post({
|
|
||||||
tid,
|
|
||||||
uid: authorId,
|
|
||||||
cid,
|
|
||||||
pid: mainPid,
|
|
||||||
title,
|
|
||||||
timestamp,
|
|
||||||
tags,
|
|
||||||
content: mainPost.content,
|
|
||||||
_activitypub: mainPost._activitypub,
|
|
||||||
}),
|
|
||||||
Notes.updateLocalRecipients(mainPid, { to, cc }),
|
|
||||||
Notes.saveAttachments(mainPid, attachment),
|
|
||||||
]);
|
|
||||||
unprocessed.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
unprocessed.reverse();
|
|
||||||
for (const post of unprocessed) {
|
|
||||||
const { to, cc, attachment } = post._activitypub;
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await Promise.all([
|
|
||||||
topics.reply(post),
|
|
||||||
Notes.updateLocalRecipients(post.pid, { to, cc }),
|
|
||||||
Notes.saveAttachments(post.pid, attachment),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Notes.syncUserInboxes(tid);
|
|
||||||
|
|
||||||
return { tid, count };
|
|
||||||
};
|
|
||||||
|
|
||||||
Notes.syncUserInboxes = async function (tid) {
|
Notes.syncUserInboxes = async function (tid) {
|
||||||
const [pids, { cid, mainPid }] = await Promise.all([
|
const [pids, { cid, mainPid }] = await Promise.all([
|
||||||
db.getSortedSetMembers(`tid:${tid}:posts`),
|
db.getSortedSetMembers(`tid:${tid}:posts`),
|
||||||
|
|||||||
Reference in New Issue
Block a user