Der Sinn dieses Downloads ist der aktuelle Stand. Die Antwort trug weder Cache-Control noch ETag noch Last-Modified - damit darf ein Browser oder ein zwischengeschalteter Proxy selbst entscheiden, ob er eine aeltere Antwort wiederverwendet, und dann fehlen neu angelegte Zeilen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vorrania – Selfhostbare Vorrats- und Gegenstandsverwaltung
Ein selbst gehosteter Dienst, um sowohl deinen Lebensmittelvorrat als auch allgemeine Gegenstände (Haushalt, Elektronik, Kleidung, Verbrauchsmaterial …) zu verwalten: Ein-/Auslagern per Barcode, Chargen mit MHD, Einzelstücke mit eigener UID/QR, Mindestbestände, automatische Einkaufsliste, verschachtelte Lagerorte, Gruppen, Kategorien und Dashboards – mit Web-UI und nativer iOS-App, die beide dieselbe REST-API nutzen.
Was Vorrania verwaltet
Lebensmittel laufen als Chargen mit Mindesthaltbarkeitsdatum. Beim Auslagern wird automatisch die zuerst ablaufende Charge entnommen (FEFO).
Gegenstände kennen drei Arten:
- Menge je Lagerort – reines Zählen (z. B. Unterhosen, Batterien).
- Einzelstücke – jedes Exemplar mit eigener UID/QR, Kaufdatum, Garantie, Bezugsquelle und Beleg (z. B. Powerbank, Kamera).
- Verbrauchsgegenstand – wie ein Lebensmittel als Charge mit Menge und Einheit geführt (z. B. Sonnencreme in ml), nur ohne eindeutigen Code.
Features
- Barcode-Lookup über Open Food Facts und Open Products Facts mit lokalem Fallback; unbekannte Produkte selbst anlegen, Bild wird automatisch geholt.
- Chargen mit MHD & FEFO, Mengen in Packungen oder Teilmengen (Basiseinheit Stück / Gramm / Milliliter plus Packungsgröße/Gebinde).
- Lagerorte beliebig verschachtelt (Keller → Regal 2 → Fach A), mit QR-Etiketten, Inhaltsansicht („was liegt hier?“), Umlagern und Entfernen mit Grund. Chargen und Einzelstücke sind einem Lagerort zugeordnet.
- Einzelstücke verleihen mit Name und Grund als Freitext und optionalem Rückgabedatum – auch direkt nach dem Scannen des QR/NFC-Tags. Der Lagerort bleibt stehen (dorthin gehört das Stück zurück), als verliehen zählt es aber nicht mehr zum Bestand. Zurückgegebene Verleihe bleiben als Verlauf erhalten; überfällige Rückgaben sind in Liste und Dashboard rot.
- Kategorien verschachtelt und pro Typ (Lebensmittel/Gegenstand); für Gegenstände mit eigenen Feldern (z. B. Kapazität, Größe, Farbe).
- Gruppen rechnen Bestände mehrerer Marken/Artikel zusammen (z. B. „5 kg Mehl“) mit eigenem Mindestbestand und Einheit.
- Mindestbestände je Produkt und Gruppe – gesamt und je Lagerort – mit zentraler Übersichtsseite, daraus die automatische Einkaufsliste und Ablaufwarnung für bald ablaufende Chargen.
- Übergreifende Listen für Produkte (getrennt Lebensmittel/Gegenstände), Einzelstücke und Chargen – filter-, sortier- und spaltenkonfigurierbar; bei Chargen lässt sich der Lagerort mehrerer auf einmal setzen.
- Dashboards mit konfigurierbaren Karten und Diagrammen, Verlauf (wer hat wann was ein-/ausgelagert), Stammdaten (Einheiten, Gebinde, Shops) und Import/Export.
- Mehrbenutzer mit Rollen: Admin verwaltet Stammdaten und Benutzer, Nutzer lagert nur ein/aus und sieht Bestände; alle Bewegungen werden protokolliert.
- Single Sign-on (optional) über OpenID Connect, z. B. gegen ein selbst gehostetes Authentik. Der Passwort-Login bleibt daneben bestehen – siehe Single Sign-on.
- Sitzungsdauer einstellbar (Einstellungen → Anmeldung): 7 Tage bis 1 Jahr oder unbegrenzt, für Website und App gemeinsam. Die Frist zählt ab dem letzten Besuch, nicht ab dem Anmelden.
In der iOS-App zusätzlich
- Scannen von Barcode, MHD/Datum per Texterkennung (auch nur Tag+Monat ohne Jahr bei Frischware), Lagerort-QR und Name/Marke per Text-Scan.
- Verleihen direkt nach dem Scannen – QR, eingenähtes NFC-Tag oder angetipptes Tag landen alle im Einzelstück, wo Herausgeben und Zurücknehmen gleich oben stehen.
- Foto direkt beim Anlegen (Kamera/Galerie), Push-Benachrichtigungen bei ablaufenden Produkten, lokale Dashboards und Home-Screen-Schnellaktionen.
Details zur App: ios/README.md.
Architektur
| Teil | Technik |
|---|---|
| Backend | Python · FastAPI · SQLAlchemy |
| Anmeldung | Passwort (JWT) und optional OpenID Connect |
| DB | PostgreSQL (SQLite für Tests/lokal) |
| Web-UI | React · Vite (via nginx ausgeliefert) |
| iOS | Native SwiftUI-App (VisionKit-Scanner) |
| Deploy | Docker Compose · install.sh |
backend/ FastAPI-App + Tests
web/ React/Vite SPA
ios/ Native SwiftUI-App (XcodeGen-Projekt)
deploy/ docker-compose.yml, install.sh, update.sh, .env.example
docs/ Roadmap
Das Schema wird beim Start per idempotenter ALTER TABLE-Migration in
backend/app/main.py nachgezogen (kein Alembic).
Installation (selfhosted, Linux)
Voraussetzung: eine Linux-Maschine (Server, NAS, Raspberry Pi …). Docker wird bei Bedarf automatisch installiert.
git clone https://git.scarriffle.com/Scarriffle/vorrania.git
cd vorrania/deploy
chmod +x install.sh
./install.sh # interaktiv – fragt Port & Admin-Passwort
# oder vollautomatisch mit generiertem Admin-Passwort:
# ./install.sh --yes
Ist das Gitea-Repo privat, fragt
git clonenach Benutzername + Access-Token. Fallsgitfehlt:apt update && apt install -y git.
Nach dem Start:
- Web-UI:
http://<server-ip>:8080(Port konfigurierbar) - Anmeldung mit dem im Installer angezeigten Admin-Benutzer/Passwort.
Die iOS-App wird mit derselben Adresse verbunden (http://<server-ip>:8080).
Single Sign-on (optional)
Neben dem Passwort-Login kann sich Vorrania an einen OpenID-Connect-Dienst hängen (getestet mit Authentik). Ohne Konfiguration ändert sich nichts: Es gibt keinen SSO-Knopf, und der Passwort-Login bleibt der einzige Weg.
1. In Authentik zwei Anwendungen anlegen – Web-UI und App brauchen unterschiedliche Client-Typen, und ein Provider kann nur einer davon sein:
| Für | Client-Typ | Redirect-URI |
|---|---|---|
| Web-UI | Confidential (mit Secret) | https://<deine-adresse>/api/auth/oidc/authentik/callback |
| iOS-App | Public (ohne Secret, PKCE) | vorrania://oauth/callback |
Drei Punkte, an denen es sonst klemmt:
- Das
/apiin der Web-Adresse ist kein Versehen: Es entsteht im mitgelieferten nginx und gehört deshalb in die bei Authentik hinterlegte Adresse. - Signing Key setzen (Provider ▸ Advanced protocol settings). Ohne ihn signiert Authentik mit HS256, und Vorrania prüft gegen den öffentlichen Schlüsselsatz – das Token würde abgelehnt.
- Jede Anwendung hat in Authentik ihren eigenen Issuer (aus dem
Application-Slug). Deshalb bekommt der App-Client eine eigene
Discovery-Adresse:
OIDC_AUTHENTIK_NATIVE_DISCOVERY_URL.
Damit die App ihre Anmeldung im Hintergrund erneuern kann, muss der öffentliche
Client offline_access ausliefern dürfen (in Authentik seit 2024.2 ein eigenes
Scope-Mapping) – sonst kommt kein Refresh-Token zurück.
2. deploy/.env ergänzen (vollständige Vorlage in deploy/.env.example):
PUBLIC_BASE_URL=https://vorrania.example.com
OIDC_PROVIDERS=authentik
OIDC_AUTHENTIK_DISPLAY_NAME=Authentik
OIDC_AUTHENTIK_DISCOVERY_URL=https://authentik.example.com/application/o/vorrania/.well-known/openid-configuration
OIDC_AUTHENTIK_CLIENT_ID=...
OIDC_AUTHENTIK_CLIENT_SECRET=...
OIDC_AUTHENTIK_NATIVE_CLIENT_IDS=vorrania-ios
OIDC_AUTHENTIK_NATIVE_DISCOVERY_URL=https://authentik.example.com/application/o/vorrania-ios/.well-known/openid-configuration
Danach ./update.sh (oder docker compose --env-file .env up -d).
Ob es geklappt hat, verrät curl http://<server>:8080/api/auth/oidc/providers.
3. Bestehende Konten verknüpfen. Beim ersten SSO-Login zu einer noch unbekannten Kennung fragt Vorrania nach, statt still ein zweites Konto anzulegen:
Die Anmeldung über Authentik hat geklappt – zu diesem Zugang gibt es hier aber noch kein Konto. [ Bestehendes Konto verknüpfen ] oder [ Neues Konto anlegen ]
Das Verknüpfen verlangt einmalig Benutzername und Passwort des bestehenden Kontos. Dieser Nachweis ist der Grund, warum der Anmeldeweg selbst nie fragt „welches Konto?" – ohne ihn könnte sich jeder mit einem beliebigen Konto beim Anmeldedienst an ein fremdes Vorrania-Konto hängen. Rolle und Verlauf bleiben dabei erhalten.
Zwei Wege führen daran vorbei, weil sie das Konto schon vorher finden:
- E-Mail-Adresse unter Benutzer eintragen – dieselbe wie in Authentik. Verknüpft wird nur bei bestätigter Adresse.
- Vorab verknüpfen: angemeldet bleiben, unten in der Seitenleiste auf den eigenen Namen → Mein Konto → Verknüpfen. Dort lässt sich eine Verknüpfung auch wieder lösen – außer es wäre die letzte Anmeldemöglichkeit.
Mit OIDC_AUTHENTIK_AUTO_CREATE=false entfällt der Knopf „Neues Konto anlegen";
dann bleibt nur das Verknüpfen mit einem Konto, das der Admin angelegt hat.
Die App zeigt diesen Dialog nicht – dort gilt weiterhin: bekannte Kennung oder
passende Adresse, sonst je nach AUTO_CREATE ein neues Konto oder eine Absage.
Einmal im Browser verknüpft, funktioniert die App danach ohne Weiteres.
Weitere Anbieter sind reine Konfiguration: Kürzel in OIDC_PROVIDERS ergänzen
und einen zweiten Block mit dessen Präfix anlegen (OIDC_<KÜRZEL>_…) – am Code
ändert sich dafür nichts.
Was dabei bewusst so gebaut ist
- Der Anmeldedienst ist nur für die Anmeldung zuständig. Danach stellt Vorrania sein eigenes Token aus, wie beim Passwort-Login – Rollen, API-Tokens und alle Endpunkte bleiben unverändert.
- Rollen kommen nie aus dem Anbieter. Neu angelegte SSO-Konten sind immer Benutzer; Admin vergibt ein Admin von Hand.
- Der Benutzername wird einmal beim Anlegen übernommen und gehört danach Vorrania – ein späterer Namenswechsel beim Anmeldedienst schlägt nicht durch. Ändern lässt er sich jederzeit unter Benutzer: Sitzungen, Verlauf und API-Tokens hängen an der Benutzer-ID, nicht am Namen.
- Automatisch verknüpft wird nur bei bestätigter E-Mail-Adresse. Sonst könnte ein Konto im Anmeldedienst mit frei gewählter Adresse ein fremdes Vorrania-Konto übernehmen. Beim Verknüpfen über Mein Konto entfällt das, weil dort die eigene Sitzung bürgt – eine bereits anderweitig vergebene Kennung wird aber auch dort abgelehnt.
- Die App meldet sich ausschließlich im System-Browser an (AppAuth mit PKCE), nie in einem eingebetteten WebView, und sendet nie ein Client-Secret.
Aktualisieren
cd vorrania/deploy
./update.sh # holt den neuesten Stand aus Git und baut neu
./update.sh --prune # zusätzlich alte Images aufräumen
./update.sh --no-pull # nur neu bauen, ohne git pull
Das Skript ist von überall aufrufbar, nutzt die vorhandene deploy/.env und
prüft am Ende, ob das Backend wieder erreichbar ist.
Verwaltung:
cd deploy
docker compose logs -f # Logs ansehen
docker compose down # stoppen
docker compose up -d # starten
Die Konfiguration liegt in deploy/.env (Passwörter, Port, JWT-Secret). Für den
Produktivbetrieb bitte hinter einen HTTPS-Reverse-Proxy (z. B. Caddy/Traefik) stellen.
Entwicklung (ohne Docker)
Backend:
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# lokale SQLite-DB nutzen:
export DATABASE_URL="sqlite:///./vorrania.db"
uvicorn app.main:app --reload
API-Doku dann unter http://localhost:8000/docs.
Web:
cd web
npm install
npm run dev # http://localhost:5173, proxyt /api → localhost:8000
iOS: siehe ios/README.md (XcodeGen-Projekt, xcodegen generate).
Tests:
cd backend
pip install -r requirements.txt
DATABASE_URL="sqlite://" pytest
Weiteres
Fahrplan und Ideen: docs/ROADMAP.md.