Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kundenplattform
Kunden-Web-UI für den Tuxflotte-Auftragskatalog — von provisioning-server
(anode) bewusst getrennter Dienst, siehe ADR-0011.
Architektur
- FastAPI + Jinja2 (serverseitig gerendert, kein Frontend-Build).
- Eigene Postgres-Datenbank (
benutzer-Tabelle: E-Mail/Passwort-Hash pro Organisation) — getrennt von provisioning-servers Datenbank, keine Cross-Database-Referenzen. - Spricht mit anode ausschließlich über dessen HTTP-API, authentifiziert mit
einem eigenen Service-Token (
TUXFLOTTE_KUNDENPLATTFORM_TOKEN, getrennt vom Admin-Token). Organisationszugehörigkeits-Prüfung (darf dieser Login auf dieses Gerät zugreifen?) passiert hier in der Anwendung, vor jedem Aufruf gegen anode — siehefind_device_in_organization()inapp.py.
Setup (lokal)
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
KUNDENPLATTFORM_DATABASE_URL=postgresql://... .venv/bin/psql ... -f migrations/0001_initial_schema.sql
Env-Variablen:
TUXFLOTTE_ANODE_URL(Defaulthttp://127.0.0.1:8080)TUXFLOTTE_KUNDENPLATTFORM_TOKEN— muss mit dem gleichnamigen Wert in provisioning-servers/etc/tuxflotte/provisioning-server.envübereinstimmenKUNDENPLATTFORM_DATABASE_URLKUNDENPLATTFORM_SESSION_SECRET— beliebiger zufälliger String zum Signieren der Session-Cookie (z.B.openssl rand -base64 32)
Start: .venv/bin/uvicorn app:app --port 8081
Mailversand
Siehe ADR-0041.
Aufrufer tragen Mails mit mail.einreihen() in die Tabelle mail_ausgang
ein (Migration 0009). Eine Versandschleife im Dienst liefert sie per SMTP
mit Anmeldung beim Mailanbieter der Domain ein und wiederholt bei Fehlern
(nach 1, 5 und 15 Minuten, dann „fehlgeschlagen“). Der Mailtext wird nach
dem Endzustand geleert, Einträge werden nach 90 Tagen gelöscht.
Env-Variablen:
-
KUNDENPLATTFORM_MAIL_MODUS—trockenlauf(Default, schreibt die Mail ins Journal statt sie zu verschicken) odersmtp -
KUNDENPLATTFORM_MAIL_HOST,KUNDENPLATTFORM_MAIL_PORT(Default587, STARTTLS) -
KUNDENPLATTFORM_MAIL_BENUTZER— Anmeldename des Postfachs -
KUNDENPLATTFORM_MAIL_ABSENDER— z.B.Tuxflotte <konto@tuxflotte.de> -
KUNDENPLATTFORM_MAIL_ANTWORT_AN— optional -
KUNDENPLATTFORM_OEFFENTLICHE_URL— Basis der Links in Mails (Defaulthttps://flotten-management.tuxflotte.de), bewusst nicht aus dem Host-Header abgeleitet
Erster Aufrufer ist der Passwort-Reset („Passwort vergessen?“ auf der
Login-Seite, Routen in auth.py). Plattform-Admins (is_superuser) sind
davon ausgenommen, für sie bleibt scripts/reset_password.py.
Das Passwort steht nicht in der Umgebung, sondern kommt als
systemd-Credential mail_passwort (siehe kundenplattform.service); für
Aufrufe außerhalb des Dienstes KUNDENPLATTFORM_MAIL_PASSWORT_DATEI.
Testmail (prüft Konfiguration und Zugangsdaten):
KUNDENPLATTFORM_DATABASE_URL=... KUNDENPLATTFORM_MAIL_MODUS=smtp ... \
.venv/bin/python scripts/testmail.py --an jemand@example.org
Tests (brauchen eine Wegwerfdatenbank mit „test“ im Namen und eingespielter Migration 0009; SMTP ist gemockt):
KUNDENPLATTFORM_DATABASE_URL=postgresql://.../kundenplattform_test \
.venv/bin/python -m unittest discover -s tests -v
Konto anlegen
Kein Self-Service in v1 (siehe ADR-0011) — Konten werden manuell angelegt:
KUNDENPLATTFORM_DATABASE_URL=... .venv/bin/python scripts/create_user.py \
--organization-id <organization-uuid> \
--email admin@schule-beispiel.de
Deploy (anode)
Wie bei provisioning-server, git-basiert statt scp:
git pull --ff-only
systemctl restart kundenplattform
Läuft unter einem eigenen, unprivilegierten System-User kundenplattform
(nicht root — seit ADR-0027 auch provisioning-server, siehe dort) — siehe
kundenplattform.service.
Port 8081 (provisioning-server belegt 8080).
Öffentliche Erreichbarkeit über Pangolin ist nicht Teil dieses Repos — das ist ein separater Ops/DNS/Reverse-Proxy-Schritt.