thermograph/static/manifest.webmanifest
Emi Griffith f34ab7ef96 Add PWA + Web Push delivery for weather alerts (#95)
Make the app installable and deliver existing alert notifications as OS
push, alongside the in-app bell.

Backend:
- PushSubscription model (per-device endpoint + keys, owned by a user) and
  register/unregister/test endpoints under /api/v2/push, cookie-auth scoped
  to the user like the subscription routes.
- push.py: VAPID key management (env -> data/vapid.json -> generated) and a
  pywebpush send helper that reports gone endpoints for pruning. No DB coupling.
- notify.py: after creating an in-app Notification, dispatch Web Push to the
  user's devices (guarded — a push failure never affects the in-app write;
  endpoints reported gone are pruned).
- Serve the .webmanifest with the correct media type.

Frontend:
- manifest.webmanifest + 192/maskable icons; <link rel="manifest"> on all pages.
- sw.js: push + notificationclick handlers (push-only; no fetch caching, so it
  doesn't fight the existing IndexedDB cache). Registered globally in nav.js in
  secure contexts.
- push-client.js + a "Notifications on this device" toggle and test-send on the
  /alerts page, subscribing through the existing cookie-aware apiFetch.

Push and service workers require a secure context, so this is active over HTTPS
(or http://localhost) and cleanly no-ops on a plain-HTTP LAN origin.
2026-07-15 23:21:06 +00:00

16 lines
735 B
JSON

{
"name": "Thermograph — how unusual is your weather?",
"short_name": "Thermograph",
"description": "See how recent weather anywhere on Earth stacks up against ~45 years of local climate history, and get alerts when it turns unusual.",
"start_url": ".",
"scope": "./",
"display": "standalone",
"background_color": "#171b21",
"theme_color": "#f0803c",
"icons": [
{ "src": "logo.svg", "sizes": "any", "type": "image/svg+xml" },
{ "src": "logo-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "logo.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
{ "src": "logo-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}