Commit Graph
6 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 3d0ff54bf5 Anmeldung wahlweise ueber OpenID Connect (Authentik)
Neben dem Passwort-Login gibt es jetzt einen zweiten, gleichberechtigten Weg:
OpenID Connect gegen einen selbst gehosteten Anmeldedienst. Ohne Konfiguration
aendert sich nichts - keine SSO-Knoepfe, Passwort-Login wie bisher.

Der Anmeldedienst ist NUR fuer die Anmeldung zustaendig. Danach stellt Vorrania
sein eigenes Token aus wie beim Passwort-Login, deshalb bleiben deps.py, die
Rollenpruefung und die langlebigen API-Tokens unveraendert.

Backend
- Anbieter kommen aus der Umgebung (OIDC_PROVIDERS + OIDC_<KUERZEL>_*). Ein
  zweiter Anbieter ist damit reine Konfiguration, ohne Codeaenderung.
- Browser-Weg (vertraulicher Client) und App-Weg (oeffentlicher Client, PKCE)
  enden beide im selben Token-Schema.
- state/nonce/PKCE-Verifier liegen in einer kurzlebigen Tabelle statt in einem
  Cookie: Starlettes CORS-Schicht spiegelt bei allow_origins=["*"] zusammen mit
  allow_credentials die Herkunft zurueck, sobald ein Request ein Cookie traegt.
  Ohne Cookie entsteht diese Wechselwirkung erst gar nicht - und der Ablauf
  bleibt mit der db-Fixture pruefbar.
- Zuordnung ueber die sub des Anbieters; die Erstverknuepfung laeuft ueber die
  BESTAETIGTE E-Mail-Adresse. Rollen kommen nie aus dem Anbieter, und ein
  gleichlautender preferred_username uebernimmt nie ein bestehendes Konto.
- users.password_hash darf jetzt leer sein (reine SSO-Konten). verify_password
  faengt das ab, sonst antwortete der Login mit 500 statt 401.

Verknuepfen bestehender Konten
- Neue Seite "Mein Konto" (fuer alle Benutzer, nicht nur Admins): verknuepfen
  und wieder loesen. Die Berechtigung stammt aus der laufenden Sitzung, es
  braucht also weder eine hinterlegte Adresse noch Admin-Rechte.
- Der Start laeuft ueber POST, weil eine Seitennavigation das Bearer-Token
  nicht mitschicken kann.
- Die letzte Anmeldemoeglichkeit eines Kontos laesst sich nicht loesen.

iOS
- AppAuth (erste Paketabhaengigkeit) mit PKCE im System-Browser, nie in einem
  eingebetteten WebView, und ohne Client-Secret.
- offline_access nur im App-Weg: der Browser-Weg braucht kein Refresh-Token und
  bekaeme sonst einen ueberfluessigen Zustimmungsdialog.
- Anmeldezustand je Serverprofil im Keychain; abgelaufene Token erneuert die App
  still im Hintergrund. Gleichzeitige 401er teilen sich eine Erneuerung.

Deployment
- docker-compose reicht die ganze .env durch. Einzeln aufzuzaehlen ginge nur mit
  fest verdrahteten Kuerzeln - genau das soll ein weiterer Anbieter nicht
  erfordern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 21:55:38 +02:00
ScarriffleandClaude Opus 5 8d4954610b Bilder blockieren nicht mehr die halbe Seite
Warum eine angeklickte Artikelseite nur zur Haelfte lud: Die Bild-Route wird je
Tabellenzeile einmal aufgerufen, und ein Browser haelt je Host nur rund sechs
Verbindungen offen. Was diese Route aufhaelt, legt die ganze Oberflaeche lahm.
Sie hielt gleich dreifach auf:

1. Fehlte die lokale Kopie, holte ``images.ensure`` das Bild MITTEN in der
   Anfrage von der fremden Adresse - bis zu 15 Sekunden, je Zeile, und bei
   jedem Aufruf erneut, weil ein Fehlschlag nirgends vermerkt wurde. Artikel
   aus einem Backup-Import haben genau diese Ausgangslage. Gemessen: 2,01 s
   je Aufruf, dreimal hintereinander. Jetzt: 0,01 s, das Nachholen laeuft nach
   der Antwort im Hintergrund und ein toter Link wird eine Stunde gesperrt.

