Phase 9 abgeschlossen: Übersicht, Beobachtungsliste, helles Thema, Messung

Die Übersicht beantwortet vier Fragen, die man morgens hat: woran arbeite ich,
was ist fällig, wo hat jemand geschrieben, was hat sich getan. Jede Liste
läuft durch visibleCaseWhere - das ist der Ort, an dem eine unachtsame
Abfrage am ehesten eine Akte verrät. Ein Test prüft jede einzeln gegen eine zu
hoch eingestufte Akte. Der bisherige Inhalt ("Dein Zugang") steht jetzt unter
/zugang.

Beobachten ist eine Merkliste, kein Recht: nur was sichtbar ist, lässt sich
merken, und steigt eine Akte später über die eigene Stufe, verschwindet sie
aus der Liste, ohne dass jemand aufräumt.

Das helle Thema tauscht nur die semantische Ebene; die Grundpalette bleibt.
Die Klassifizierungsfarben sind für hellen Grund eine Spur dunkler, im Farbton
aber dieselben - eine Stufe muss in beiden Themen wiedererkennbar sein. Drei
Zustände, "wie das System" ist der Auslieferungszustand, und ein Skript im
Kopf setzt die Wahl, bevor das erste Mal gemalt wird.

Die Messung misst nicht den Nutzer. web-vitals schickt Zahlen und das
Routenmuster, keine Kennung und keinen konkreten Pfad: wer wann welche Akte
geöffnet hat, steht im Zugriffsprotokoll, und eine zweite Spur daneben wäre
eine Überwachungsfunktion ohne Auftrag. Ausgewertet wird Median und 75.
Perzentil, nicht der Mittelwert.

Mobil: die breiten Tabellen scrollen in sich, statt die Seite breiter zu
machen. Ein e2e-Test hält fest, dass keine Seite waagerecht überläuft.

scripts/restore-test.sh spielt eine Sicherung in eine Wegwerf-Datenbank und
prüft ausdrücklich, dass Regeln und Trigger mitkommen: eine Wiederherstellung
ohne entry_no_delete wäre eine Datenbank ohne die Zusicherung, um derentwillen
das System gebaut wird. Einmal durchgespielt, alles angekommen.

186 Tests im Web-Paket, 17 in der Datenbank, 7 im Worker, 54 e2e.
Grösste Route 116,2 von 120 kB.
This commit is contained in:
Harlekin
2026-09-03 10:28:50 +02:00
parent d2a7910861
commit addef20e16
26 changed files with 1381 additions and 85 deletions
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Spielt eine Sicherung in eine Wegwerf-Datenbank ein und prüft, ob alles
# angekommen ist. Ein ungetestetes Backup ist keins — siehe docs/11-betrieb.md.
#
# scripts/restore-test.sh [container] [quelldatenbank]
#
# Legt `<db>_restore_test` an, spielt einen frischen Dump hinein, vergleicht
# Zeilenzahlen, Regeln und Trigger, und räumt danach auf.
set -euo pipefail
CONTAINER="${1:-afis-dev-db}"
QUELLE="${2:-afis}"
ZIEL="${QUELLE}_restore_test"
NUTZER="afis"
rot() { printf '\033[31m%s\033[0m\n' "$*"; }
gruen() { printf '\033[32m%s\033[0m\n' "$*"; }
info() { printf ' %s\n' "$*"; }
psql_in() { docker exec "$CONTAINER" psql -U "$NUTZER" -d "$1" -tA -c "$2"; }
docker exec "$CONTAINER" pg_isready -U "$NUTZER" -d "$QUELLE" >/dev/null 2>&1 \
|| { rot "Container $CONTAINER antwortet nicht."; exit 1; }
echo "Wiederherstellung prüfen: ${QUELLE} -> ${ZIEL}"
info "Dump erzeugen"
docker exec "$CONTAINER" pg_dump -U "$NUTZER" -d "$QUELLE" -Fc -f /tmp/restore-test.dump
info "Zieldatenbank neu anlegen"
docker exec "$CONTAINER" psql -U "$NUTZER" -d postgres -q \
-c "DROP DATABASE IF EXISTS \"${ZIEL}\";" -c "CREATE DATABASE \"${ZIEL}\";"
info "Einspielen"
# pg_restore meldet auf einer frischen Datenbank Warnungen zu Besitzrechten.
# Die sind erwartbar; entscheidend ist der Vergleich danach.
docker exec "$CONTAINER" pg_restore -U "$NUTZER" -d "$ZIEL" --no-owner \
/tmp/restore-test.dump >/dev/null 2>&1 || true
FEHLER=0
info "Zeilen vergleichen"
for TABELLE in Case CaseEntry Document PersonnelFile AuditLog ChatMessage SearchIndex; do
A=$(psql_in "$QUELLE" "SELECT count(*) FROM \"$TABELLE\";" 2>/dev/null || echo "-")
B=$(psql_in "$ZIEL" "SELECT count(*) FROM \"$TABELLE\";" 2>/dev/null || echo "-")
if [ "$A" = "$B" ]; then
info "$TABELLE: $A"
else
rot " $TABELLE: Quelle $A, Wiederherstellung $B"
FEHLER=1
fi
done
# Ohne Regeln und Trigger wäre die Wiederherstellung eine Datenbank ohne die
# harten Zusicherungen: löschbare Verlaufseinträge, kein Suchindex.
REGELN=$(psql_in "$ZIEL" "SELECT count(*) FROM pg_rules WHERE schemaname='public';")
TRIGGER=$(psql_in "$ZIEL" "SELECT count(*) FROM pg_trigger WHERE NOT tgisinternal;")
info "Regeln: ${REGELN}, Trigger: ${TRIGGER}"
[ "$REGELN" -ge 5 ] || { rot " Zu wenige Regeln: die Anhängigkeit fehlt."; FEHLER=1; }
[ "$TRIGGER" -ge 5 ] || { rot " Zu wenige Trigger: Suchindex oder Wächter fehlen."; FEHLER=1; }
# Stichprobe auf die wichtigste Regel überhaupt.
psql_in "$ZIEL" "SELECT 1 FROM pg_rules WHERE rulename='entry_no_delete';" | grep -q 1 \
|| { rot " entry_no_delete fehlt: Verlaufseinträge wären löschbar."; FEHLER=1; }
info "Aufräumen"
docker exec "$CONTAINER" psql -U "$NUTZER" -d postgres -q -c "DROP DATABASE IF EXISTS \"${ZIEL}\";"
docker exec "$CONTAINER" rm -f /tmp/restore-test.dump
if [ "$FEHLER" = "0" ]; then
gruen "Wiederherstellung geprüft: alles angekommen."
else
rot "Wiederherstellung unvollständig."
exit 1
fi