// Admin → Health: is the server in good shape? Disk use, snapshots (make, download, restore) and recent failures.
import { useCallback, useEffect, useState } from 'react';
import { adminBackupNow, adminGetHealth, adminRestoreSnapshot, adminSnapshotUrl } from '../syncService.js';
const btn = 'rounded-lg border border-[var(--r-line)] px-3 py-1.5 text-xs hover:bg-black/5 disabled:opacity-50';
const card = 'rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-4';
export const formatBytes = (n) => {
if (n == null) return 'unknown';
if (n < 1024) return `${n} B`;
const units = ['KB', 'MB', 'GB', 'TB'];
let v = n / 1024; let i = 0;
while (v >= 1024 && i < units.length - 1) { v /= 1024; i += 1; }
return `${v < 10 ? v.toFixed(1) : Math.round(v)} ${units[i]}`;
};
export const formatUptime = (s) => {
if (s < 90) return `${s} seconds`;
if (s < 5400) return `${Math.round(s / 60)} minutes`;
if (s < 129600) return `${Math.round(s / 3600)} hours`;
return `${Math.round(s / 86400)} days`;
};
const ago = (at) => {
const m = Math.round((Date.now() - at) / 60000);
if (m < 2) return 'just now';
if (m < 120) return `${m} minutes ago`;
if (m < 2880) return `${Math.round(m / 60)} hours ago`;
return `${Math.round(m / 1440)} days ago`;
};
function Log({ title, empty, rows }) {
return (
{empty}{title}
{rows.length === 0 ?
{rows.map((e, i) => (
)}
Loading…
; if (state.error) return{state.error}
; const { data } = state; const { backups, disk } = data; const stale = backups.enabled && (!backups.latest || Date.now() - backups.latest.at > 36 * 3600 * 1000); const usedPct = disk.total ? Math.round(((disk.total - disk.free) / disk.total) * 100) : null; return (Node {data.node} · IQ Bible {data.iqConfigured ? 'connected' : 'not set up'} · data in {data.dataDir}
Automatic snapshots are turned off (BACKUP_DISABLED).
} {stale &&{backups.latest ? `The last snapshot is ${ago(backups.latest.at)}.` : 'There are no snapshots yet.'} One should be made every day.
} {data.backupFailures.length > 0 &&Last failure: {data.backupFailures[0].detail} ({ago(data.backupFailures[0].at)})
} {backups.snapshots.length > 0 && (Keeps the newest {backups.keep}. A snapshot holds every account’s data.
{restoring && (This replaces everyone’s data with what was saved in {restoring}. Anything changed since then is lost, though what’s there now is saved as a new snapshot first. Type the snapshot’s name to go ahead.
setTyped(e.target.value)} aria-label="Snapshot name" placeholder={restoring} className="w-full rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-3 py-2 text-xs" />{message}
}The failure lists are kept in memory, so they start over when the server restarts.