feat(backup): Konzept (BACKUP.md) + backups-Datenmodell (0004)
Backup-Übersicht/Restore bewusst NICHT als Fake-UI gebaut — im Mock gäbe es keine echten Kundendaten. Stattdessen: - 0004_backups.sql: Register-Tabelle (Metadaten: instance/account, trigger, status, storage_key, size, sha256, Zeitstempel). Dump-Dateien liegen extern. - BACKUP.md: Konzept für Modell A (studio-bezogener Export statt Full-Dump), Erzeugung (pg_dump/cron/Storage/Rotation), Restore-Sicherheitsregeln, geplante API. Bau der Logik, sobald echtes Provisioning steht. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
# RAPPORT-HOST — Backup-Konzept
|
||||||
|
|
||||||
|
> Status: **Konzept + Datenmodell stehen** (Migration `0004_backups.sql`). Die
|
||||||
|
> Erzeugungs-/Restore-Logik wird gebaut, sobald echtes Provisioning gegen einen
|
||||||
|
> laufenden Rapport-Stack läuft — vorher gäbe es nur leere Listen und Buttons
|
||||||
|
> ohne Funktion.
|
||||||
|
|
||||||
|
## 1 · Warum nicht jetzt schon „fertig"
|
||||||
|
|
||||||
|
Im aktuellen **MOCK-Modus** existieren keine echten Kunden-Datenbanken — Instanzen
|
||||||
|
sind synthetische DB-Einträge. Ein Backup-Mechanismus braucht aber echte
|
||||||
|
Studio-Daten in einem laufenden Postgres. Darum: Konzept + `backups`-Tabelle
|
||||||
|
jetzt, ausführende Logik mit echtem Stack.
|
||||||
|
|
||||||
|
## 2 · Was ein Backup im Modell A bedeutet
|
||||||
|
|
||||||
|
Wir hosten **Modell A**: ein geteilter Rapport-Stack, jeder Kunde ist ein
|
||||||
|
*Studio* (mandantengetrennt per RLS). Ein „Backup von Kunde X" ist deshalb
|
||||||
|
**kein** Full-DB-Dump, sondern ein **studio-bezogener Export**:
|
||||||
|
|
||||||
|
- alle Zeilen mit `studio_id = <Kunde>` aus allen relevanten Tabellen
|
||||||
|
- plus die zugehörigen Storage-Objekte (Quittungen/Logos) des Studios
|
||||||
|
|
||||||
|
Das hält Backups klein und verhindert, dass Kundendaten vermischt werden.
|
||||||
|
|
||||||
|
## 3 · Erzeugung (geplant)
|
||||||
|
|
||||||
|
| Aspekt | Entscheidung |
|
||||||
|
|---|---|
|
||||||
|
| Werkzeug | `pg_dump` mit studio-gefiltertem Export bzw. eine `export_studio(studio_id)`-RPC im Rapport-Schema |
|
||||||
|
| Zeitplan | täglich automatisch (cron im HOST-Backend) + manuell per Admin-Button |
|
||||||
|
| Speicherort | konfigurierbar via Env: lokale Disk (Default) oder S3/Backblaze |
|
||||||
|
| Verschlüsselung | at-rest (Storage-seitig) — bei externem Ziel Pflicht |
|
||||||
|
| Aufbewahrung | z.B. 7 tägliche + 4 wöchentliche (rotierend); konfigurierbar |
|
||||||
|
| Register | jede Datei wird in `backups` protokolliert (Status, Größe, sha256) |
|
||||||
|
|
||||||
|
## 4 · Restore (geplant, heikel)
|
||||||
|
|
||||||
|
Restore in einem Multi-Tenant-Stack ist die gefährlichste Operation. Regeln:
|
||||||
|
|
||||||
|
1. **Nur das eine Studio** wiederherstellen — niemals die ganze DB.
|
||||||
|
2. Vor Restore: **Sicherungs-Dump des aktuellen Studio-Stands** (Rollback-Option).
|
||||||
|
3. Restore in eine **Staging-Tabelle/Transaktion**, dann atomar umschalten.
|
||||||
|
4. Admin-Bestätigung mit Tippen des Kundennamens (kein Ein-Klick-Restore).
|
||||||
|
5. Audit-Log-Eintrag (wer/wann/welches Backup).
|
||||||
|
|
||||||
|
## 5 · Datenmodell (`backups`-Tabelle, schon angelegt)
|
||||||
|
|
||||||
|
`id · instance_id · account_id · trigger(scheduled|manual) · status(pending|ok|failed)
|
||||||
|
· storage_key · size_bytes · sha256 · error · created_at · completed_at`
|
||||||
|
|
||||||
|
Die Dump-**Dateien** liegen NICHT in der DB, nur die Metadaten. Die Admin-UI
|
||||||
|
liest später nur diese Tabelle (Liste pro Kunde) + löst manuelle Backups/Restores
|
||||||
|
über Endpunkte aus.
|
||||||
|
|
||||||
|
## 6 · Geplante API (noch nicht gebaut)
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/admin/accounts/:id/backups Liste der Backups eines Kunden
|
||||||
|
POST /api/admin/instances/:id/backup manuelles Backup auslösen
|
||||||
|
POST /api/admin/backups/:id/restore Restore (mit Bestätigung)
|
||||||
|
GET /api/admin/backups/:id/download Dump herunterladen (signiert)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7 · Nächster Schritt
|
||||||
|
|
||||||
|
Erst **echtes Provisioning** (laufender Stack, echte Studio-Daten). Dann:
|
||||||
|
`export_studio`/`import_studio`-RPC im Rapport-Schema → HOST-Cron + Endpunkte →
|
||||||
|
Admin-UI (Backup-Liste im Kunden-Detail + manuelles Backup/Restore).
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
-- RAPPORT-HOST — Backup-Register.
|
||||||
|
-- Speichert NUR Metadaten zu Backups (eine Zeile pro erstelltem Dump). Die
|
||||||
|
-- eigentlichen Dump-Dateien liegen NICHT in dieser DB, sondern am Storage-Ort
|
||||||
|
-- (lokale Disk / S3 / Backblaze) — siehe BACKUP.md.
|
||||||
|
--
|
||||||
|
-- Der Erzeugungs- und Restore-Mechanismus selbst (pg_dump pro Studio gegen den
|
||||||
|
-- Rapport-Stack) wird erst gebaut, wenn echtes Provisioning steht. Diese
|
||||||
|
-- Tabelle ist die Grundlage, damit die Admin-UI später nur noch lesen/anzeigen
|
||||||
|
-- muss.
|
||||||
|
|
||||||
|
create table if not exists backups (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
instance_id uuid references instances(id) on delete set null,
|
||||||
|
account_id uuid references accounts(id) on delete set null,
|
||||||
|
-- Wofür: automatischer Zeitplan oder manuell vom Admin ausgelöst.
|
||||||
|
trigger text not null default 'scheduled', -- scheduled | manual
|
||||||
|
status text not null default 'pending', -- pending | ok | failed
|
||||||
|
-- Wo die Datei liegt (Pfad/Key am Storage-Ort) + Größe + Prüfsumme.
|
||||||
|
storage_key text,
|
||||||
|
size_bytes bigint,
|
||||||
|
sha256 text,
|
||||||
|
error text, -- Fehlertext, falls failed
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
completed_at timestamptz
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists idx_backups_instance on backups(instance_id);
|
||||||
|
create index if not exists idx_backups_account on backups(account_id);
|
||||||
|
create index if not exists idx_backups_created on backups(created_at desc);
|
||||||
Reference in New Issue
Block a user