From 763de7fd0581f149f718d115ad44e9ec9aac92a9 Mon Sep 17 00:00:00 2001 From: zv Date: Fri, 12 Jun 2026 21:33:41 +0200 Subject: [PATCH] fix: handle notification endpoint failures --- app/api/notifications/check/route.ts | 79 ++++++++++++---------- app/api/notifications/daily-brief/route.ts | 7 +- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/app/api/notifications/check/route.ts b/app/api/notifications/check/route.ts index f0e7820..f323084 100644 --- a/app/api/notifications/check/route.ts +++ b/app/api/notifications/check/route.ts @@ -26,6 +26,10 @@ function isCronAuthorized(request: Request) { return authHeader === `Bearer ${secret}` || secretHeader === secret; } +function getErrorMessage(error: unknown) { + return error instanceof Error ? error.message : "Unknown notification check error."; +} + export async function GET(request: Request) { if (!isCronAuthorized(request)) { return NextResponse.json({ error: "Unauthorized." }, { status: 401 }); @@ -34,44 +38,51 @@ export async function GET(request: Request) { return NextResponse.json({ error: "Web Push is not configured." }, { status: 503 }); } - const now = Date.now(); - const warnings = (await fetchMeteoWarnings(AbortSignal.timeout(12_000))).filter((warning) => isRelevantWarning(warning, now)); - const subscriptions = getPushSubscriptions().filter((subscription) => subscription.enabled); - const activeWarningIds = new Set(warnings.map((warning) => warning.id)); - let sent = 0; - let skipped = 0; - let failed = 0; + try { + const now = Date.now(); + const warnings = (await fetchMeteoWarnings(AbortSignal.timeout(12_000))).filter((warning) => isRelevantWarning(warning, now)); + const subscriptions = getPushSubscriptions().filter((subscription) => subscription.enabled); + const activeWarningIds = new Set(warnings.map((warning) => warning.id)); + let sent = 0; + let skipped = 0; + let failed = 0; - pruneSentWarnings(activeWarningIds); + pruneSentWarnings(activeWarningIds); - for (const subscription of subscriptions) { - const matchingWarnings = warnings.filter((warning) => ( - subscription.countyTeryt ? warningMatchesCounty(warning, subscription.countyTeryt) : warning.provinces.includes(subscription.province) - )); - for (const warning of matchingWarnings) { - if (hasSentWarning(subscription.endpoint, warning.id)) { - skipped += 1; - continue; - } + for (const subscription of subscriptions) { + const matchingWarnings = warnings.filter((warning) => ( + subscription.countyTeryt ? warningMatchesCounty(warning, subscription.countyTeryt) : warning.provinces.includes(subscription.province) + )); + for (const warning of matchingWarnings) { + if (hasSentWarning(subscription.endpoint, warning.id)) { + skipped += 1; + continue; + } - try { - await sendWarningNotification(subscription, warning); - markWarningSent(subscription.endpoint, warning.id); - sent += 1; - } catch (error) { - failed += 1; - const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null; - if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint); + try { + await sendWarningNotification(subscription, warning); + markWarningSent(subscription.endpoint, warning.id); + sent += 1; + } catch (error) { + failed += 1; + const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null; + if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint); + } } } - } - return NextResponse.json({ - ok: true, - subscriptions: subscriptions.length, - warnings: warnings.length, - sent, - skipped, - failed, - }); + return NextResponse.json({ + ok: true, + subscriptions: subscriptions.length, + warnings: warnings.length, + sent, + skipped, + failed, + }); + } catch (error) { + return NextResponse.json({ + error: "Unable to check IMGW meteorological warnings.", + details: getErrorMessage(error), + }, { status: 502 }); + } } diff --git a/app/api/notifications/daily-brief/route.ts b/app/api/notifications/daily-brief/route.ts index 9aec50f..7aef425 100644 --- a/app/api/notifications/daily-brief/route.ts +++ b/app/api/notifications/daily-brief/route.ts @@ -42,7 +42,11 @@ export async function GET(request: Request) { && Number.isFinite(subscription.latitude) && Number.isFinite(subscription.longitude) )); - const warnings = await fetchMeteoWarnings(AbortSignal.timeout(12_000)); + let warningsUnavailable = false; + const warnings = await fetchMeteoWarnings(AbortSignal.timeout(12_000)).catch(() => { + warningsUnavailable = true; + return []; + }); let sent = 0; let skipped = 0; let failed = 0; @@ -82,6 +86,7 @@ export async function GET(request: Request) { ok: true, date: dateKey, subscriptions: subscriptions.length, + warningsUnavailable, sent, skipped, failed,