Zum Inhalt springen

Änderungen und Sicherung

Wer hat was geändert — und wie kommst du zurück? Drei Ebenen: Das Audit-Log protokolliert Änderungen, Snapshots frieren Datenstände ein, Backups sichern alles für den Ernstfall. Suchbegriffe: Änderungshistorie, Verlauf, Wiederherstellen, Snapshot, Datensicherung.

Audit-Log

Protokolliert Änderungen — wer, wann, vorher/nachher.

Snapshots

Frieren Datenstände im Ordner snapshotPath ein.

Backups

Voll-Export als ZIP für Umzug und Ernstfall.

Audit ist zweistufig: Es läuft nur, wenn Server und Projekt es erlauben.

api/config.yml — Projekt-Opt-in (Starter-Standard)
audit:
enabled: true
Ebene Schalter Standard
Server audit.enabled, TTL audit.defaultTTL: 720h (30 Tage, "0" = ewig) Betreiber-Sache
Projekt audit.enabled: true Starter: an
Sammlung audit: { enabled, actions: [create, update, delete] } Standard-Aktionen: anlegen, ändern, löschen
Action audit: { enabled, actions: [get, post] } Nur was du einschaltest
  1. Etwas ändern (z. B. Seitentitel per PUT).
  2. Im tibi-admin die Änderungshistorie des Eintrags öffnen — wer, wann, vorher/nachher.
  3. Alte Fassung per Wiederherstellen zurückschreiben (erst an Testdaten üben).
  4. Per API lesen geht mit GET /api/v1/audit (Recht audit.read nötig).

Du musst nicht per API lesen: Der Admin zeigt den Verlauf pro Eintrag (letzte 50 Änderungen mit Vorher/Nachher-Vergleich und Wiederherstellen einzelner Felder) sowie global als Audit-Log. Details zur Bedienung — Verlaufspanel, Diff-Ansicht, Wiederherstellen-Dialog, Seitenleiste — stehen unter Eintrags-Editor.

Snapshots sind eingefrorene Datenstände im Ordner snapshotPath (../snapshots im Starter):

api/config.yml — Snapshot-Profile des Starters
snapshotPath: ../snapshots
snapshots:
- name: daily # 12:00 Uhr, redaktionelle Daten + Medien, 14 Stück
schedule: "0 12 * * *"
collections: [content, navigation, tags, medialib, ssr]
includeMedia: true
retention: { maxCount: 14 }
- name: full-daily
schedule: "0 3 * * *" # 03:00 Uhr, alles + Medien, 14 Stück
includeMedia: true
retention: { maxCount: 14 }
- name: full-weekly # sonntags 05:00 Uhr, 12 Stück
schedule: "0 5 * * 0"
collections: [] # leer = alle Sammlungen
includeMedia: true
retention: { maxCount: 12 }
- name: full-monthly # monatlich, Langzeitarchiv, 12 Stück
schedule: "0 6 1 * *"
collections: []
includeMedia: true
retention: { maxCount: 12 }
Regel Bedeutung
collections: [] (leer) Alle Sammlungen sichern
Liste mit Namen Nur diese sichern (Medien nur mit includeMedia: true, per Hardlink)
retention.maxCount Älteste fliegen automatisch raus
Speicherung json (BSON-Extended-JSON auf Platte, Standard); db/both sind Zukunft

Alle Felder aus snapshot_config.go:11-58 — nur diese Namen verwenden:

