Build and Push Docker Image / build-and-push (push) Successful in 10m22s
- Reader: chapter cross-reference map (M) from IQ Bible's reference graph - Admin: Health tab with disk use, snapshots (back up, download, restore), failure logs - Passkey sign-in (WebAuthn), signed-in device list, sign out others, optional sign-in alerts - Settings split into Account, Preferences, Notifications, Security, Your data tabs - ntfy server field: catch a topic typed into the server box Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
38 lines
1.7 KiB
JavaScript
38 lines
1.7 KiB
JavaScript
// What the admin's Health page shows: a short in-memory log of things that went wrong (failed sign-ins, failed
|
|
// notifications, backups), plus disk use and the snapshot list. The log resets when the server restarts.
|
|
import { existsSync, statSync, statfsSync } from 'fs';
|
|
import { join } from 'path';
|
|
import { dataDir } from './db.js';
|
|
import { backupDir, listSnapshots } from './backups.js';
|
|
|
|
const MAX_EVENTS = 200;
|
|
const events = [];
|
|
const started = Date.now();
|
|
|
|
// kind: 'signin' | 'notify' | 'backup'. `ok` false marks a failure.
|
|
export function logEvent(kind, detail, { ok = false, userId = null } = {}) {
|
|
events.unshift({ at: Date.now(), kind, ok, detail: String(detail).slice(0, 300), userId });
|
|
if (events.length > MAX_EVENTS) events.length = MAX_EVENTS;
|
|
}
|
|
|
|
export const recentEvents = (kind, limit = 50) => events.filter((e) => !kind || e.kind === kind).slice(0, limit);
|
|
export function clearEvents() { events.length = 0; }
|
|
|
|
const size = (path) => { try { return statSync(path).size; } catch { return 0; } };
|
|
|
|
export function diskUse() {
|
|
const dir = dataDir();
|
|
const db = size(join(dir, 'projects.db')) + size(join(dir, 'projects.db-wal'));
|
|
const backups = listSnapshots(backupDir()).reduce((n, s) => n + s.bytes, 0);
|
|
let free = null; let total = null;
|
|
try { const s = statfsSync(existsSync(dir) ? dir : '.'); free = s.bavail * s.bsize; total = s.blocks * s.bsize; } catch { /* not supported here */ }
|
|
return { db, backups, free, total };
|
|
}
|
|
|
|
export function backupStatus() {
|
|
const snapshots = listSnapshots(backupDir());
|
|
return { enabled: process.env.BACKUP_DISABLED !== '1', keep: Number(process.env.BACKUP_KEEP) || 7, snapshots, latest: snapshots[0] ?? null };
|
|
}
|
|
|
|
export const uptimeSeconds = () => Math.round((Date.now() - started) / 1000);
|