v0.14.0 — Milestone 11: browser push notifications
Extends deadline alerts with a second delivery channel — the roadmap's
"near-zero marginal cost, good for deadline reminders" pairing. No new
external account: VAPID keys are self-generated (web-push library),
and push services (FCM/Mozilla push) are free browser infrastructure.
- push_subscriptions table (same per-user RLS pattern as
saved_opportunities/followed_filters). - PWA manifest (src/app/manifest.ts) + on-brand icon routes reusing
the opengraph-image ImageResponse pattern (src/lib/radar-icon.tsx)
instead of needing external image tools — same radar mark, sized
with a generous safe zone for maskable-icon cropping. - Minimal service worker (public/sw.js): push -> showNotification,
notificationclick -> focus or open the relevant opportunity. - Account page toggle to subscribe/unsubscribe (src/app/account/
push-toggle.tsx), calling server actions directly (no form needed —
the subscription object comes from an async browser API call). - runDeadlineAlerts now also pushes, best-effort, to every subscription
for a user whenever an email reminder fires; a 404/410 response from
the push service (subscription gone) deletes that row automatically.
Verified end-to-end for real: subscribed via the actual browser
permission prompt, triggered the cron, received a real push
notification on a real saved item's 48h reminder.