Reaktion auf mehrere Bug-Reports und Feature-Wuensche zum bestehenden
Ticket-System (v1.1.0): 'Logs gehen nicht', 'Ticketverwaltung fehlt',
'Lizenzen koennen nicht vom Dashboard geloescht werden', plus eine Reihe
konkreter neuer Anforderungen.
## Bugfixes
- /logs: behob einen Absturz (jinja2.exceptions.UndefinedError:
'global_check_interval' is undefined) UND die eigentliche Ursache
dahinter -- logs.html war unveraendert aus dem TESM-Fork uebernommen
(PoE-Geraete-Polling-Text/-Farblogik, ein Template-Feld, das app.py nie
befuellt hat) und ausserdem schrieb auf diesem Server ueberhaupt nichts
in live.log (kein poe.sh-Aequivalent vorhanden). Live-Log zeigt jetzt
echte Ticket-/Lizenzaktionen, mit passendem Hinweistext und Faerbung.
- Lizenz-Loeschung war bereits vorhanden, aber nur ueber Ticket- ->
Lizenz-Detailseite erreichbar -- jetzt zusaetzlich inline in der
Lizenzhistorie jedes Tickets.
## Ticketverwaltung (neu: eigene Seite /tickets)
- Volle CRUD: Liste mit Suche, Bearbeiten (Typ/Module/Laufzeit/Lifetime,
wirkt nur auf die naechste Ausstellung), Loeschen (nur wenn keine
offene Lizenz mehr besteht, dann inkl. Historie).
- Dashboard (/) zeigt seitdem NICHT mehr die Ticket-Tabelle, sondern eine
rein informative Kachel-Uebersicht der aktuell AKTIVEN Lizenzen, exakt
im Kachel-Stil von TESMs eigenem Geraete-Dashboard: Ampelfarben (gruen
>90 Tage, orange 30-90, rot <30/abgelaufen), gruppiert, Firma + Hostname
der Kundeninstanz. Hostname kommt neu vom TESM-Client per Activate/
Heartbeat (srv/tesm/licensing.py, abwaerts-kompatibel: alte Clients ohne
das Feld ueberschreiben nie einen bereits bekannten Hostnamen).
## Kundenstammdaten
- Neue Felder: Strasse/Hausnummer/PLZ/Ort/Ansprechpartner. E-Mail ist beim
Anlegen jetzt Pflicht; beim Bearbeiten bewusst nur validiert, wenn
ausgefuellt (sonst waeren Bestandskunden aus der Zeit vor dieser
Umstellung fuer JEDE Aenderung blockiert gewesen).
## Kundenportal (neu, per Magic-Link, kein Login)
- /portal/<token>: zeigt einem Kunden ALLE seine Tickets/Lizenzen auf
einen Blick (ergaenzt den bestehenden Pro-Ticket-Link
/self-service/<ticket_id>) und erlaubt ihm, seine eigenen Kontakt-/
Anschriftdaten selbst zu pflegen -- Firmenname und kommerzielle
Ticket-Eckdaten bleiben bewusst admin-verwaltet. token = eigene Spalte
license_customers.portal_token, rueckwirkend fuer Bestandskunden erzeugt.
## Lifetime-Lizenzen
- Neues Ticket-Flag 'Lifetime': ignoriert die Laufzeit fuer jede REGULAERE
Ausstellung (Sentinel-Ablaufdatum 2099-12-31, licensing.
LIFETIME_EXPIRES_AT) -- eine zusaetzlich erzeugte Trial-Lizenz aus
demselben Ticket bleibt immer die normale 30-Tage-Variante. Trial ALS
TICKET-GRUNDTYP + Lifetime wird serverseitig verhindert (haette eine
dauerhafte, nie ablaufende Lizenz ganz ohne Module ergeben).
## Absicherung nach Review
Ein Adversarial-Review-Durchlauf ueber den kompletten Diff deckte vor dem
Deploy zusaetzlich auf und wurde behoben:
- TOCTOU-Race: zwei nahezu gleichzeitige 'Lizenz erstellen'-Anfragen fuer
dasselbe Ticket (z.B. Doppelklick im Self-Service) konnten beide
durchkommen. Jetzt durch einen partiellen Unique-Index
(idx_one_open_license_per_ticket) auf DB-Ebene ausgeschlossen; die
zweite Anfrage bekommt sauber die bereits erstellte Lizenz zurueck statt
eine zweite zu erzeugen.
- Analoger Unique-Index auf license_customers.portal_token als Backstop
fuer den neuen Magic-Link.
- tickets.html nutzte noch die alte, feste '<=30 Tage'-Schwelle statt der
gemeinsamen Ampelfarbe -- Lizenzen im 30-90-Tage-Bereich blieben dort
unmarkiert, obwohl das Dashboard sie schon als orange zeigte.
SCHEMA_VERSION 2 -> 3 (neue Spalten/Tabellen, ueber install.sh mit
Backup+Health-Check abgesichert).
Verifiziert: vollstaendige Migration gegen eine echte Kopie der
Live-Datenbank, anschliessend 22 Funktionstests direkt gegen die echten
Routen (Validierung, Lifetime-Ausstellung, Sperr-/Kaskadenregeln bei
Loeschungen, Kundenportal inkl. ungueltiger Eingaben) sowie ein Playwright-
Seitenladetest ohne Konsolenfehler -- beides nach den Review-Fixes erneut
komplett durchlaufen, inkl. direktem Nachweis, dass der neue Unique-Index
eine doppelte offene Lizenz pro Ticket tatsaechlich auf DB-Ebene ablehnt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>