V5 komplett: Auth, Admin, Federation, Channels, Devices, Cockpit, Profil, Autotext-Fix, Uebersetzer-Fix

Made-with: Cursor
This commit is contained in:
2026-04-20 14:38:16 +02:00
parent c53bba4587
commit dcce7107ab
9 changed files with 2254 additions and 320 deletions

View File

@@ -1,6 +1,6 @@
# AZA Master Handover / Operational Runbook
## Arbeitsmodus / Regeln
## Arbeitsmodus / Regeln (VERBINDLICH)
User bastelt nicht; nur Composer-Patches (meist Opus) oder 1 exakter Command mit Pfad.
@@ -8,10 +8,76 @@ User bastelt nicht; nur Composer-Patches (meist Opus) oder 1 exakter Command mit
- User fuehrt nur vorgegebene Commands aus, keine manuellen Edits.
- Jede Aenderung in 1 Patch, kein schrittweises Anleiten.
- Keine risky Refactors immer minimal und sicher.
- KEINE Bastelloesungen, KEINE Prototypisierung, KEINE Zwischenloesungen.
- KEINE localStorage-Geschaeftsdaten als Zielbild.
- KEINE vagen „spaeter besser"-Loesungen.
- KEINE Diffs als Hauptlieferung nur vollstaendige fertige Dateien.
- Immer dazuschreiben WO etwas auszufuehren ist: Browser / Windows PowerShell / Hetzner SSH / Composer.
- Root-cause-first bei jedem Problem.
- Nur Schritte empfehlen, die zur Live-Architektur passen.
## AKTUELLE PROJEKTPHASE (Stand 2026-04-12)
## Server-Deploy-Realitaet (VERBINDLICH)
**Phase:** Device-/Seat-Logik V1 implementiert. Backup-Konzept + Deinstallations-UX als naechste Hauptbloecke.
Hetzner `/root/aza-app` ist KEIN Git-Repository.
Server-Updates laufen real ueber:
1. Lokal aendern (Composer/Max)
2. Per `scp` auf Hetzner hochkopieren
3. Auf Hetzner: `cd /root/aza-app/deploy && docker compose up --build -d`
NICHT `git pull` auf Hetzner annehmen. Das funktioniert dort nicht.
## AKTUELLE PROJEKTPHASE (Stand 2026-04-18)
**Phase:** Praxis-Chat V5 live (Auth + Sessions + Tasks + Admin + Devices + Channels + Federation).
Admin-Panel im Browser eingebaut. Naechste Bloecke: Admin real pruefen, Empfangs-Huelle NaN-Bug, Uebersetzer-Bug.
## BEKANNTE REGRESSIONEN + KORREKTUREN
### Autotext-Regression April 2026 (GEFIXT + EINGEFROREN)
**Root Cause:** Race Condition zwischen zwei gleichzeitig aktiven Autotext-Systemen:
1. In-App-Autotext (`on_keyrelease` auf Tkinter Text-Widgets, `_bind_autotext()`)
2. Globaler Autotext-Listener (pynput `_run_global_autotext_listener()`)
Der Fokusstatus (`_autotext_focus_in_app`) wird per PID-Check alle 1000ms aktualisiert.
In dem Zeitfenster konnten beide Systeme feuern → doppelte Einfuegung.
**Symptome:**
- Doppelte Autotext-Einfuegung in AZA-Textfeldern
- Teils unzuverlaessiges Verhalten in externen Programmen
**Funktionierende Korrektur (basis14.py, `_bind_autotext()`):**
- `_autotext_injecting`-Check am Anfang von `on_keyrelease`: ueberspringt wenn der
globale Listener gerade injiziert
- Deduplizierung: wenn dasselbe Wort innerhalb von 1 Sekunde erneut expandiert werden
soll, wird es uebersprungen (`_last_expansion` mit Zeitstempel + Wort)
- Kein grosser Umbau, nur minimale Absicherung
**Do-Not-Break-Regeln (VERBINDLICH):**
- `_run_global_autotext_listener()` NICHT aendern (Freeze seit Maerz 2026)
- `_bind_autotext()` NICHT aendern ausser bei reproduzierbarem Bug
- `_periodic_focus_check()` NICHT aendern
- `_check_autotext_focus_out()` NICHT aendern
- Die `_last_expansion`-Deduplizierung NICHT entfernen
- Den `_autotext_injecting`-Check in `on_keyrelease` NICHT entfernen
- KEINE Disk-I/O in on_press/on_release
- KEINE Aenderung der Timing-Konstanten (REPLACE_DELAY, 0.04s/0.01s, etc.)
- Bei kuenftigen Aenderungen an basis14.py: Autotext-Bereich (Zeilen ~8825-9082) explizit
NICHT anfassen, ausser bei reproduzierbarem Bug mit klarer Root Cause
## OFFENE REALE BUGS (Stand 2026-04-18)
1. **Empfangs-Huelle: Schriftgroesse NaN** (GEFIXT, Deploy ausstehend)
- Root Cause: Huelle laedt alte empfang.html. Neue Version hat robustes parseInt + kein uiScale.
- Fix: neue empfang.html per scp deployen
2. **Uebersetzer: Zielsprache wird nicht eingehalten** (GEFIXT, Neustart noetig)
- Root Cause: `get_lang_codes()` fiel bei fehlendem ` ` im Combo-Wert auf `"en"` zurueck
- Fix: 3 Stellen in translate.py gefixt, Fallback nutzt gespeicherte Sprache
3. **`An Empfang senden` (`basis14.py`): noch nicht professionell**
- ~1100 Zeilen `_send_to_empfang()` Hochrisikobereich
- Offen: Auto-Integration medizinischer Inhalte, Bilder, Chatfluss, Aktionsleiste
## VERBINDLICHE PROJEKTKONTINUITAET (ab 2026-04-12)
@@ -218,40 +284,190 @@ kurze Antworten, kein voller Hauptclient).
Festlegung: Mobile spaeter lieber als echte App, nicht als dauerhafte Browser-Notloesung.
#### 6.11 Aktueller Stand vs. Zielarchitektur
#### 6.11 Praxis-zu-Praxis-Kopplung (VERBINDLICH, 2026-04-18)
**Was schon da ist (V1):**
- Benutzer-Sync via Backend (`empfang_users.json`)
- Thread-basierter Chat (thread_id, reply_to)
- Aufgaben-Panel (localStorage, user-scoped)
- 3-Panel-Layout im Browser-Empfang
- Ton-/Benachrichtigungssystem
- Empfangs-Desktop-Huelle
Interne Kurzbezeichnung: **AZA Praxis-Federation**
**Was noch fehlt fuer V2:**
- practice_id in allen Entitaeten
- Echte Authentifizierung (JWT/Session)
- Serverseitige Kanalstruktur
- Serverseitige Aufgaben (statt localStorage)
- Geraeteverwaltung fuer Praxis-Admin
- QR-Code-Kopplung fuer Mobile
**Grundprinzip:** Praxen sind standardmaessig vollstaendig getrennt.
Eine Verbindung entsteht NUR durch explizite beidseitige Zustimmung.
**Ablauf:**
1. Admin Praxis A erzeugt Verbindungseinladung (Einmal-Code, 48h gueltig)
2. Admin Praxis B gibt Code ein und bestaetigt
3. Serverseitig: `PracticeConnection`-Objekt wird angelegt
4. Definierte externe Kommunikationsraeume werden freigeschaltet
5. Jeder Admin kann die Verbindung jederzeit trennen
**Datenmodell `PracticeConnection`:**
| Feld | Typ | Beschreibung |
|---|---|---|
| `connection_id` | UUID | Eindeutige Verbindung |
| `practice_a_id` | UUID | Initiierende Praxis |
| `practice_b_id` | UUID | Annehmende Praxis |
| `status` | enum | pending / active / revoked |
| `created_by` | user_id | Admin der die Einladung erstellt hat |
| `accepted_by` | user_id | Admin der angenommen hat |
| `created_at` | timestamp | Erstellzeitpunkt |
| `accepted_at` | timestamp | Annahmezeitpunkt |
| `revoked_at` | timestamp | Trennzeitpunkt (optional) |
| `shared_channels` | list | Freigegebene Kanaltypen |
**Externe Kanalstruktur bei Kopplung:**
- Allgemeiner externer Kanal (Praxis A ↔ Praxis B)
- Optional: Aerzte ↔ Aerzte (nur Aerzte beider Praxen)
- Optional: Sekretariat ↔ Sekretariat
- Jeder externe Kanal wird vom jeweiligen Admin freigegeben
**Sicherheitsregeln:**
- Externe Nachrichten sind IMMER als extern markiert
- Externe Benutzer sehen NUR den freigegebenen Kanal, NICHT interne Daten
- Trennung sofort wirksam (kein Nachlauf)
- Verbindungshistorie im Audit-Log
#### 6.12 Admin-Benutzerverwaltung (VERBINDLICH, 2026-04-18)
**Benutzer-Objekt (Mindestfelder):**
| Feld | Typ | Beschreibung |
|---|---|---|
| `user_id` | UUID | Eindeutiger Benutzer |
| `practice_id` | UUID | Praxis-Zugehoerigkeit |
| `display_name` | string | Anzeigename |
| `email` | string | E-Mail (optional, fuer Passwort-Reset) |
| `role` | enum | admin / arzt / mpa / empfang |
| `status` | enum | active / deactivated / deleted |
| `pw_hash` | string | PBKDF2-SHA256 |
| `pw_salt` | string | Zufaelliger Salt |
| `created_by` | user_id | Wer hat den Account angelegt |
| `created_at` | timestamp | Erstellzeitpunkt |
| `last_login` | timestamp | Letzter Login |
| `deactivated_at` | timestamp | Deaktivierungszeitpunkt (optional) |
**Admin-Aktionen:**
- Benutzer anlegen (mit Name, Rolle, optionalem Passwort)
- Benutzer deaktivieren (Login gesperrt, Daten bleiben)
- Benutzer reaktivieren
- Benutzer endgueltig loeschen (DSGVO)
- Rolle aendern (arzt ↔ mpa ↔ empfang, Admin nur durch anderen Admin)
- Passwort zuruecksetzen (erzeugt temporaeres Passwort)
- Alle Sitzungen eines Benutzers beenden
**Nachvollziehbarkeit:**
- `created_by` zeigt wer den Benutzer angelegt hat
- Rollenaenderungen im Audit-Log
- Deaktivierung/Loeschung im Audit-Log
#### 6.13 Admin-Geraeteverwaltung (VERBINDLICH, 2026-04-18)
**Device-Objekt (Mindestfelder):**
| Feld | Typ | Beschreibung |
|---|---|---|
| `device_id` | UUID | Eindeutiges Geraet |
| `user_id` | UUID | Zugehoeriger Benutzer |
| `practice_id` | UUID | Zugehoerige Praxis |
| `device_name` | string | z.B. "Praxis-PC Empfang", "iPhone" |
| `platform` | string | Windows / macOS / iOS / Android / Browser |
| `device_type` | enum | desktop / browser / mobile / tablet |
| `user_agent` | string | Browser-/App-Kennung |
| `last_active` | timestamp | Letzter API-Zugriff |
| `first_seen` | timestamp | Erstmalige Registrierung |
| `trust_status` | enum | trusted / pending / blocked |
| `ip_last` | string | Letzte IP-Adresse |
**Admin sieht pro Benutzer:**
- Alle registrierten Geraete
- Typ (Desktop-Icon, Browser-Icon, Handy-Icon)
- Letzter Zugriff (relativ: "vor 2 Stunden", "vor 3 Tagen")
- Status-Badge (aktiv / verdaechtig / gesperrt)
**Admin-Aktionen:**
- Geraet als vertrauenswuerdig markieren
- Geraet sperren (sofort, alle Sitzungen werden beendet)
- Geraet loeschen (aus der Liste entfernen)
- Alle Sitzungen auf einem Geraet beenden
- Erneute Anmeldung erzwingen
**Automatische Erkennung:**
- Bei jedem Login: device_id wird aus User-Agent + fingerprint abgeleitet
(oder Cookie-basiert fuer Browser)
- Neues Geraet: Admin erhaelt optional Benachrichtigung
- Zu viele Geraete: Warnung an Admin
#### 6.14 UI-Sichtbarkeit nach Rolle
**Admin sieht:**
- Praxis-Einstellungen (Name, Einladungscode, Admin-E-Mail)
- Alle Benutzer mit Rollen + Status + letztem Login
- Alle Geraete aller Benutzer
- Gekoppelte Praxen + Verbindungsstatus
- Alle internen + externen Kanaele
- Audit-Log
#### 6.12 Umsetzungsphasen
**Arzt / MPA / Empfang sieht:**
- Eigene Praxis (Name)
- Eigene Kanaele (interner Allgemein + Rollen-Kanal + Direktchats)
- Eigene Aufgaben + an eigene Rolle zugewiesene Aufgaben
- Freigegebene externe Kanaele (falls Praxis-Kopplung aktiv)
- Eigene Geraete (nur eigene, nicht die anderer Benutzer)
**Phase 1 (kurzfristig aktuell):**
Frontend-Layout, Benutzer-Sync, Chat-Threads. Kein Backend-Umbau.
Einzelpraxis-Betrieb reicht. practice_id wird als Konzept vorbereitet,
aber noch nicht erzwungen.
**Externer Praxis-Kontakt sieht:**
- NUR den freigegebenen externen Kanal
- Keine internen Daten, Benutzer, Aufgaben der anderen Praxis
**Phase 2 (mittelfristig):**
Backend: practice_id + user_id + JWT-Auth einfuehren. Kanalstruktur serverseitig.
Aufgaben serverseitig. Geraeteverwaltung. Admin-Panel fuer Practice Admin.
Presence/Heartbeat. Invite-Links.
#### 6.15 Aktueller Stand (nach V4-Deploy, 2026-04-18)
**Phase 3 (spaeter):**
Multi-Tenant produktiv (mehrere Praxen). WebSocket statt Polling. Mobile-App.
QR-Code-Kopplung. 2FA. Verschluesselte Speicherung. Externe Praxis-Verbindungen.
**Was jetzt implementiert ist:**
- Serverseitige Auth: PBKDF2 + Session-Cookie (empfang_routes.py V4)
- practice_id in allen Nachrichten + Benutzern + Aufgaben
- Login / Setup / Register mit Einladungscode
- Rollen: admin, arzt, mpa, empfang
- Serverseitige Aufgaben (GET/POST/DELETE /tasks)
- Browser-Empfang mit Login-Overlay, Server-only-Daten
- Praxisname + Admin-E-Mail beim Setup
- Einladungscode kopieren + erneuern (Admin)
- 3-Panel-Layout (Sidebar, Chat, Aufgaben)
- Ton-/Benachrichtigungssystem
- Empfangs-Desktop-Huelle
- Caddy Rewrite (empfang.aza-medwork.ch ohne /empfang/)
**Was als naechstes fehlt:**
- Serverseitige Kanalstruktur (Allgemein, Aerzte, MPA statt flacher Thread-Liste)
- Device-Tracking (device_id bei jedem Login)
- Admin-Panel im Browser (Benutzer verwalten, Geraete sehen)
- Praxis-zu-Praxis-Kopplung (Federation)
- Audit-Log
- QR-Code-Kopplung fuer Mobile
#### 6.16 Umsetzungsphasen (aktualisiert 2026-04-18)
**Phase 1 (ERLEDIGT):**
Backend V4 mit Auth + practice_id + Sessions + Tasks.
Browser-Empfang mit Login, Server-only-Daten, 3-Panel-Layout.
Einzelpraxis-Betrieb produktiv moeglich.
**Phase 2 (naechster Hauptblock):**
- Serverseitige Kanalstruktur (Channel-Objekte im Backend)
- Device-Tracking bei jedem Login
- Admin-Panel im Browser: Benutzer verwalten, Geraete sehen
- Presence/Heartbeat (Online-Status)
- Hauptfenster `_send_to_empfang()` aufraumen (AO2/AO4/AO5/AO6)
**Phase 3 (mittelfristig):**
- Praxis-zu-Praxis-Federation
- WebSocket statt Polling
- Mobile-App (iPhone/Android)
- QR-Code-Kopplung
- 2FA fuer Admin
- Verschluesselte Nachrichtenspeicherung
**Phase 4 (spaeter):**
- Multi-Praxis produktiv (mehrere zahlende Praxen)
- Apple Watch / Wearable (nur Benachrichtigungen)
- Externe Praxis-Kanaele
- DSGVO-Loeschfunktionen
- Audit-Export
---