P4-B: notifications Web Push (VAPID) sur passage en waiting

- db: migration id=4 `push_subscriptions` (liées au token d'auth) ; clés VAPID en settings
- PushService: bootstrap idempotent des clés VAPID, subscribe/unsubscribe/count,
  notify() best-effort (purge des abonnements 410/404 Gone) ; sender injectable pour les tests
- routes/push.ts: GET vapid-public-key, POST subscribe/unsubscribe/test (toutes sous auth globale)
- pty-manager: déclencheur push sur FRONT MONTANT vers waiting, débouncé 1500ms et annulable
  (faux positif ignoré) ; câblage app/index/config (--vapid-contact)
- shared/api.ts: types VapidKeyResponse / PushSubscribeRequest / PushUnsubscribeRequest
- deps: web-push (+ @types/web-push) côté serveur
- tests: push-service (idempotence, UPSERT, 410-purge, payload) + trigger pty-manager (169 verts)
This commit is contained in:
2026-06-15 12:13:56 +02:00
parent 25d6d09d24
commit 664e2b583c
13 changed files with 493 additions and 11 deletions

View File

@@ -51,6 +51,23 @@ const MIGRATIONS: Array<{ id: number; sql: string }> = [
);
`,
},
{
// P4 — abonnements Web Push. Liés au token d'auth (token_id) ; clés VAPID en settings.
id: 4,
sql: `
CREATE TABLE push_subscriptions (
id TEXT PRIMARY KEY,
token_id TEXT NOT NULL,
endpoint TEXT NOT NULL UNIQUE,
p256dh TEXT NOT NULL,
auth TEXT NOT NULL,
user_agent TEXT,
created_at TEXT NOT NULL,
last_ok_at TEXT
);
CREATE INDEX idx_push_subs_token ON push_subscriptions(token_id);
`,
},
];
export type Db = DatabaseSync;