Notify a phone that has this installation installed
A `webpush` alert channel, and the PWA it needs to arrive. The payload is encrypted to the subscription (RFC 8291) and the request signed with this installation's own keypair (RFC 8292), both over `http-ece` — `pywebpush` does the same in one call but brings `requests` and `aiohttp` with it, two HTTP stacks beside httpx on a machine that may be a Raspberry Pi. The manifest and the worker are hand-written rather than `vite-plugin-pwa`: there is nothing worth precaching when the page carrying the credential is `no-store`, so the worker handles `push` and `notificationclick` and nothing else. `registration.scope` is the app's root in both places it runs, which is why the payload carries no URL. A run finishing in error is the first event worth waking someone for; `ok` and `cancelled` describe to nothing, so a nightly batch that works stays quiet. The events were already on the bus — only the filter changed. `WEBPUSH_FILE` is a derived path, so the keypair lands on the data volume with the alerts beside it. Off it, a rebuild would silently stop every phone being notified: the key they subscribed against would be gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EbeFPm6WNC3YD9vrqqT3a
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "Fluksio",
|
||||
"short_name": "Fluksio",
|
||||
"description": "Flows, dashboards and the house they run",
|
||||
"id": "/",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"theme_color": "#59849b",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
||||
{
|
||||
"src": "/icon-512-maskable.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* The service worker, which exists only so a push can arrive when no tab is
|
||||
* open. There is deliberately no `fetch` handler and nothing is cached: the
|
||||
* page this app is served from carries a credential and is sent `no-store`,
|
||||
* so caching it would hand the next visitor somebody else's session.
|
||||
*
|
||||
* `registration.scope` is the app's root in both places it runs — `/` on an
|
||||
* installation of its own, `/i/{id}/` through the portal — which is why the
|
||||
* push payload does not carry a URL.
|
||||
*/
|
||||
|
||||
self.addEventListener("push", (event) => {
|
||||
const alert = event.data ? event.data.json() : {}
|
||||
const scope = new URL(self.registration.scope)
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(alert.title || "Fluksio", {
|
||||
body: alert.body || "",
|
||||
// Under the portal the icons are the shared bundle's, not this
|
||||
// installation's path.
|
||||
icon: scope.pathname.startsWith("/i/")
|
||||
? "/app-shell/icon-192.png"
|
||||
: `${scope.pathname}icon-192.png`,
|
||||
// The same fault repeating replaces its notification rather than
|
||||
// stacking another one up.
|
||||
tag: alert.flow || alert.title || "fluksio",
|
||||
timestamp: Date.now(),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
event.notification.close()
|
||||
const scope = self.registration.scope
|
||||
event.waitUntil(
|
||||
self.clients
|
||||
.matchAll({ type: "window", includeUncontrolled: true })
|
||||
.then((clients) => {
|
||||
// A tab on this installation is already open: raise it rather than
|
||||
// opening a second one.
|
||||
const open = clients.find((client) => client.url.startsWith(scope))
|
||||
if (open) return open.focus()
|
||||
return self.clients.openWindow(scope)
|
||||
}),
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user