feat(sv): Backups card — offsite DR status (Core-4 -> Farm) + /api/backups (2.6.0)

migration 026 backup_runs; POST ingest (owner) from offsite-backup.sh, GET for the
Sacred Valley card showing last run, per-guest sizes, Farm free, schedule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-09 17:47:17 +10:00
parent 16e324102e
commit b967c0bfdd
10 changed files with 139 additions and 5 deletions

View File

@@ -0,0 +1,12 @@
-- 026_backup_runs.sql
-- Offsite DR backup run history, fed by /usr/local/bin/offsite-backup.sh on CT 300
-- (Core-4 vzdump -> Farm/Won). Powers the Sacred Valley "Backups" card.
CREATE TABLE IF NOT EXISTS backup_runs (
id serial PRIMARY KEY,
ran_at timestamptz NOT NULL DEFAULT now(),
ok boolean NOT NULL DEFAULT true,
total_bytes bigint,
won_free_bytes bigint,
guests jsonb, -- [{vmid,name,bytes}]
duration_sec integer
);

21
lib/db/repos/backups.js Normal file
View File

@@ -0,0 +1,21 @@
import { pool } from '../pool.js';
export async function record({ ok = true, total_bytes = null, won_free_bytes = null,
guests = null, duration_sec = null }) {
const { rows: [r] } = await pool.query(
`INSERT INTO backup_runs (ok, total_bytes, won_free_bytes, guests, duration_sec)
VALUES ($1,$2,$3,$4,$5) RETURNING *`,
[ok, total_bytes, won_free_bytes, guests ? JSON.stringify(guests) : null, duration_sec]);
return r;
}
export async function latest() {
const { rows: [r] } = await pool.query(
`SELECT * FROM backup_runs ORDER BY id DESC LIMIT 1`);
return r || null;
}
export async function count() {
const { rows: [r] } = await pool.query(`SELECT count(*)::int AS n FROM backup_runs`);
return r.n;
}