Feld YAML-Name Bedeutung
Name name Eindeutige Profil-Kennung (Pflicht, sonst snapshot config: name is required)
Beschreibung description Freitext, optional
Sammlungen collections [] = alle; String-Kurzform "content" = include; Objektform {name, mode} (include oder exclude) — gemischt include + exclude ist ein Validierungsfehler
Medien includeMedia Datei-Felder per Hardlink sichern (Standard: true)
Deduplizierung collectionDedup SHA256-Dedup identischer Collection-Exporte über .store/collections/ per Hardlink — nur für große, selten geänderte Sammlungen
Modus mode ""/json = Platte (Standard, implementiert); db/both = MongoDB-$out-Kopie (Zukunft) — Medien immer Hardlink, egal welcher Modus
Aufbewahrung retention: { maxCount, maxAge, maxSizeBytes } Auto-Prune-Regeln, alle optional
Zeitplan schedule Cron-Ausdruck; leer = nur manuell per Trigger-API
Team-Rechte permissions Liste reiner Berechtigungsnamen — Phase 2, aktuell gilt AllowSnapshots
An/Aus enabled false = Scheduler überspringt das Profil; manueller Trigger geht trotzdem
Snapshot-Profil — alle Felder (Beispiel)
snapshots:
- name: redaktionell
description: "Täglich, ohne Systemdaten"
collections:
- content # Kurzform = include
- name: ssr # Objektform
mode: exclude
includeMedia: true
collectionDedup: false
mode: json
retention: { maxCount: 14, maxAge: "336h", maxSizeBytes: 10737418240 }
schedule: "0 12 * * *"
permissions: []
enabled: true

Alle Routen aus setup.go:436-495 (GET liest, POST löst aus, DELETE löscht):

Methode Pfad (hinter /api/v1/_/:project/_/admin) Zweck
GET /snapshots/configs Alle Profile aus der Projekt-YAML lesen (AdminSnapshotConfigs)
POST /snapshots/configs/:name/trigger Profil manuell auslösen — geht auch bei enabled: false
GET /snapshots Alle Einträge auflisten (Scannt das entries-Verzeichnis)
GET /snapshots/download/:file Datei-Download (vor :id registriert, damit download kein :id matcht)
GET /snapshots/:id Einzelnen Eintrag ansehen
DELETE /snapshots/:id Eintrag löschen
POST /snapshots/:id/restore?merge=1&collections=a,b Zurückspielen (merge=1 = zusammenführen; collections = Auswahl)
GET /snapshots/:id/download Eintrag als ZIP laden
GET /snapshots/stats Belegung prüfen
POST /snapshots/prune Retention-Aufräumen manuell anstoßen

Im tibi-admin (/projects/:id/snapshots, drei Tabs):

Tab Was du tust
Configs Profile ansehen
Entries Auslösen (mit Fortschritt), herunterladen (ZIP), zurückspielen, löschen, aufräumen
Stats Belegung prüfen

Zum Zurückspielen braucht dein Token allowSnapshots: true. Übe das Zurückspielen nur mit Testdaten — es überschreibt den Live-Stand.

Alle Routen aus setup.go:398-432:

Methode Pfad (hinter /api/v1/_/:project/_/admin) Zweck
GET /backup/analyze-download?collections=a,b Schätzung (Dokument-/Medien-Zahlen) ohne Voll-Export — vor dem Download prüfen
GET /backup Voll-ZIP erzeugen und laden (?collections= schränkt ein, ?sse=1 streamt)
GET /backup/download/:file Fertiges Temp-ZIP aus dem SSE-Modus laden (One-Time-Use: zweiter Request = 404)
POST /backup?sse=1 ZIP hochladen und analysieren → antwortet mit restoreToken
POST /backup/:restoreToken?collections=…&merge=true&sse=1 Restore aus dem analysierten Temp-Verzeichnis ausführen
DELETE /backup/:restoreToken Temp-Daten zum Token manuell aufräumen

POST /backup erzeugt Token plus Temp-Verzeichnis (admin_backup.go:204-234), POST /backup/:restoreToken verbraucht es, DELETE räumt auf.

  1. Analyse: ZIP per POST /backup hochladen — Antwort enthält restoreToken, sourceNamespace, createdAt, expiresAt (admin_backup.go:378-384). Mit ?sse=1 kommt der Fortschritt als Stream (init → done, jeweils mit restoreToken).
  2. Prüfen: collections-Analyse in der Antwort lesen (Quelle gegen Ziel abgleichen, ggf. ?collections= für Teil-Restore vormerken).
  3. Restore: POST /backup/:restoreToken ausführen — bei Erfolg landet restoredCollections/totalDocuments/mediaFiles in der Antwort, alles wird als Audit-Action backup/restore protokolliert. Danach ist das Token verbraucht (Eintrag wird vor dem Restore aus dem Store entfernt).
  4. Cleanup: Läuft der Restore ohne sse, löscht der Server das Temp-Verzeichnis selbst (os.RemoveAll). Bei Abbruch oder abgelaufenem Token per DELETE /backup/:restoreToken aufräumen — Antwort 404 restore token not found or already cleaned up heißt: schon weg. Abgelaufene Tokens räumt zusätzlich ein Hintergrund-Job weg.
