Admin health: push devices, pushes sent, expired devices and the last failure

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-28 12:52:05 -04:00
1 parent 551f970721
commit c4b830dd43
5 files changed
+18 -4

No files matched your search

+13 -3
View File
@@ -36,13 +36,23 @@ function localNow(tz, at = new Date()) {
return { day: `${p.year}-${p.month}-${p.day}`, hm: `${p.hour}:${p.minute}` };
}
// Since the server started: how many pushes went out, and what the push service last refused (shown to the admin in Settings > Admin)
const stats = { sent: 0, failed: 0, expired: 0, lastOk: 0, lastErr: '', lastErrAt: 0 };
function health() {
let devices = 0, people = 0;
for (const u of users.all()) { if (u.disabled) continue; const n = load(users.dirOf(u.id)).subs.length; devices += n; if (n) people++; }
return { ...stats, devices, people };
}
async function send(dir, st, uid, payload) {
if (uid) inbox.add(uid, { kind: 'remind', title: payload.title, body: payload.body, url: payload.url, tag: payload.tag, ref: payload.ref }); // (reminders also land in the Notifications list)
keys(); const { ref, ...wire } = payload;
let changed = false;
await Promise.all(st.subs.map(async s => {
try { await webpush.sendNotification(s.sub, JSON.stringify(wire), { TTL: 3600 }); }
catch (e) { if (e.statusCode === 404 || e.statusCode === 410) { s.dead = true; changed = true; } else console.warn('[push] send failed:', e.statusCode || e.message); }
try { await webpush.sendNotification(s.sub, JSON.stringify(wire), { TTL: 3600 }); stats.sent++; stats.lastOk = Date.now(); }
catch (e) {
if (e.statusCode === 404 || e.statusCode === 410) { s.dead = true; changed = true; stats.expired++; }
else { stats.failed++; stats.lastErr = String(e.statusCode || e.message).slice(0, 120); stats.lastErrAt = Date.now(); console.warn('[push] send failed:', e.statusCode || e.message); }
}
}));
if (changed) { st.subs = st.subs.filter(s => !s.dead); save(dir, st); }
}
@@ -182,4 +192,4 @@ async function handle(req, res) {
}
/** A notification for one person's devices (a chat message, say), unless they turned that kind off. */
async function notify(uid, payload) { const dir = users.dirOf(uid), st = load(dir); if (!st.subs.length || st.chat === false) return; await send(dir, st, null, payload); }
module.exports = { handle, start, tick, localNow, notify };
module.exports = { handle, start, tick, localNow, notify, health };