Vorabpruefung der Branches und Tests ist enthalten. Nichts davon ist umgesetzt - der Plan haelt nur fest, was gefunden wurde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
3.4 KiB
Plan: Verbesserungen
Erstellt am 13.09.2026 nach einer Durchsicht des gesamten Repos, parallel zur
Kantone-App. Ergänzt Plan.md, der den ursprünglichen Aufbau beschreibt.
Noch nichts davon ist umgesetzt.
Vorabprüfung (13.09.2026, erledigt)
| Arbeitsverzeichnis | sauber |
| Offene Branches | keine |
| Tests | 81 grün in 12 Sekunden |
| Letzter Tag | v1.0.0 |
Der CI-Lauf zu v1.0.0 wurde nie bestätigt. Der Tag wurde einmal auf dem
Server gelöscht und danach auf e0212cf neu gesetzt. Vor dem nächsten
Release prüfen, ob in der Registry tatsächlich ein Image liegt.
Zum Zustand des Codes
Die Durchsicht hat wenig ergeben, und das ist das Ergebnis: Escaping, CSRF, Token-Vergleich in konstanter Zeit, Bild-Upload mit Inhaltsprüfung statt Endung, bewusst vermiedenes N+1, CHECK-Constraints in der Datenbank statt nur in Python — das ist alles vorhanden und richtig gemacht.
Die Punkte unten sind Ergänzungen, keine Korrekturen.
P1 — Keine Sicherung der Datenbank
Der wichtigste offene Punkt, und hier wiegt er schwerer als bei der
Kantone-App: In kleiderboerse.sqlite stecken Einträge und in uploads/
die dazugehörigen Fotos. Beides ist nicht wiederherstellbar — anders als
die Kantonsdaten, die sich neu importieren liessen.
docker-compose.betrieb.yml sieht keinen Sicherungsschritt vor.
Ein cp der laufenden Datei genügt nicht; sie kann mitten in einer
Schreiboperation erwischt werden. Richtig ist sqlite3 ... ".backup ..."
oder VACUUM INTO, beide konsistent auch bei laufendem Zugriff.
Vorgehen
- Täglicher Lauf, sieben Stände, danach rollierend überschreiben.
- Die Bilder gehören dazu — eine Datenbank ohne
uploads/ist wertlos. - Einmal einen Stand tatsächlich zurückspielen und prüfen, dass die Galerie danach vollständig ist. Eine ungetestete Sicherung ist keine.
Punkt 3 ist kein Formalismus: Am 13.09.2026 ist Jellyfin auf demselben Netzwerk an einer Datenbank-Migration hängengeblieben. Dass es dort Sicherungen gab, war der einzige Grund, warum die Lage beherrschbar blieb.
P2 — Antworten werden nicht komprimiert
app/main.py bindet keine GZipMiddleware ein. Jede HTML- und
HTMX-Antwort geht unkomprimiert über die Leitung — bei einer Galerie mit
vielen Kacheln merkbar, besonders über Mobilfunk.
from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
P2 — Statische Dateien ohne Cache-Dauer
app.mount("/static", StaticFiles(directory=...), name="static")
Ohne max_age werden htmx.min.js, theme.js und die beiden
Swagger-Dateien bei jedem Aufruf neu verhandelt. Eine lange Cache-Dauer
setzen und bei Änderungen einen Versionsanhänger an die URL hängen.
Für die hochgeladenen Bilder gilt dasselbe — die Dateinamen sind zufällig erzeugt und ändern sich nie, die dürfen dauerhaft im Cache bleiben.
P4 — Zwei Filterspalten ohne Index
crud.items_suchen() filtert unter anderem nach gender und season.
Beide Spalten haben keinen Index; status, size und category_id haben
einen.
Bei den zu erwartenden Stückzahlen ist das ohne praktische Bedeutung — der Vollständigkeit halber notiert, nicht als Handlungsbedarf.
Reihenfolge
- Sicherungen einrichten, inklusive Bilder, mit einer echten Rückspielprobe
- GZip und Cache-Dauer — zusammen wenige Zeilen
- Rest nach Bedarf