From 30dc527cc0ec5bfbe4c0cfd459ef5a517a645871 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 20 Oct 2025 12:17:23 -0400 Subject: [PATCH] feat: unwind announce(delete), federate out Remove(Context) on delete, but not on purge --- src/activitypub/inbox.js | 24 ++--------- src/activitypub/index.js | 1 + src/api/activitypub.js | 92 +++++++++++++++------------------------- src/api/topics.js | 5 +-- src/topics/delete.js | 3 +- 5 files changed, 43 insertions(+), 82 deletions(-) diff --git a/src/activitypub/inbox.js b/src/activitypub/inbox.js index 9fe4a52a4d..14fe460a53 100644 --- a/src/activitypub/inbox.js +++ b/src/activitypub/inbox.js @@ -182,6 +182,7 @@ inbox.update = async (req) => { inbox.delete = async (req) => { const { actor, object } = req.body; + console.log(actor, object); if (typeof object !== 'string') { const { id } = object; if (!id) { @@ -200,8 +201,6 @@ inbox.delete = async (req) => { if (type === 'Tombstone') { method = 'delete'; // soft delete - } else if (activitypub._constants.acceptable.contextTypes.includes(type)) { - method = 'move'; // move to cid -1 } } catch (e) { // probably 410/404 @@ -221,11 +220,7 @@ inbox.delete = async (req) => { // db.isSortedSetMember('usersRemote:lastCrawled', object.id), ]); - // 'move' method only applicable for contexts - if (method === 'move' && !isContext) { - return reject('Delete', object, actor); - } - + console.log(isNote, isContext); switch (true) { case isNote: { const cid = await posts.getCidByPid(id); @@ -248,13 +243,8 @@ inbox.delete = async (req) => { return; } const { tid, uid } = await posts.getPostFields(pid, ['tid', 'uid']); - if (method === 'move') { - activitypub.helpers.log(`[activitypub/inbox.delete] Moving tid ${tid} to cid -1.`); - await api.topics.move({ uid }, { tid, cid: -1 }); - } else { - activitypub.helpers.log(`[activitypub/inbox.delete] Deleting tid ${tid}.`); - await api.topics[method]({ uid }, { tids: [tid] }); - } + activitypub.helpers.log(`[activitypub/inbox.delete] Deleting tid ${tid}.`); + await api.topics[method]({ uid }, { tids: [tid] }); break; } @@ -347,12 +337,6 @@ inbox.announce = async (req) => { break; } - case object.type === 'Delete': { - req.body = object; - await inbox.delete(req); - break; - } - case object.type === 'Create': { object = object.object; // falls through diff --git a/src/activitypub/index.js b/src/activitypub/index.js index 7b84148600..e6a8e447d5 100644 --- a/src/activitypub/index.js +++ b/src/activitypub/index.js @@ -406,6 +406,7 @@ async function sendMessage(uri, id, type, payload) { } ActivityPub.send = async (type, id, targets, payload) => { + console.log('sending', payload, targets); if (!meta.config.activitypubEnabled) { return ActivityPub.helpers.log('[activitypub/send] Federation not enabled; not sending.'); } diff --git a/src/api/activitypub.js b/src/api/activitypub.js index 3d1d3ff546..bf302054ea 100644 --- a/src/api/activitypub.js +++ b/src/api/activitypub.js @@ -412,63 +412,6 @@ activitypubApi.announce.user = enabledCheck(async (caller, { tid }) => { }); }); -activitypubApi.announce.delete = enabledCheck(async ({ uid }, { tid }) => { - const now = new Date(); - const { mainPid: pid, cid } = await topics.getTopicFields(tid, ['mainPid', 'cid']); - - // Only local categories - if (!utils.isNumber(cid) || parseInt(cid, 10) < 1) { - return; - } - - const allowed = await privileges.categories.can('topics:read', cid, activitypub._constants.uid); - if (!allowed) { - activitypub.helpers.log(`[activitypub/api] Not federating announce of pid ${pid} to the fediverse due to privileges.`); - return; - } - - const { to, cc, targets } = await activitypub.buildRecipients({ - to: [activitypub._constants.publicAddress], - cc: [`${nconf.get('url')}/category/${cid}/followers`], - }, { cid }); - - const deleteTpl = { - id: `${nconf.get('url')}/topic/${tid}#activity/delete/${now.getTime()}`, - type: 'Delete', - actor: `${nconf.get('url')}/category/${cid}`, - to, - cc, - origin: `${nconf.get('url')}/category/${cid}`, - }; - - // 7888 variant - await activitypub.send('cid', cid, Array.from(targets), { - id: `${nconf.get('url')}/topic/${tid}#activity/announce/delete/${now.getTime()}`, - type: 'Announce', - actor: `${nconf.get('url')}/category/${cid}`, - to, - cc, - object: { - ...deleteTpl, - object: `${nconf.get('url')}/topic/${tid}`, - }, - }); - - // 1b12 variant - await activitypub.send('cid', cid, Array.from(targets), { - id: `${nconf.get('url')}/post/${encodeURIComponent(pid)}#activity/announce/delete/${now.getTime()}`, - type: 'Announce', - actor: `${nconf.get('url')}/category/${cid}`, - to, - cc, - object: { - ...deleteTpl, - actor: `${nconf.get('url')}/uid/${uid}`, - object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid, - }, - }); -}); - activitypubApi.undo = {}; // activitypubApi.undo.follow = @@ -573,3 +516,38 @@ activitypubApi.undo.flag = enabledCheck(async (caller, flag) => { }); await db.sortedSetRemove(`flag:${flag.flagId}:remote`, caller.uid); }); + +activitypubApi.remove = {}; + +activitypubApi.remove.context = enabledCheck(async ({ uid }, { tid }) => { + // Federates Remove(Context); where Context is the tid + const now = new Date(); + const cid = await topics.getTopicField(tid, 'cid'); + + // Only local categories + if (!utils.isNumber(cid) || parseInt(cid, 10) < 1) { + return; + } + + const allowed = await privileges.categories.can('topics:read', cid, activitypub._constants.uid); + if (!allowed) { + activitypub.helpers.log(`[activitypub/api] Not federating deletion of tid ${tid} to the fediverse due to privileges.`); + return; + } + + const { to, cc, targets } = await activitypub.buildRecipients({ + to: [activitypub._constants.publicAddress], + cc: [`${nconf.get('url')}/category/${cid}/followers`], + }, { cid }); + + // Remove(Context) + await activitypub.send('cid', cid, Array.from(targets), { + id: `${nconf.get('url')}/topic/${tid}#activity/remove/${now.getTime()}`, + type: 'Remove', + actor: `${nconf.get('url')}/uid/${uid}`, + to, + cc, + object: `${nconf.get('url')}/topic/${tid}`, + origin: `${nconf.get('url')}/category/${cid}`, + }); +}); \ No newline at end of file diff --git a/src/api/topics.js b/src/api/topics.js index 38468f5956..50252ebdad 100644 --- a/src/api/topics.js +++ b/src/api/topics.js @@ -321,13 +321,12 @@ topicsAPI.move = async (caller, { tid, cid }) => { if (!topicData.deleted) { socketHelpers.sendNotificationToTopicOwner(tid, caller.uid, 'move', 'notifications:moved-your-topic'); - // AP: Announce(Delete(Object)) if (cid === -1) { - await activitypubApi.announce.delete({ uid: caller.uid }, { tid }); + // tbd: activitypubApi.move (to null target) // tbd: activitypubApi.undo.announce? } else { activitypubApi.announce.category(caller, { tid }); - // tbd: api.activitypub.announce.move + // tbd: api.activitypub.move } } diff --git a/src/topics/delete.js b/src/topics/delete.js index 3557349d9d..ee9d39e107 100644 --- a/src/topics/delete.js +++ b/src/topics/delete.js @@ -25,7 +25,7 @@ module.exports = function (Topics) { deleterUid: uid, deletedTimestamp: Date.now(), }), - api.activitypub.announce.delete({ uid }, { tid }), + api.activitypub.remove.context({ uid }, { tid }), ]); await categories.updateRecentTidForCid(cid); @@ -82,7 +82,6 @@ module.exports = function (Topics) { } deletedTopic.tags = tags; await deleteFromFollowersIgnorers(tid); - await api.activitypub.announce.delete({ uid }, { tid }), await Promise.all([ db.deleteAll([