diff --git a/lib/mastodon/helpers/interactions.js b/lib/mastodon/helpers/interactions.js index 3072b02..f8b702c 100644 --- a/lib/mastodon/helpers/interactions.js +++ b/lib/mastodon/helpers/interactions.js @@ -30,7 +30,15 @@ export async function likePost({ targetUrl, federation, handle, publicationUrl, ); 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 baseUrl = publicationUrl.replace(/\/$/, ""); @@ -95,7 +103,13 @@ export async function unlikePost({ targetUrl, federation, handle, publicationUrl ); 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) { const like = new Like({ @@ -160,9 +174,15 @@ export async function boostPost({ targetUrl, federation, handle, publicationUrl, 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 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) { try { await ctx.sendActivity({ identifier: handle }, recipient, announce, {