Fa arrivare le push ad app chiusa e rende diagnosticabile quando non arrivano.

Il testo della notifica viaggia ora cifrato dentro la push (aes128gcm, RFC
8188/8291) invece di essere recuperato dal service worker con una fetch al
risveglio. Era quella fetch a non chiudersi in tempo: ad app chiusa il browser
tiene vivo il worker pochi secondi, showNotification non veniva mai chiamata e
non compariva niente, mentre ad app aperta con la rete calda sembrava tutto a
posto. La POST porta anche Urgency: high, che chiede la consegna immediata
invece di far accumulare i messaggi fino al risveglio del dispositivo.

Cadono i pezzi che esistevano solo per rimediare al payload vuoto: la route
push-messaggio, la coda promemoria_push con la sua scadenza a 12 ore,
messaggioPalloniOggi() e il timeout nel worker. Tutti e tre i mittenti avevano
gia il testo pronto prima di inviare.

Il worker si aggiorna da solo all'avvio e a ogni ritorno in primo piano
(mantieniWorkerPushAggiornato): nella webapp installata quello vecchio puo
sopravvivere a lungo, e senza questo un dispositivo resterebbe fermo alla
versione che va a cercare il testo in rete.

Profilo -> Opzioni ha "Mandami una notifica di prova", visibile solo a notifiche
attive: manda una push a questo dispositivo e riporta stato HTTP, corpo della
risposta e se l'endpoint risulta davvero in push_subscriptions. Senza, "non
arriva" era cieco: ogni prova richiedeva un admin, un evento nello stato giusto
e una seconda persona, e la risposta del servizio push veniva buttata via.
inviaPush torna { stato, corpo } e logga il corpo sui rifiuti.

Dalle prove sul campo: a parita di server, iPhone installato da Home riceve ad
app chiusa. Su Android installato come webapp resta da verificare: il WebAPK e
un'app Android a se, con permesso notifiche (Android 13+) e voce batteria
distinti da quelli di Chrome. Annotato nei limiti noti.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 14:24:07 +02:00
co-authored by Claude Opus 5
parent 2b8ba340a6
commit d8f334007b
26 changed files with 782 additions and 306 deletions
+22 -27
View File
@@ -1,36 +1,31 @@
// Service worker dedicato alle notifiche push del turno palloni.
// Service worker dedicato alle notifiche push di CrAPP.
// Non memorizza nella cache pagine o asset dell'app.
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("install", (event) => event.waitUntil(self.skipWaiting()));
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
async function testoNotifica() {
try {
const sub = await self.registration.pushManager.getSubscription();
if (!sub) return null;
const res = await fetch("/api/public/push-messaggio", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ endpoint: sub.endpoint }),
});
if (!res.ok) return null;
return await res.json();
} catch {
return null;
}
}
self.addEventListener("push", (event) => {
// Il testo arriva cifrato dentro la push: nessuna rete, nessuna attesa. A dispositivo
// dormiente il browser sveglia il worker per pochi secondi e una fetch di troppo lo fa
// terminare prima di `showNotification` — la notifica non appare affatto, mentre ad app
// aperta, con la rete calda, sembra funzionare tutto.
let dati = null;
try {
dati = event.data?.json() ?? null;
} catch {
dati = null;
}
event.waitUntil(
(async () => {
const dati = await testoNotifica();
await self.registration.showNotification(dati?.title ?? "CrAPP · Turno palloni", {
body: dati?.body ?? "Controlla il turno palloni di oggi.",
icon: "/icon-192.png",
badge: "/icon-192.png",
tag: "turno-palloni",
});
})(),
self.registration.showNotification(dati?.title ?? "CrAPP", {
body: dati?.body ?? "Apri l'app per i dettagli.",
icon: "/icon-192.png",
badge: "/icon-192.png",
// Il tag fa sostituire la notifica precedente invece di accumularne una pila;
// `renotify` fa sì che la sostituzione avvisi di nuovo, altrimenti la seconda
// notifica comparirebbe in silenzio e sembrerebbe non essere mai arrivata.
tag: "crapp-notifica",
renotify: true,
}),
);
});