mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
fix(mastodon-api): favourite/reblog blocks on unbound resolveAuthor HTTP requests
likePost, unlikePost and boostPost all call resolveAuthor() which makes up to 3 signed HTTP requests to the remote server (post fetch, author actor fetch, getAttributedTo) with no timeout. If the remote server is slow or unreachable, the favourite/reblog HTTP response hangs until the Node.js socket default times out (~2 min). Mastodon clients (Phanpy, Elk) give up much sooner and show "Failed to load post". Fix: wrap every resolveAuthor() call in a Promise.race() with a 5 s timeout. The interaction is still recorded in ap_interactions and the Like/Announce activity is still sent when recipient resolution succeeds within the window; if it times out, AP delivery is silently skipped (the local record is kept — the client sees a successful ⭐). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,15 @@ export async function likePost({ targetUrl, federation, handle, publicationUrl,
|
|||||||
);
|
);
|
||||||
|
|
||||||
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
||||||
const recipient = await resolveAuthor(targetUrl, ctx, documentLoader, collections);
|
// resolveAuthor makes up to 3 signed HTTP requests to the remote server.
|
||||||
|
// Cap at 5 s so a slow/unreachable remote never blocks the client response.
|
||||||
|
let recipient = null;
|
||||||
|
try {
|
||||||
|
recipient = await Promise.race([
|
||||||
|
resolveAuthor(targetUrl, ctx, documentLoader, collections),
|
||||||
|
new Promise((_, reject) => setTimeout(() => reject(new Error("resolveAuthor timeout")), 5000)),
|
||||||
|
]);
|
||||||
|
} catch { /* skip AP delivery — interaction is still recorded locally */ }
|
||||||
|
|
||||||
const uuid = crypto.randomUUID();
|
const uuid = crypto.randomUUID();
|
||||||
const baseUrl = publicationUrl.replace(/\/$/, "");
|
const baseUrl = publicationUrl.replace(/\/$/, "");
|
||||||
@@ -95,7 +103,13 @@ export async function unlikePost({ targetUrl, federation, handle, publicationUrl
|
|||||||
);
|
);
|
||||||
|
|
||||||
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
||||||
const recipient = await resolveAuthor(targetUrl, ctx, documentLoader, collections);
|
let recipient = null;
|
||||||
|
try {
|
||||||
|
recipient = await Promise.race([
|
||||||
|
resolveAuthor(targetUrl, ctx, documentLoader, collections),
|
||||||
|
new Promise((_, reject) => setTimeout(() => reject(new Error("resolveAuthor timeout")), 5000)),
|
||||||
|
]);
|
||||||
|
} catch { /* skip AP delivery */ }
|
||||||
|
|
||||||
if (recipient) {
|
if (recipient) {
|
||||||
const like = new Like({
|
const like = new Like({
|
||||||
@@ -160,9 +174,15 @@ export async function boostPost({ targetUrl, federation, handle, publicationUrl,
|
|||||||
orderingKey: targetUrl,
|
orderingKey: targetUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also send directly to the original post author
|
// Also send directly to the original post author (best-effort, 5 s cap)
|
||||||
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
const documentLoader = await ctx.getDocumentLoader({ identifier: handle });
|
||||||
const recipient = await resolveAuthor(targetUrl, ctx, documentLoader, collections);
|
let recipient = null;
|
||||||
|
try {
|
||||||
|
recipient = await Promise.race([
|
||||||
|
resolveAuthor(targetUrl, ctx, documentLoader, collections),
|
||||||
|
new Promise((_, reject) => setTimeout(() => reject(new Error("resolveAuthor timeout")), 5000)),
|
||||||
|
]);
|
||||||
|
} catch { /* skip author delivery — follower delivery already happened */ }
|
||||||
if (recipient) {
|
if (recipient) {
|
||||||
try {
|
try {
|
||||||
await ctx.sendActivity({ identifier: handle }, recipient, announce, {
|
await ctx.sendActivity({ identifier: handle }, recipient, announce, {
|
||||||
|
|||||||
Reference in New Issue
Block a user