2. Ein Vorschaubild ist 34x34 Pixel gross - ausgeliefert wurde das Original.
   Gemessen: 2530 KB je Briefmarke, bei 40 Zeilen 99 MB fuer eine Liste. Neu
   erzeugt die Bild-Route (Pillow) eine Vorschau und legt sie daneben ab;
   ``?thumb=1`` liefert sie aus. Gemessen: 0,6 KB.

3. If-None-Match wurde ignoriert. Nach den 5 Minuten Cache-Frist lud der
   Browser jedes Bild komplett neu - das erklaert, warum es "alle 20 Minuten"
   wieder losging. Jetzt 304 ohne Daten.

Dazu im Web: laufende Bildabrufe werden beim Seitenwechsel abgebrochen (sonst
steht die neue Seite hinter Bildern Schlange, die niemand mehr sieht), und wo
``image_version`` bekannt ist, entfaellt die Anfrage fuer bildlose Artikel ganz.

Und der Verbindungsvorrat der Datenbank: 5 (+10) gegen 40 Arbeits-Threads von
FastAPI. Ab der 16. gleichzeitigen Anfrage wartete eine Route stillschweigend
30 Sekunden - von aussen ein haengender Server. Jetzt 10 (+20) mit 10 Sekunden
Frist: lieber ein Fehler als eine halbe Minute Stille.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:14:43 +02:00
ScarriffleandClaude Opus 4.8 61270e6d00 Backend: Kaufpreis + Belege an Einzelstücken, PDF-Garantie-Vorschlag
- Item: price_cents + currency (Kaufpreis rappen-/centgenau). Migration ergaenzt.
- Neue Tabelle item_documents (mehrere Belege je Stueck, PDF oder Bild); Blob
  wird verzoegert geladen, damit Item-Listen leicht bleiben.
- Endpunkte: Upload (POST), Ansehen/Download (GET), Loeschen (DELETE) je Stueck.
  Dateiname beim Download gehaertet (keine Header-Injektion).
- services/warranty.py: schaetzt aus PDF-Text lokal ein Garantieende
  (Zeitraum + Kaufdatum, oder Datum nahe Garantie-Stichwort); pypdf ergaenzt.
  Der Upload liefert den Vorschlag zurueck. 10 neue Tests, Suite 136 gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 20:49:08 +02:00
ScarriffleandClaude Opus 4.8 12823a6d73 Backend: python-multipart ergaenzen (Fix fuer Startup-Crash)
Der Login-Endpunkt nutzt OAuth2PasswordRequestForm (Form-Data); FastAPI
benoetigt dafuer python-multipart. Fehlte in requirements.txt -> RuntimeError
beim Import von auth.py -> Backend-Restart-Loop -> 502.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:46:06 +02:00
ScarriffleandClaude Opus 4.8 022a6d5ea8 Backend: passlib entfernt, bcrypt direkt nutzen (Fix fuer Startup-Crash/Restart-Loop)
passlib 1.7.4 vertraegt sich schlecht mit bcrypt 4.x und kann beim Hashen des
Admin-Passworts im Startup (ensure_first_admin) crashen -> Backend-Container in
Restart-Loop -> 502 in der Web-UI. hash_password/verify_password nutzen jetzt
bcrypt direkt (mit 72-Byte-Grenze). passlib aus requirements entfernt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:39:36 +02:00
ScarriffleandClaude Opus 4.8 639126468f Schritt 1: Fundament der Lebensmittel-Lagerverwaltung (Pantry)
Backend (FastAPI + PostgreSQL): Chargen mit MHD, FEFO-Auslagern,
Einheiten-Umrechnung (Stueck/g/ml + Packungen), Open-Food-Facts-Lookup
mit lokalem Fallback, JWT-Auth mit Rollen (Admin/Nutzer), erster Admin
beim Setup, Einkaufsliste, Ablaufwarnung, Lagerorte, pytest fuer FEFO.

Web-UI (React/Vite): Login, Dashboard, Ein-/Auslagern, Produkte,
Lagerorte, Benutzerverwaltung, Einkaufsliste - rollenabhaengig.

Deploy: docker-compose + install.sh (Docker-Autoinstall, Secrets),
README und Roadmap fuer Schritt 2 (iOS) und Schritt 3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:14:14 +02:00