Push ueber VAPID, Mail ueber SMTP, beide unabhaengig zuschaltbar. Die Schluessel eines Push-Geraets liegen verschluesselt in der Datenbank. Ein Geraet, das der Push-Dienst mit 404 oder 410 ablehnt, wird sofort entfernt; andere Fehler zaehlen hoch und fuehren nach fuenf Versuchen zum selben Ergebnis. Eine Liste toter Abos putzt sich so von allein. Der Nachweis wird vor dem Senden geschrieben, nicht danach: lieber eine verpasste Nachricht als eine, die im Minutentakt erneut versucht wird. Weder Endpunkt noch Schluessel noch Mailinhalt landen im Protokoll. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
54 lines
1.6 KiB
JavaScript
54 lines
1.6 KiB
JavaScript
/*
|
|
* Service Worker des Planers.
|
|
*
|
|
* Er tut genau eine Sache: Push-Nachrichten anzeigen und beim Klick die
|
|
* richtige Seite öffnen. Kein Zwischenspeichern von Seiten — ein halb
|
|
* veralteter Planer wäre schlimmer als gar keiner.
|
|
*/
|
|
|
|
self.addEventListener('install', () => {
|
|
// Sofort übernehmen, nicht auf das Schließen aller Tabs warten.
|
|
self.skipWaiting()
|
|
})
|
|
|
|
self.addEventListener('activate', (ereignis) => {
|
|
ereignis.waitUntil(self.clients.claim())
|
|
})
|
|
|
|
self.addEventListener('push', (ereignis) => {
|
|
let daten = { titel: 'Planer', text: '', pfad: '/' }
|
|
try {
|
|
if (ereignis.data) daten = { ...daten, ...ereignis.data.json() }
|
|
} catch {
|
|
// Kaputte Nutzlast: lieber eine karge Meldung als gar keine.
|
|
}
|
|
|
|
ereignis.waitUntil(
|
|
self.registration.showNotification(daten.titel, {
|
|
body: daten.text,
|
|
lang: 'de',
|
|
// Gleiche Anlässe überschreiben einander, statt sich zu stapeln.
|
|
tag: daten.anlass || 'planer',
|
|
data: { pfad: daten.pfad },
|
|
}),
|
|
)
|
|
})
|
|
|
|
self.addEventListener('notificationclick', (ereignis) => {
|
|
ereignis.notification.close()
|
|
const pfad = (ereignis.notification.data && ereignis.notification.data.pfad) || '/'
|
|
|
|
ereignis.waitUntil(
|
|
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((fenster) => {
|
|
// Ist der Planer schon offen, dorthin wechseln statt ein zweites
|
|
// Fenster aufzumachen.
|
|
for (const f of fenster) {
|
|
if (f.url.includes(self.location.origin)) {
|
|
return f.focus().then(() => f.navigate(pfad))
|
|
}
|
|
}
|
|
return self.clients.openWindow(pfad)
|
|
}),
|
|
)
|
|
})
|