fix: robust author resolution for like/boost with URL pattern fallback

When lookupObject fails (Authorized Fetch, network issues) and the post
isn't in ap_timeline, likes returned 404 "Could not resolve post author".

Adds shared resolveAuthor() with 3 strategies:
1. lookupObject on post URL → getAttributedTo
2. Timeline + notifications DB lookup
3. Extract author from URL pattern (/users/NAME/, /@NAME/)

Refactors like, unlike, boost controllers to use the shared helper.
This commit is contained in:
Ricardo
2026-02-22 21:33:45 +01:00
parent 77aad65947
commit bd07edefbb
4 changed files with 203 additions and 112 deletions

View File

@@ -4,6 +4,7 @@
*/
import { validateToken } from "../csrf.js";
import { resolveAuthor } from "../resolve-author.js";
/**
* POST /admin/reader/boost — send an Announce activity to followers.
@@ -66,40 +67,38 @@ export function boostController(mountPath, plugin) {
orderingKey: url,
});
// Also send to the original post author (signed request for Authorized Fetch)
try {
const documentLoader = await ctx.getDocumentLoader({
identifier: handle,
});
const remoteObject = await ctx.lookupObject(new URL(url), {
documentLoader,
});
// Also send directly to the original post author
const documentLoader = await ctx.getDocumentLoader({
identifier: handle,
});
const { application } = request.app.locals;
const recipient = await resolveAuthor(
url,
ctx,
documentLoader,
application?.collections,
);
if (
remoteObject &&
typeof remoteObject.getAttributedTo === "function"
) {
const author = await remoteObject.getAttributedTo({ documentLoader });
const recipient = Array.isArray(author) ? author[0] : author;
if (recipient) {
await ctx.sendActivity(
{ identifier: handle },
recipient,
announce,
{ orderingKey: url },
);
}
if (recipient) {
try {
await ctx.sendActivity(
{ identifier: handle },
recipient,
announce,
{ orderingKey: url },
);
console.info(
`[ActivityPub] Sent boost directly to ${recipient.id?.href || "author"}`,
);
} catch (error) {
console.warn(
`[ActivityPub] Direct boost delivery to author failed:`,
error.message,
);
}
} catch (error) {
console.warn(
`[ActivityPub] lookupObject failed for ${url} (boost):`,
error.message,
);
}
// Track the interaction
const { application } = request.app.locals;
const interactions = application?.collections?.get("ap_interactions");
if (interactions) {