fix: scope email notifications to genuine inbox deliveries

This commit is contained in:
Linus Rath
2026-05-01 02:08:13 +02:00
parent 2dea33e698
commit 3f97e6ed8d
2 changed files with 15 additions and 7 deletions
+6 -1
View File
@@ -127,8 +127,13 @@ export async function GET(request: NextRequest) {
}, },
}); });
} catch (error) { } catch (error) {
// `fetch failed` from undici is too generic to debug — the real reason
// (ENOTFOUND, ECONNREFUSED, TLS error, …) is on `error.cause`.
const err = error as Error & { cause?: { code?: string; message?: string } };
logger.error('push preview failed', { logger.error('push preview failed', {
error: error instanceof Error ? error.message : 'Unknown error', error: err?.message ?? 'Unknown error',
causeCode: err?.cause?.code,
causeMessage: err?.cause?.message,
}); });
return NextResponse.json({ error: 'Internal error' }, { status: 500 }); return NextResponse.json({ error: 'Internal error' }, { status: 500 });
} }
+9 -6
View File
@@ -46,6 +46,7 @@ async function handlePush(event) {
// expired, server down) we fall back to a generic "New mail" so the user // expired, server down) we fall back to a generic "New mail" so the user
// still sees something. // still sees something.
let preview = null; let preview = null;
let previewOk = false;
try { try {
const res = await fetch("/api/push/preview", { const res = await fetch("/api/push/preview", {
credentials: "include", credentials: "include",
@@ -53,6 +54,7 @@ async function handlePush(event) {
}); });
if (res.ok) { if (res.ok) {
preview = await res.json(); preview = await res.json();
previewOk = true;
} }
} catch (_) { } catch (_) {
preview = null; preview = null;
@@ -63,12 +65,13 @@ async function handlePush(event) {
? preview.unreadTotal ? preview.unreadTotal
: 0; : 0;
// The push subscription is scoped to EmailDelivery, but we still see the // Push subscription is scoped to EmailDelivery, but stragglers from the
// occasional wake-up that does not correspond to a new unread message // older broader-types subscription, marking-as-read races and verification
// (legacy subscription with broader types, races with marking-as-read, // pings can still wake us with no actual unread mail. When the preview API
// verification pings). Without a concrete unread email to surface, stay // succeeded and reports zero unread, stay silent. When the preview API
// silent rather than firing a generic "New mail" toast for a non-event. // failed (network/auth/server down) we cannot tell, so fall through to the
if (!email && unreadTotal === 0) { // generic "New mail" toast rather than miss a real delivery.
if (previewOk && !email && unreadTotal === 0) {
return; return;
} }