Files
edgeguard-native/internal/database/migrations/0018_backups.sql
Debian 571f51ba9a feat(backup): pg_dump + state-tarball + daily auto + UI
Production-Box braucht Backups — bisher keine. Jetzt komplette
Pipeline:

Backend (internal/services/backup/):
  - Output: /var/backups/edgeguard/eg-YYYYMMDD-HHMMSS.tar.gz
  - Inhalt: dump.sql (pg_dump --clean --if-exists --no-owner --no-acl),
    files/setup.json, files/license_key, files/license.cache,
    files/.jwt_fingerprint, files/node.conf, files/acme-account/* +
    manifest.json (Version, kind, hostname, sizes)
  - sha256 während-write via TeeWriter, Size + sha in backups-DB-Row
  - Failure-Path: row mit status=failed + error, kein orphan-tarball
  - Prune(keepN=14) löscht erfolgreiche Backups älter als die letzten N

Migration 0018: backups(id, file, size, sha256, db/files bytes, kind,
status, error, host, started/finished).

Scheduler (cmd/edgeguard-scheduler):
  - 24h-Tick → backup.Run(KindScheduled) + Prune. Beim Boot wird ein
    initialer Backup NICHT sofort gezogen (kein nervöses Spam),
    sondern erst beim nächsten 24h-Slot.

REST (internal/handlers/backup.go):
  GET    /api/v1/backups              — list (newest first)
  POST   /api/v1/backups              — trigger manual (sync, audit'ed)
  GET    /api/v1/backups/:id          — single
  GET    /api/v1/backups/:id/download — sendfile tar.gz
  DELETE /api/v1/backups/:id          — entferne file + row

UI (management-ui/src/pages/Backups):
  - Liste mit Time, File+sha (first 16), Kind-Tag, Status, Size (mit
    DB + Files Aufschlüsselung), Dauer
  - „Backup jetzt erstellen" Button, Refresh, Download, Delete
  - Auto-Refresh 30s
  - Sidebar-Eintrag „Backups" unter System

postinst:
  - /var/backups/edgeguard 0750 edgeguard:edgeguard (enthält sensitive
    pg_dump + license_key → NICHT world-readable)
  - sudoers-Whitelist `sudo -u postgres /usr/bin/pg_dump --clean
    --if-exists --no-owner --no-acl edgeguard` — exakte Form

Verifiziert auf der Box: backups-Tabelle existiert, scheduler logged
„backup enabled tick=24h dir=/var/backups/edgeguard keep_n=14",
pg_dump-via-sudoers liefert 2808 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:08:18 +02:00

38 lines
1.6 KiB
SQL

-- +goose Up
-- +goose StatementBegin
-- Backup-History: jede Backup-Operation (manual oder scheduled) wird
-- nach Abschluss hier eingetragen — Operator sieht die Liste in der
-- /backup-UI mit Größe, Dauer, Status. Files liegen auf der lokalen
-- Box unter /var/backups/edgeguard/<file>; nicht zwischen Cluster-
-- Nodes synchronisiert (Backup ist node-local — jeder Node sichert
-- seinen eigenen State).
CREATE TABLE IF NOT EXISTS backups (
id BIGSERIAL PRIMARY KEY,
file TEXT NOT NULL, -- Basename, z.B. eg-20260512-153045.tar.gz
size_bytes BIGINT NOT NULL,
sha256 TEXT NOT NULL,
db_dump_bytes BIGINT NOT NULL DEFAULT 0,
files_bytes BIGINT NOT NULL DEFAULT 0,
kind TEXT NOT NULL, -- manual | scheduled
status TEXT NOT NULL, -- success | failed
error TEXT, -- gesetzt bei failed
host TEXT, -- hostname der Box (für Cluster-Sicht später)
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
finished_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT backups_file_unique UNIQUE (file),
CONSTRAINT backups_kind_check CHECK (kind IN ('manual', 'scheduled')),
CONSTRAINT backups_status_check CHECK (status IN ('success', 'failed'))
);
CREATE INDEX IF NOT EXISTS idx_backups_started ON backups (started_at DESC);
CREATE INDEX IF NOT EXISTS idx_backups_status ON backups (status);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS backups;
-- +goose StatementEnd