# DD-038 — Ogni notifica ha tre canali: la push dei promemoria parte dal worker **Data:** 29 settembre 2026 **Stato:** Accettata **Contesto** Il requisito è che ogni notifica arrivi su tutti e tre i canali: push, centro notifiche in-app ed email. Due casi non lo rispettavano ([catalogo](../modules/notifiche.md#catalogo-delle-notifiche)): - i **promemoria a 24 e 3 ore** nascono dentro il database (job `pg_cron`, M17), che non può firmare né cifrare una push Web (VAPID, RFC 8291): arrivavano solo in-app e per email; - il **sondaggio pre-partita** partiva solo come push e non scriveva in `notifiche_utente`, quindi non compariva in-app e, dato che le email nascono da quella tabella (DD-036), non arrivava per email. Le altre notifiche (messaggio dello staff, turno palloni, sollecito presenze) partono dall'app su Vercel e avevano già i tre canali. **Decisione** 1. **Sondaggio pre-partita:** la route `apri-sondaggio` scrive anche `notifiche_utente` (tipo nuovo `sondaggio_cacche`, un `upsert` per giocatore ed evento come le altre), per i giocatori attivi che non sono allenatori. La mail parte dal trigger di M22 senza altro lavoro. La push resta com'è. Il vincolo `CHECK` sul tipo si estende con la migration `m24`. 2. **Push dei promemoria:** la manda il **worker `mailer/`**, con un secondo canale accanto alle email. Un trigger accoda ogni promemoria a 24 o 3 ore in `notifiche_push_coda` (stessa struttura di `notifiche_email_coda`); il worker prende il lotto con `FOR UPDATE SKIP LOCKED`, manda la push a tutti i dispositivi iscritti del giocatore e registra l'esito. Usa **lo stesso modulo dell'app** (`src/lib/webpush.server.ts`, senza dipendenze) invece di una copia: il container si costruisce dalla radice del repository. Le iscrizioni che rispondono 404 o 410 vengono eliminate, come nelle route. Le push in coda da oltre 3 ore si scartano: un promemoria arrivato a evento iniziato è solo rumore. 3. La push dei promemoria non dipende dall'interruttore «Email»: dipende solo dai dispositivi iscritti, come le altre push. **Alternative scartate** - Far chiamare all'app dal database (`pg_net`, già abilitata) una route che manda la push → scartata: il database dipenderebbe dall'indirizzo pubblico di Vercel e da un segreto condiviso, e `pg_net` è un'estensione del provider (regola 4 di [PORTABILITA.md](../PORTABILITA.md)). - Vercel Cron → scartato: sul piano gratuito gira una volta al giorno, come già notato in DD-036. - Copiare il modulo push nel worker → scartata: due versioni da tenere allineate per codice che cifra e firma. - Mandare dal worker la push di tutte le notifiche → scartata: le altre partono già dall'app, si avrebbero push doppie. - Un unico stato di coda per email e push → scartata: le due consegne hanno esiti, tentativi e destinatari diversi (una push per dispositivo, un'email per giocatore). **Conseguenze** - Il worker non fa più solo email: gestisce due canali (il documento operativo resta [WORKER_EMAIL.md](../WORKER_EMAIL.md)). Serve la chiave VAPID privata anche sull'host, un segreto in più. Senza le chiavi il worker parte lo stesso e manda solo le email, segnalandolo nel log. - La push dei promemoria dipende dall'host di casa acceso, come le email: se è spento parte alla ripartenza, ma non oltre le 3 ore dalla creazione. - Una tabella nuova (`notifiche_push_coda`) e una migration (`m24_push_promemoria_e_sondaggio`). - Il sondaggio compare nel centro notifiche e arriva per email ai giocatori attivi, non agli allenatori; la push continua ad andare a tutti i dispositivi iscritti tranne quelli degli allenatori, anche di giocatori non più attivi. - Le altre notifiche non cambiano canali e non ricevono push doppie. **Aggiornamento (30 settembre 2026)** — DD-040 esclude dalle email le notifiche del turno palloni: per quei tipi i canali sono due (push e in-app). **Riesame** Se l'host di casa si rivela poco affidabile per una notifica sensibile al tempo come la push, o se il database dovesse comunque chiamare l'app per altro, valutare di nuovo la chiamata dal database all'app.