Files
indiekit-server/scripts/patch-ap-interactions-send-guard.mjs
Sven 2211b1d6f7
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m14s
fix: activitypun fav/boost
2026-04-01 14:27:45 +02:00

121 lines
4.1 KiB
JavaScript

/**
* Patch: guard sendActivity calls in likePost and boostPost with try/catch.
*
* Root cause:
* - likePost: `ctx.sendActivity({ identifier }, recipient, like, ...)` is not
* wrapped in try/catch. If Fedify or the underlying queue (Redis) throws, the
* error propagates up through the route handler → 500, and the ap_interactions
* DB write that follows is never reached (like not recorded locally either).
*
* - boostPost: `ctx.sendActivity({ identifier }, "followers", announce, ...)` is
* the very first await in the function — also not wrapped. Same consequence:
* any delivery error aborts the function before the DB write, returning 500.
*
* Fix:
* Wrap both sendActivity calls in try/catch so federation delivery failures are
* non-fatal. The interaction is still recorded in ap_interactions so the client
* sees the correct UI state. Delivery of the boost to the original post author is
* already guarded (separate try/catch added previously).
*/
import { access, readFile, writeFile } from "node:fs/promises";
const MARKER = "// [patch] ap-interactions-send-guard";
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-activitypub/lib/mastodon/helpers/interactions.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/mastodon/helpers/interactions.js",
];
// ── Change 1: guard likePost sendActivity ─────────────────────────────────────
const OLD_LIKE_SEND = ` if (recipient) {
await ctx.sendActivity({ identifier: handle }, recipient, like, {
orderingKey: targetUrl,
});
}`;
const NEW_LIKE_SEND = ` if (recipient) {
try { ${MARKER}
await ctx.sendActivity({ identifier: handle }, recipient, like, {
orderingKey: targetUrl,
});
} catch { /* delivery failed — interaction still recorded locally */ }
}`;
// ── Change 2: guard boostPost sendActivity("followers") ──────────────────────
const OLD_BOOST_SEND = ` // Send to followers
await ctx.sendActivity({ identifier: handle }, "followers", announce, {
preferSharedInbox: true,
syncCollection: true,
orderingKey: targetUrl,
});`;
const NEW_BOOST_SEND = ` // Send to followers
try { ${MARKER}
await ctx.sendActivity({ identifier: handle }, "followers", announce, {
preferSharedInbox: true,
syncCollection: true,
orderingKey: targetUrl,
});
} catch { /* delivery failed — interaction still recorded locally */ }`;
async function exists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
let checked = 0;
let patched = 0;
for (const filePath of candidates) {
if (!(await exists(filePath))) continue;
checked += 1;
const source = await readFile(filePath, "utf8");
if (source.includes(MARKER)) {
console.log(`[postinstall] patch-ap-interactions-send-guard: already applied to ${filePath}`);
continue;
}
let updated = source;
let changes = 0;
if (!updated.includes(OLD_LIKE_SEND)) {
console.warn(`[postinstall] patch-ap-interactions-send-guard: likePost snippet not found in ${filePath}`);
} else {
updated = updated.replace(OLD_LIKE_SEND, NEW_LIKE_SEND);
changes++;
}
if (!updated.includes(OLD_BOOST_SEND)) {
console.warn(`[postinstall] patch-ap-interactions-send-guard: boostPost snippet not found in ${filePath}`);
} else {
updated = updated.replace(OLD_BOOST_SEND, NEW_BOOST_SEND);
changes++;
}
if (changes === 0) {
console.log(`[postinstall] patch-ap-interactions-send-guard: no changes in ${filePath}`);
continue;
}
await writeFile(filePath, updated, "utf8");
patched += 1;
console.log(`[postinstall] Applied patch-ap-interactions-send-guard to ${filePath} (${changes}/2 changes)`);
}
if (checked === 0) {
console.log("[postinstall] patch-ap-interactions-send-guard: no target files found");
} else if (patched === 0) {
console.log("[postinstall] patch-ap-interactions-send-guard: already up to date");
} else {
console.log(`[postinstall] patch-ap-interactions-send-guard: patched ${patched} file(s)`);
}