diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index c31546b..51c7684 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -124,11 +124,22 @@ const app = new Hono<{ Bindings: CloudflareBindings }>() EMBED_USER_AGENT: c.env.EMBED_USER_AGENT, }); } catch (cause) { - const problem = createProblem(EmbedlyErrors.PlatformFetchFailed, { + const errorContext = getErrorContext(cause); + let event = EmbedlyErrors.PlatformFetchFailed; + if (platform === "Instagram") { + if (errorContext.upstream_reason === "instagram.age_restricted") { + event = EmbedlyErrors.InstagramAgeRestricted; + } else if (errorContext.upstream_reason === "instagram.post_unavailable") { + event = EmbedlyErrors.InstagramPostUnavailable; + } else if (errorContext.upstream_reason === "instagram.media_unavailable") { + event = EmbedlyErrors.InstagramMediaUnavailable; + } + } + const problem = createProblem(event, { request_id: requestId, - context: { ...logContext, ...getErrorContext(cause) }, + context: { ...logContext, ...errorContext }, }); - Object.assign(logContext, getErrorContext(cause), { + Object.assign(logContext, errorContext, { outcome: "error", status_code: problem.status, error_type: problem.type, diff --git a/packages/logging/src/main.ts b/packages/logging/src/main.ts index c9042a6..ed5bc3a 100644 --- a/packages/logging/src/main.ts +++ b/packages/logging/src/main.ts @@ -8,6 +8,7 @@ export interface ErrorContext { error_message?: string; upstream_status?: LogValue; upstream_message?: LogValue; + upstream_reason?: LogValue; } export interface EmbedlyEvent { @@ -62,6 +63,25 @@ export const EmbedlyErrors = { detail: "Embedly could not fetch that post from the platform.", status: 502, }), + InstagramAgeRestricted: defineError({ + type: "instagram.age_restricted", + title: "Age-restricted content.", + detail: "Instagram requires login to view this post. Embedly can only access public posts.", + status: 403, + }), + InstagramPostUnavailable: defineError({ + type: "instagram.post_unavailable", + title: "Post unavailable.", + detail: "Instagram isn't making this post available without login.", + status: 404, + }), + InstagramMediaUnavailable: defineError({ + type: "platform.fetch_failed", + title: "Couldn't load this post.", + detail: + "Instagram didn't provide public media for this post. It may require login or be unavailable.", + status: 502, + }), PlatformTransformFailed: defineError({ type: "platform.transform_failed", title: "Failed to build post data.", @@ -250,6 +270,7 @@ export function getErrorContext(error: unknown): ErrorContext { if ("code" in error) context.upstream_status = toLogValue(error.code); if ("status" in error) context.upstream_status = toLogValue(error.status); if ("message" in error) context.upstream_message = toLogValue(error.message); + if ("reason" in error) context.upstream_reason = toLogValue(error.reason); return context; } diff --git a/packages/platforms/src/platforms/instagram.ts b/packages/platforms/src/platforms/instagram.ts index 8f37eea..933bd09 100644 --- a/packages/platforms/src/platforms/instagram.ts +++ b/packages/platforms/src/platforms/instagram.ts @@ -101,24 +101,36 @@ export const Instagram: Platform<"Instagram", InstagramMedia, {}> = { (text) => text.includes("RelayPrefetchedStreamCache") && text.includes(PRELOADER_PREFIX), ); - if (!script) { - throw { - code: 500, - message: "Instagram page structure changed: missing data script", - }; - } - let media: InstagramMedia | undefined | null; - try { - media = parseRelayMedia(script); - } catch { - throw { code: 500, message: "Failed to parse Instagram data" }; + if (script) { + try { + media = parseRelayMedia(script); + } catch { + throw { code: 500, message: "Failed to parse Instagram data" }; + } } if (!media) { + $("script, style").remove(); + const text = $("body") + .text() + .replace(/\s+/g, " ") + .replace(/\u2019/g, "'"); + let reason = "instagram.media_unavailable"; + let code = 500; + if (text.includes("Age-restricted content")) { + reason = "instagram.age_restricted"; + code = 403; + } else if (text.includes("Post isn't available")) { + reason = "instagram.post_unavailable"; + code = 404; + } throw { - code: 500, - message: "Instagram page structure changed: missing media data", + code, + reason, + message: script + ? "Instagram page structure changed: missing media data" + : "Instagram page structure changed: missing data script", }; }