Add incoming webhooks, fix stale service worker and push re-registration

- Webhooks for automations: per-job secret, Gitea/GitHub HMAC signatures,
  payload appended to the instructions; settings on the automations page
- Service worker gets a new file name per build (proxies that cache *.js
  served a stale worker, which broke updates and Android installation);
  /sw.js is a kill switch that removes old workers
- Push: devices re-register their subscription with the server on start and
  before a test; the test reports why a push service rejected a message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 00:38:04 +01:00
1 parent 6dffcb501a
commit 10d4b64289
16 files changed
+267 -18

No files matched your search

+6 -4
View File
@@ -138,23 +138,25 @@ fn vapid_header(key: &SecretKey, endpoint: &str, subject: &str) -> Result<String
Ok(format!("vapid t={jwt}, k={k}"))
}
/// Send a notification to all devices of a user.
pub async fn send_to_user(app: &App, user_id: &str, payload: &Value) -> usize {
/// Send a notification to all devices of a user: (delivered, error messages).
pub async fn send_to_user(app: &App, user_id: &str, payload: &Value) -> (usize, Vec<String>) {
let subs = app.store.push_subscriptions(user_id).unwrap_or_default();
let mut sent = 0;
let mut errors = Vec::new();
for sub in subs {
match send_one(app, &sub.endpoint, &sub.p256dh, &sub.auth, payload).await {
Ok(()) => sent += 1,
Err(e) => {
let gone = e.to_string().contains("410") || e.to_string().contains("404");
tracing::debug!("push to {}: {e:#}", sub.endpoint);
tracing::warn!("push to {}…: {e:#}", sub.endpoint.chars().take(40).collect::<String>());
errors.push(format!("{e:#}").chars().take(200).collect());
if gone {
let _ = app.store.push_remove(&sub.endpoint);
}
}
}
}
sent
(sent, errors)
}
async fn send_one(app: &App, endpoint: &str, p256dh: &str, auth: &str, payload: &Value) -> Result<()> {