Ebene Schalter Beleg
Server-Token Scope project.backup / project.snapshot (scope.go:53-55) X-Admin-Token oder Authorization: Bearer
Projekt-Token allowBackup / allowSnapshots in der Projekt-Config (project.go:118-120) Nur mit passendem Flag
JWT-Login Rolle AdminRole plus Scope-Check (EnforceScope) Persönliche Tokens bleiben durch ihre Scopes begrenzt

Der Check läuft in admin_auth.go:20-74 immer in dieser Reihenfolge:

  1. Server-Token mit passendem Scope (config.GetAdminTokenInfo).
  2. Projekt-Token mit passendem Flag (AllowBackup / AllowSnapshots per ConstantTimeCompare).
  3. JWT-Login mit Admin-Rolle plus Scope-Prüfung — sonst 403 permission denied.

Backups sind der Voll-Export für Umzug und Desaster-Fall — im Admin unter /projects/:id/backup:

  1. Export-Tab: Sammlungen wählen (Kopf-Kästchen = alle), „Start backup“ klicken, Fortschritt abwarten, Datei mit Größenangabe laden.
  2. Bricht der Download ab: erst Analyse (backup/analyze-download), dann neu laden.
  3. Import-Tab: ZIP in die Zone ziehen, Analyse lesen, Restore bestätigen.
  4. Danach: Reload und SSR-Cache prüfen, Seite anonym aufrufen.
Unterschied Snapshot Backup
Wofür Schneller Rücksprung im Alltag Umzug, Desaster, Weitergabe
Wo snapshotPath auf dem Server ZIP-Datei bei dir
Recht allowSnapshots Projekt-Admin plus AllowBackup
Fortschritt Ja (Stream) Ja (SSE-Stream)
Fehlerbild Ursache Lösung
Audit bleibt leer audit.enabled im Projekt fehlt oder Server-Flag aus Beide Schalter prüfen, Änderung erneut auslösen
Alte Einträge verschwinden TTL (720h) abgelaufen Wichtige Stände als Snapshot sichern, TTL ggf. auf "0"
Snapshot-Button fehlt Token ohne allowSnapshots Flag allowSnapshots am Projekt-Token setzen (siehe Rechte und Auth-Reihenfolge)
Backup-Button fehlt Kein Projekt-Admin oder AllowBackup fehlt Projekt-Admin-Rolle bzw. Flag AllowBackup am Projekt-Token prüfen (siehe Rechte und Auth-Reihenfolge)
400 missing restoreToken Restore ohne Token aufgerufen Erst POST /backup (Analyse), Token aus der Antwort nehmen
404 restore token not found or expired Token verbraucht, aufgeräumt oder TTL abgelaufen Analyse erneut hochladen, neues Token holen
403 invalid entry id / 404 snapshot not found Snapshot-Restore mit falscher/gelöschter ID ID aus GET /snapshots neu lesen
snapshot config: name is required Profil ohne name name in der YAML ergänzen
Restore zeigt keine Wirkung Cache alt Reload plus ?clear=1 (siehe Veröffentlichen)

Wie ein Redakteur eine Sicherung einspielt: Backup einspielen.

Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter, config.yml audit- und snapshots-Blöcke), am 68b4278 2026-10-17 (tibi-server, audit-, backup- und snapshot-docs), am 2b091d8 2026-10-17 (tibi-admin-nova, test-pläne 43 und 44, AuditView, AuditHistoryPanel, AuditDiff, AuditRestoreDialog, AuditSidebarHistory)