syncova-backup/docs/web-ui.md
Jerrit Fritzsche 053e9ae817
Some checks failed
CI / Backend (Go) (push) Failing after 30s
CI / Frontend (React/TypeScript) (push) Successful in 43s
CI / Sicherheitsprüfungen (push) Successful in 26s
Dokumentation und Aenderungsliste fuer rc7
docs/web-ui.md beschreibt jetzt das Preset samt der drei begruendeten
Abweichungen, die Sitzung mit ihren zwei Uhren und die Fehlergrenze. Dazu zwei
neue Grenzen: Geist Mono wird nicht mitgeliefert, und die Suche im
Ereignisprotokoll filtert weiterhin im Browser.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:10:17 +02:00

235 lines
11 KiB
Markdown

# Weboberfläche
Die Konsole, über die man die Anlage bedient — und der Ort, an dem sich am
leichtesten etwas vortäuschen ließe.
Sie erreicht **89 von 95 fachlichen Endpunkten**. Die sechs offenen sind
Detail-Abrufe (`GET /alerts/{id}`, `GET /agents/{id}`, `GET /proxmox/vms/{id}`,
`GET /security/findings`, `GET /verification/{id}` und dessen Ergebnisse), deren
Daten die jeweiligen Listen bereits enthalten.
## Die eine Entscheidung, die alles trägt
**Semantische Farben ausschließlich für Zustände** (PROMPT §106). Die Konsole ist
neutral gehalten; wo Farbe erscheint, bedeutet sie etwas. Eine bunte Oberfläche
verschleiert, welche Information wirklich dringend ist — und in einer
Backup-Konsole ist genau das die einzige Frage, die zählt.
Daraus folgt der Zuschnitt: kein Markenblau als Fläche, der Akzent nur für
Bedienelemente. Der Schweregrad einer Kennzahl färbt die **Randlinie**, nicht die
Kachel; zehn farbige Flächen nebeneinander ergeben ein Mosaik, in dem die eine
kritische Zahl untergeht.
### Die Statuszuordnung steht an genau einer Stelle
`components/ui/StatusBadge.tsx` bildet jeden Fachbegriff der API auf einen der
fünf Töne ab. Wäre diese Abbildung über die Seiten verteilt, erschiene früher
oder später irgendwo `partial_failure` grün — und ein Betreiber hält einen
Teilfehler dann für einen Erfolg.
Vier Zuordnungen sind keine Geschmacksfrage:
| Wert | Ton | Warum |
| --- | --- | --- |
| `partial_failure` | Warnung | Entwicklungsregel 1: nie `SUCCESS` |
| `successful` (Einstufung) | **neutral** | Heißt „der Lauf ist durchgelaufen", nicht „wiederherstellbar" |
| `corrupted` | kritisch | Nicht zu 70 % wiederherstellbar, sondern gar nicht |
| `advisory` (Löschschutz) | Warnung | Der Schutz ist eine Software-Regel, kein Schutz des Dateisystems |
**Ein unbekannter Wert wird neutral dargestellt, niemals grün.** Ein neuer
Serverzustand, den die Tabelle nicht kennt, darf nicht als „in Ordnung"
durchgehen. `StatusBadge.test.tsx` hält alle fünf Punkte fest; jeder wurde durch
Mutation als fangend bestätigt.
## Was nicht da ist, wird benannt
Der Grundsatz aus PROMPT §139 gilt unverändert: Ein Menü, das nur die fertigen
Bereiche zeigt, verschweigt den Ausbaustand; eines mit leeren Masken täuscht ihn
vor. Jeder Eintrag erscheint, und ein noch nicht verfügbarer nennt, was fehlt.
Konkret sichtbar an vier Stellen:
- **Kennzahlen ohne Datengrundlage** erscheinen mit Begründung statt mit einer
Null. „0 kritische Meldungen" hieße „keine Probleme" und bedeutete „es wird
nicht geprüft".
- **Ungemessene Eingangsgrößen der Bewertung** stehen als „ungemessen" da, nicht
als null Punkte. Als 0 zu zeigen bestrafte das Unbekannte.
- **Nicht ausgewertete Meldungsregeln** werden als solche gekennzeichnet. Eine
Regel, die dauerhaft schweigt, ist gefährlicher als keine.
- **Ungeprüfte Bereiche im Security Center** gehen weder positiv noch negativ in
die Rechnung ein; ab drei sagt die Seite, dass die Zahl eine Vermutung ist.
## Die achtzehn Seiten
| Bereich | Seiten |
| --- | --- |
| **Betrieb** | Übersicht · Sicherungsaufträge · Wiederherstellung · Meldungen |
| **Daten** | Wiederherstellungspunkte · Prüfung · Repositories · Aufbewahrung |
| **Infrastruktur** | Agenten · Proxmox · Geschützte Systeme |
| **Analyse** | Kennzahlen · Berichte · Security Center |
| **Verwaltung** | Benutzer · Rollen · Ereignisprotokoll · Einstellungen |
Die Gliederung folgt dem Weg durch die Anlage: Was täglich beobachtet wird, steht
oben; was einmal eingerichtet und dann selten angefasst wird, unten.
## Handlungen, die etwas verändern
### Die drei Hürden vor dem Überschreiben
Der Wiederherstellungs-Assistent setzt sie sichtbar um:
1. Das Kennzeichen `overwrite_existing` muss gesetzt werden.
2. Die Berechtigung `restores.overwrite` prüft der Server — sie steckt bewusst
**nicht** in `restores.execute`.
3. `confirm_overwrite` verlangt den **wörtlich wiederholten Zielpfad**.
Läuft die dritte ins Leere, weil das Ziel leer ist, entfällt sie. Ein Ritual ohne
Anlass gewöhnt das Wegklicken an — und dann wirkt es dort nicht mehr, wo es
zählt. Dieselbe Überlegung trägt die wörtliche Bestätigung beim Löschen eines
Wiederherstellungspunkts, eines Auftrags und beim Anwenden einer
Aufbewahrungsregel.
### Die Vorabprüfung ist ein eigener Schritt
Sie schreibt nichts und stellt fest, ob **jeder benötigte Block noch da ist**.
Ein Manifest allein belegt nur, dass jemand einmal etwas gesichert hat. Fehlende
Blöcke stehen ganz oben und in Rot; sie sind der einzige Befund, bei dem
feststeht, dass die Wiederherstellung nicht vollständig gelingen kann.
### Ein 409 ist eine Auskunft, kein Fehler
Ein zweiter Anstoß bei laufendem Auftrag erscheint als Hinweis, nicht als
Fehlschlag. Der Auftrag läuft ja — und genau das wollte der Betreiber wissen.
### Was Löschen wirklich bewirkt, steht dabei
- Ein gelöschter **Auftrag** nimmt seine Wiederherstellungspunkte nicht mit. Sie
gehören zum Repository. Ohne diesen Hinweis löscht jemand einen Auftrag in der
Annahme, Platz zu schaffen.
- Wird beim Löschen **kein Speicher frei**, ist das kein Fehler, sondern
Deduplizierung: Die Blöcke werden von einem anderen Backup gebraucht.
### Geheimnisse gehen nur hinein
Das Aufnahme-Token eines Agenten erscheint **genau einmal**, mit
ausdrücklichem Hinweis und ohne Weg, den Dialog versehentlich zu schließen. Das
API-Token eines Proxmox-Verbunds wird nach dem Anlegen nie wieder ausgeliefert —
das ist kein Mangel, sondern der Grund, warum ein Lesezugriff auf die
Konfiguration ungefährlich ist.
## Aussehen
Farben, Radien, Schatten und Schrift stammen aus dem Preset `b5vnF8SMi`
(tweakcn): violett als Handlungsfarbe, **Radius 0**, schattenlos, durchgehend
Geist Mono. Kantig und ruhig.
Drei Abweichungen, begründet in `styles/theme.css`:
- **Die Statusfarben bleiben.** Das Preset kennt nur `destructive` und fünf
Diagrammfarben; §106 verlangt fünf Bedeutungen. Ohne sie ließe sich ein
Teilfehler nicht von einem Erfolg unterscheiden.
- **Das dunkle Thema hängt an `[data-theme='dark']`**, nicht an `.dark` — der
Umschalter setzt dieses Attribut. `.dark` funktioniert zusätzlich.
- **Geist Mono lädt nicht nach.** Sie steht zuerst im Stapel; liegt sie nicht
auf dem Gerät, greift die System-Monospace. Eine Schrift von einem fremden
Host zu holen verbietet die CSP — und ein Backup-Server, der für seine
Oberfläche ins Internet greift, wäre auch ohne CSP falsch.
Eine Zuordnung ist die Stolperstelle: In shadcn ist `accent` die dezente
Hover-Fläche und `primary` die Farbe der Handlung. Sie zu verwechseln macht jede
Schaltfläche grau.
Die Benennung bleibt semantisch (`--surface-card`, `--text-primary`) statt
shadcn-typisch: Die Zuordnung steht an genau einer Stelle, und ein Wechsel des
Presets fasst keine einzige Komponente an.
## Technik
- **Tailwind v4 und Radix-Primitive** nach shadcn-Muster. Alles gebündelt; die
CSP der Auslieferung lässt externe Ressourcen ohnehin nicht zu.
- **Farben als CSS-Variablen**, damit dieselbe Komponente in beiden Themen
funktioniert, ohne dass jede Klasse eine `dark:`-Variante braucht.
- **Fehlergrenze um den Seiteninhalt.** Ohne sie reißt ein Fehler in einer
Komponente den gesamten Baum ab; übrig bleibt eine leere Seite — im dunklen
Thema ein schwarzer Bildschirm ohne Hinweis. Menü und Kopfzeile bleiben
stehen, der Fehlertext ist lesbar.
- **Dark Mode über ein Attribut am Wurzelelement**, nicht allein über die
Medienabfrage: Eine Konsole, die nachts während einer Störung von selbst
umschaltet, ist lästig. Das Attribut sitzt am Wurzelelement, weil ein Dialog im
Portal sonst im falschen Thema erschiene.
- **Navigation über die History-API**, keine Router-Bibliothek. Zwei Ebenen —
Seite und optional ein Objekt darauf — reichen für diese Konsole.
- **`useMutation` für schreibende Aufrufe:** Doppelklickschutz, Vorgangsnummer
bis in die Meldung, kein `setState` nach dem Aushängen.
- **`Idempotency-Key`** an allen anlegenden und zerstörenden Aufrufen. Ohne ihn
erzeugt ein Doppelklick zwei Aufträge — und bei einer Wiederherstellung zwei
gleichzeitige Läufe in dasselbe Ziel.
- **Die Servermeldung hat Vorrang** vor der allgemeinen Erklärung zum
Fehlercode. Sie kennt den Einzelfall, und diese Genauigkeit ist mehr wert.
- **Berechtigungen im Menü sind Anzeige, keine Sicherung.** Sie verhindern
Sackgassen; geprüft wird auf dem Server.
### Sitzung
Sie überlebt ein Neuladen. Die Tokens liegen im `sessionStorage` des Tabs —
nicht im `localStorage`, der ein Schließen des Browsers überstünde. Begrenzt
wird sie durch zwei Uhren:
| | |
| --- | --- |
| **Harte Obergrenze** | 30 Minuten ab Anmeldung, durch keine Interaktion verschiebbar |
| **Untätigkeitsgrenze** | 30 Minuten ohne Eingabe |
Maßgeblich ist die frühere der beiden. Die verbleibende Zeit steht neben
„Abmelden" und wird unter fünf Minuten auffällig.
Dass die Tokens überhaupt abgelegt werden, kehrt eine frühere Entscheidung um:
Vorher lagen sie nur im Arbeitsspeicher, und jedes Neuladen warf den Betreiber
auf die Anmeldemaske. Mitten in einer Störung ist das kein Sicherheitsgewinn,
sondern ein Hindernis. Was den Rest trägt, ist nicht der Speicherort, sondern
der **sofortige serverseitige Widerruf**: Die Tokens sind opak, kein JWT, und
genau dafür wurden sie gewählt.
- **Jede Fehleranzeige nennt `request_id`**, kopierbar. Ohne sie bleibt „es hat
nicht funktioniert".
### Betriebsfolge
Das Bundle braucht einen SPA-Fallback (`try_files $uri /index.html`), sonst
ergibt ein Neuladen auf `/recovery-points` einen 404. `setup.sh` richtet das mit
ein.
## Nachgewiesen
Gegen Debian 12 mit echtem PostgreSQL und nginx, über genau die Aufrufe, die die
Konsole macht:
- Repository übernommen, Durchsetzungsstufe **gemessen** (`advisory` — auf
overlayfs richtig)
- Auftrag angelegt, Lauf `202`, zweiter Anstoß `409`
- Sicherung erfolgreich: 2 Objekte, 3.000.006 Byte, 0 übergangen
- Blockprüfung `clean`, 5 Blöcke
- Vorabprüfung: „wiederherstellbar, 2 Dateien, 2,9 MiB"
- Wiederherstellung nach `/etc` abgewiesen
- Alle 18 Seiten liefern über HTTPS `200`; das Design-System steckt samt
Dark-Mode-Regeln im ausgelieferten CSS
76 Tests, `tsc` sauber, `eslint` ohne Warnung. Bundle 466 KB (136 KB gzip), CSS
30 KB (6 KB gzip).
## Bekannte Grenzen
- **Sechs Detail-Endpunkte** haben keine eigene Ansicht; ihre Daten stehen in den
Listen.
- **Die Suche im Ereignisprotokoll filtert im Browser** über die letzten 100
Einträge. Bei größeren Beständen gehört sie auf den Server.
- **Geist Mono wird nicht mitgeliefert.** Ohne die Schrift auf dem Gerät sieht
die Oberfläche in der System-Monospace anders aus als im Preset.
- **Kein Live-Fortschritt.** `/api/v1/events/stream` steht in `SYNCOVA_API.md`,
ist aber **auch serverseitig nicht umgesetzt** — es fehlt nicht nur die
Anbindung. Laufende Sicherungen und Wiederherstellungen aktualisieren sich
beim Neuladen, nicht von selbst.
- **Kein Simple Mode.** Der Plan trennt Simple und Advanced Mode; umgesetzt ist
eine Ansicht.
- **Proxmox ist nicht auf echter Hardware freigegeben.** Die Seite sagt das
ausdrücklich.