Files
aza/AzA march 2026 - Kopie (12)/AZA_DETAILED_HANDOVER.md

431 lines
18 KiB
Markdown
Raw Normal View History

2026-04-16 13:32:32 +02:00
# AZA Detaillierte Projektuebergabe / Handover
**Stand: 2026-04-06**
**Zweck:** Diese Datei ist die verbindliche Referenz fuer jeden neuen Chat. Zuerst lesen, dann arbeiten.
---
## 1. Projektziel / Aktueller Fokus
**AZA** (AZA Medical AI Assistant / AZA Desktop) ist eine medizinische KI-Desktop-Anwendung fuer Windows. Sie unterstuetzt Aerzte bei Diktat, Textverarbeitung, medizinischer Recherche und Dokumentation.
**Architektur (Variante B verbindlich seit 2026-03-25):**
- Desktop-App (Python/Tkinter) kommuniziert mit eigenem AZA-Backend auf Hetzner
- Backend leitet KI-Anfragen serverseitig an OpenAI weiter
- Kein OpenAI-Key beim Kunden noetig
- Kein OpenAI-Key in der Desktop-App
**Aktueller Schwerpunkt:**
Produktiver Kundenfluss mit Lizenzschluessel. Die klare Zielsequenz ist:
```
Kauf → Lizenzschluessel per E-Mail → Download → Installation → Aktivierung → Nutzung
```
---
## 2. Aktueller stabiler technischer Stand
### 2.1 Lizenzschluessel-Flow (PRODUKTIV, Stand 2026-04-06)
| Aspekt | Status |
|---|---|
| `/license/activate` | Funktioniert produktiv auf Hetzner |
| `/license/status` | Funktioniert produktiv auf Hetzner |
| Lizenzschluessel-Erzeugung | Automatisch beim Kauf (Format: `AZA-XXXX-XXXX-XXXX-XXXX`) |
| Lizenzschluessel in DB | Gespeichert in `licenses`-Tabelle, Spalte `license_key` |
| Desktop-Aktivierung | Lizenzschluessel kann in der Desktop-App eingegeben und aktiviert werden |
| `/license/status?license_key=...` | Liefert `valid: true` fuer aktive Lizenzen |
| Device-Enforcement | Aktiv und funktioniert korrekt |
| Success-Seite `/billing/success` | Zeigt dem Kunden den Lizenzschluessel nach Kauf an |
| Produktiver Test | Erfolgreich mit aktivem Lizenzschluessel |
**Wichtig:** Device-Bindings waren zeitweise ein Blocker. Fuer den Testdatensatz (`admin@aza-medwork.ch`) wurden bestehende Device-Bindings aus der DB geloescht, damit der erneute Aktivierungstest funktionierte. Bei neuen Kunden tritt dieses Problem nicht auf.
### 2.2 Mailversand (PRODUKTIV ueber Resend, Stand 2026-04-06)
| Aspekt | Status |
|---|---|
| Produktiver Versandkanal | **Resend HTTP API** |
| Resend-Domain | `mail.aza-medwork.ch` (DNS bei Hostpoint verifiziert) |
| Absender / MAIL_FROM | `AZA MedWork <noreply@mail.aza-medwork.ch>` |
| Test-Endpunkt | `POST /stripe/test_license_email?email=...` (Admin-geschuetzt) |
| Letzter erfolgreicher Test | `{"sent": true, "to": "admin@aza-medwork.ch"}` |
| E-Mail Zustellung | Produktiv bestaetigt Mail kommt an |
### 2.3 Stripe / Billing (PRODUKTIV)
| Aspekt | Status |
|---|---|
| Stripe-Modus | Live (sk_live_) |
| Webhook-Endpunkt | `https://api.aza-medwork.ch/stripe/webhook` |
| Echter Live-Kauf | CHF 59 Basic Monthly erfolgreich durchgefuehrt |
| Lizenz-Lifecycle | Kauf → active → Storno/Refund → canceled → Desktop Testmodus (bewiesen) |
| Webhook-Events | `checkout.session.completed`, `customer.subscription.updated`, `customer.subscription.deleted` |
### 2.4 Desktop-App
| Aspekt | Status |
|---|---|
| Lizenzpruefung | Ueber Backend (`/license/status`) mit Lizenzschluessel |
| Vollmodus | Wenn Backend `valid: true` liefert |
| Testmodus | Wenn keine gueltige Lizenz oder Backend nicht erreichbar |
| Lokales Aktivierungs-Gate | Bei Remote-Backend uebersprungen (Root Cause 14 behoben) |
| Update-Checker | Prueft `https://api.aza-medwork.ch/download/version.json` beim Start |
| Aktuelle Version | `APP_VERSION = "1.0.0"`, `APP_CHANNEL = "stable"` |
### 2.5 Admin Control Panel (PRODUKTIV)
8 interne Admin-Endpunkte, geschuetzt via `X-Admin-Token` / `AZA_ADMIN_TOKEN`:
**v1:**
- `GET /admin/system_status` App-Health, Uptime, Disk, Stripe-Config, DB-Info
- `GET /admin/licenses_overview` Lizenzen nach Status, letzte 20, `?email=` Filter
- `GET /admin/backup_status` Backup-Pfade, Groesse, neustes Backup, Log-Tail
- `GET /admin/billing_overview` Stripe-Health, Lizenz-Summary, Event-Log
**v2:**
- `GET /admin/license_customer_map` Detaillierte Lizenznehmer-Uebersicht, `?email=` und `?status=` Filter
- `GET /admin/revenue_overview` MRR, Stripe-Live-Daten (gross/refunds/net), recent_charges, recent_refunds
- `GET /admin/alerts` Strukturierte Warnliste (info/warning/critical)
- `GET /admin/dashboard_summary` Sammel-Endpunkt fuer alle Kennzahlen
### 2.6 Backup / Storage
- Taegliches Backup-Skript: `/root/aza-backups/backup_aza.sh` (Cronjob)
- Backup-Pfad: `/root/aza-backups/daily/`
- In Container gemountet als `/host_backups` (read-only)
- Ca. 137 GB frei, ca. 4-5% belegt kein Speicherdruck
---
## 3. Mailversand-Historie / Root Causes / Finaler Weg
### Chronologie
1. **Erster Versuch: Hostpoint-SMTP**
- Hostpoint-Mailbox `noreply@aza-medwork.ch` wurde angelegt
- SMTP-Server: `asmtp.mail.hostpoint.ch`
- Port 465 (SSL) und 587 (STARTTLS) getestet
- SMTP-Daten wurden mehrfach geprueft und waren korrekt
2. **Beobachtete Fehler (SMTP von Hetzner/Container):**
- Erste Tests: Auth-Fehler (falscher Host `mail.hostpoint.ch` statt `asmtp.mail.hostpoint.ch`)
- Nach Host-Korrektur: `OSError: [Errno 101] Network is unreachable`
- Port 465 → Timeout / Network unreachable
- Port 587 → Timeout / Network unreachable
- Ursache: Hetzner-Container kann Hostpoint-SMTP-Server nicht erreichen (Netzwerk-/Firewallsperre)
3. **Schlussfolgerung:**
- Hostpoint-SMTP ist von Hetzner aus nicht nutzbar
- Das ist ein Infrastruktur-/Netzwerkproblem, kein Code-Problem
- Hostpoint-SMTP ist **nicht** der produktive Versandweg
4. **Loesung: Umstellung auf Resend HTTP API**
- Resend-Account erstellt
- Domain `mail.aza-medwork.ch` bei Resend registriert und via DNS bei Hostpoint verifiziert
- `MAIL_FROM` gesetzt auf `AZA MedWork <noreply@mail.aza-medwork.ch>`
- Code in `stripe_routes.py` umgebaut: `_send_via_resend()` als primaerer Kanal
- **Wichtiger Fix:** Resend-HTTP-API erfordert `User-Agent` Header (ohne → Error 1010/403)
- Fix angewandt: `"User-Agent": "AZA-MedWork/1.0"` im Request
5. **Finaler erfolgreicher Test:**
```
POST /stripe/test_license_email?email=admin@aza-medwork.ch
→ {"sent": true, "to": "admin@aza-medwork.ch"}
```
E-Mail kam produktiv an.
### Aktueller Zustand der Mailfunktion in `stripe_routes.py`
```
send_license_email(to_email, license_key)
├── RESEND_API_KEY gesetzt? → _send_via_resend() [PRODUKTIVER WEG]
└── sonst → _send_via_smtp() [INAKTIVER FALLBACK]
```
- SMTP-Code ist noch vorhanden als Fallback
- SMTP-Variablen in `.env` sind Altlast, nicht produktiv aktiv
- Wenn `RESEND_API_KEY` gesetzt ist (und das ist es), wird immer Resend benutzt
---
## 4. Wichtige Pfade / Betriebsorte / Operator-Wissen
### Lokaler Windows-Rechner
| Was | Pfad |
|---|---|
| Projektordner | `C:\Users\surov\Documents\AZA_GIT\aza\AzA march 2026` |
| Desktop-App direkt starten | `python basis14.py` (im Projektordner) |
| Build-EXE | `.\build_exe.ps1` |
| Build-Installer | `.\build_installer.ps1` |
| Kompletter Release | `.\ship_release.ps1` |
| Nur Upload | `.\publish_update.ps1` |
| Installer-Artefakt | `dist\installer\aza_desktop_setup.exe` |
| Release-Manifest | `release\version.json` |
| Versionsquelle | `aza_version.py` (`APP_VERSION`, `APP_CHANNEL`) |
**Wichtig:** NICHT ueber lokale Starter starten (`start_all.bat`, `RUN_AZA_ONECLICK.bat`, `START_AZA.bat`, `start_backend_autoport.bat`) diese setzen Env-Variablen auf localhost und ueberschreiben die Live-Backend-URL.
### Hetzner-Server (SSH)
| Was | Pfad / Befehl |
|---|---|
| SSH-Zugang | `ssh root@178.104.51.177` |
| Repo-Root | `/root/aza-app` |
| Docker-Compose-Ordner | `/root/aza-app/deploy` |
| `.env`-Datei | `/root/aza-app/deploy/.env` |
| Rebuild (immer im deploy-Ordner!) | `cd /root/aza-app/deploy && docker compose down && docker compose up -d --build` |
| Container-Logs | `docker logs aza-api --tail 100` |
| ENV im Container pruefen | `docker exec aza-api env \| grep VARIABLE` |
| Backup-Ordner | `/root/aza-backups/daily/` |
**WICHTIG:** `docker compose` Befehle muessen IMMER im Ordner `/root/aza-app/deploy` ausgefuehrt werden, nicht im Repo-Root `/root/aza-app`.
### Hostpoint (Website / DNS)
| Was | Detail |
|---|---|
| Haupt-Website | Hostpoint bleibt fuer Website, Marketing, WooCommerce |
| DNS-Verwaltung | Bei Hostpoint (fuer `aza-medwork.ch` und Subdomains) |
| Mailboxen | Hostpoint verwaltet Mailboxen (z.B. `noreply@aza-medwork.ch`) |
| Resend-DNS | `mail.aza-medwork.ch` DNS-Records fuer Resend bei Hostpoint gesetzt |
### Produktive URLs
| URL | Zweck |
|---|---|
| `https://api.aza-medwork.ch` | Backend-API |
| `https://api.aza-medwork.ch/health` | Health-Check |
| `https://api.aza-medwork.ch/stripe/webhook` | Stripe-Webhook |
| `https://api.aza-medwork.ch/download/version.json` | Update-Manifest |
| `https://api.aza-medwork.ch/download/aza_desktop_setup.exe` | Installer-Download |
| `https://api.aza-medwork.ch/billing/success` | Kauf-Erfolgsseite |
---
## 5. Wichtige ENV / Konfiguration
### Aktive produktive ENV-Variablen (auf Hetzner in `/root/aza-app/deploy/.env`)
| Variable | Rolle | Status |
|---|---|---|
| `RESEND_API_KEY` | Resend API Credential fuer Mailversand | **AKTIV PRODUKTIV** |
| `MAIL_FROM` | Absender fuer Lizenzschluessel-Mail | **AKTIV PRODUKTIV** |
| `STRIPE_SECRET_KEY` | Stripe Live API Key (sk_live_...) | **AKTIV PRODUKTIV** |
| `STRIPE_WEBHOOK_SECRET` | Stripe Webhook Signing Secret | **AKTIV PRODUKTIV** |
| `AZA_ADMIN_TOKEN` | Token fuer Admin-Endpunkte | **AKTIV PRODUKTIV** |
| `MEDWORK_API_TOKENS` | API-Token fuer Desktop-Backend-Kommunikation | **AKTIV PRODUKTIV** |
| `OPENAI_API_KEY` | OpenAI API Key (serverseitig) | **AKTIV PRODUKTIV** |
| `AZA_DOMAIN` | `api.aza-medwork.ch` | **AKTIV PRODUKTIV** |
| `ACME_EMAIL` | `info@aza-medwork.ch` (fuer Caddy/HTTPS) | **AKTIV PRODUKTIV** |
### Inaktive / historische ENV-Variablen
| Variable | Rolle | Status |
|---|---|---|
| `SMTP_HOST` | Hostpoint SMTP Server | **INAKTIV** Fallback, wird nicht genutzt |
| `SMTP_PORT` | Hostpoint SMTP Port | **INAKTIV** Fallback |
| `SMTP_USER` | Hostpoint SMTP User | **INAKTIV** Fallback |
| `SMTP_PASS` | Hostpoint SMTP Passwort | **INAKTIV** Fallback |
| `SMTP_FROM` | Hostpoint SMTP Absender | **INAKTIV** Fallback |
**Keine Rueckkehr zu Hostpoint-SMTP noetig, solange Resend stabil laeuft.**
---
## 6. Naechster Hauptblock: End-to-End-Kundentest
**Ziel:** Den kompletten Kundenfluss ohne Basteln, ohne manuelle DB-Eingriffe und ohne Operator-Hilfe beweisen.
### Zielbild fuer den Kundenfluss
```
1. Kunde kauft ueber Stripe Payment Link / Checkout
2. Stripe Webhook verarbeitet den Kauf
3. Backend erzeugt Lizenzschluessel und speichert ihn in der DB
4. Resend sendet automatisch E-Mail mit Lizenzschluessel an den Kunden
5. Success-Seite zeigt dem Kunden ebenfalls den Lizenzschluessel
6. Kunde laedt AZA ueber offiziellen Download-Link herunter
7. Kunde installiert AZA
8. Kunde gibt Lizenzschluessel in der Desktop-App ein
9. Desktop aktiviert gegen Backend (/license/activate)
10. Desktop startet im Vollmodus
```
### Was dabei noch geprueft / sichergestellt werden muss
- [ ] E-Mail mit Lizenzschluessel kommt automatisch beim Kauf an (nicht nur via Test-Endpunkt)
- [ ] Download-Link in der E-Mail ist korrekt und funktioniert
- [ ] Installer laesst sich sauber installieren
- [ ] Erststart ohne vorherige Konfiguration funktioniert
- [ ] Lizenzschluessel-Eingabe in der App funktioniert auf Anhieb
- [ ] Vollmodus wird sofort nach Aktivierung erreicht
---
## 7. Download-/Installer-Entscheidung
**Fuer den naechsten Kundenfluss-Test:**
- Download soll ueber die offizielle Website / Download-Seite priorisiert werden
- Nicht zuerst einen rohen Direktlink als Hauptweg verwenden
- Die E-Mail soll idealerweise einen klaren Download-Link enthalten
- Der Kundentest soll moeglichst realistisch am echten Kundenablauf orientiert sein
**Zielbild:**
Mail mit Lizenzschluessel + klarem Download-Link → Download → Installation → Aktivierung
**Aktuell verfuegbarer Direktlink:**
`https://api.aza-medwork.ch/download/aza_desktop_setup.exe`
---
## 8. Offene Restpunkte
| Punkt | Prioritaet | Blocker? |
|---|---|---|
| Mailtext und Success-Seite inhaltlich polieren | Niedrig | Nein |
| Admin-Token rotieren (wurde im Chat offengelegt) | Mittel | Nein, aber vor echtem Kundenbetrieb empfohlen |
| SMTP-Reste in `.env` aufraeumen | Niedrig | Nein (inaktiv) |
| Resend-Setup / Domain-Policy weiter polieren | Niedrig | Nein |
| Device-Bindings-Management fuer Mehrgeraete klarer machen | Mittel | Nein |
| translate.py, aza_email.py, diktat_app.py auf Backend-Chat migrieren | Niedrig | Nein (Nebenpfade) |
| WooCommerce / Website-Kaufpfad professionalisieren | Mittel | Spaeterer Block |
| Browser-AZA / Web-App | Niedrig | Spaeterer Block |
---
## 9. Arbeitsstil / Nutzerpraeferenzen
**Diese Regeln gelten fuer ALLE zukuenftigen Chats:**
### Allgemeine Regeln
- Nutzer bastelt nicht alle Aenderungen kommen als fertige, vollstaendige Dateien (ready-to-paste)
- Nutzer fuehrt nur vorgegebene Commands aus, keine manuellen Edits
- Jede Aenderung in 1 Patch, kein schrittweises Anleiten
- Keine risky Refactors immer minimal und sicher
- Root-cause-first bei jedem Problem
- Keine Monsterpatches
- Keine Rueckfragen-Orgien
- Keine Variantenflut genau 1 Weg, der beste
### Operator-Schritte
- **Immer** explizit angeben, WO ein Schritt auszufuehren ist:
- `[Windows PowerShell]` lokaler Rechner, Projektordner
- `[Hetzner SSH]` `ssh root@178.104.51.177`
- `[Browser]` URL angeben
- `[Composer/IDE]` Cursor Editor
- **Immer** exakte Copy-Paste-Befehle liefern
- **Immer** mit Pfad oder Ort starten
- Nicht schreiben, was der Nutzer NICHT tun soll, sondern nur den naechsten exakten Schritt
- Keine vagen Formulierungen wie "send this" oder "do something like"
- Nutzer will moeglichst wenig manuelle Improvisation
### Uebergaben
- Uebergaben fuer naechste Chats sollen ausfuehrlich sein, nicht minimal
- Wichtige Root Causes immer dokumentieren
- Geloeste Probleme klar als geloest markieren
- Nicht bei alten Problemen wieder anfangen
---
## 10. Empfohlener Chat-Start fuer den naechsten Chat
### Sinnvolle naechste Hauptbloecke (nach Prioritaet)
1. **End-to-End-Kundentest** (EMPFOHLEN als naechstes)
- Kontrollierter Kauf → automatische E-Mail → Download → Installation → Aktivierung → Vollmodus
- Beweisen, dass der gesamte Fluss ohne Basteln funktioniert
2. **Download-Seite / Website-Kaufpfad**
- Offizielle Download-Seite auf der Website einrichten
- Klaren Kundenweg von Website → Kauf → Download definieren
3. **Admin-Token-Rotation + Secrets-Hygiene**
- Offengelegten Admin-Token rotieren
- Sicherstellen, dass keine Secrets im Repo liegen
### Beste Empfehlung
**Starte mit Block 1: End-to-End-Kundentest.**
### Erster konkreter Operator-Schritt
```
[Browser]
Stripe Payment Link oeffnen und kontrollierten Testkauf mit einer
frischen E-Mail-Adresse durchfuehren (NICHT admin@aza-medwork.ch,
sondern eine neue Adresse, um den Neukundenfall zu simulieren).
```
Danach pruefen:
1. `[Hetzner SSH]` `/stripe/license_debug?email=NEUE_EMAIL` → aktive Lizenz?
2. `[E-Mail-Postfach]` Lizenzschluessel-Mail angekommen?
3. `[Browser]` Installer herunterladen ueber Link aus der Mail
4. `[Windows]` Installer ausfuehren, App starten, Lizenzschluessel eingeben
5. `[Desktop-App]` Vollmodus bestaetigen
---
## 11. Geloeste Root Causes (Referenz)
| RC | Problem | Loesung | Datei | Datum |
|---|---|---|---|---|
| RC14 | Desktop zeigte Testversion trotz aktiver Remote-Lizenz | `_has_remote_backend()` Bypass fuer lokales Aktivierungs-Gate | `basis14.py` | 2026-03-30 |
| RC15 | `current_period_end` war null fuer aktive Lizenz | Fallback auf `items.data[0].current_period_end` im Webhook | `stripe_routes.py` | 2026-03-30 |
| RC16 | revenue_overview zu grob fuer Betreiber | `recent_charges` und `recent_refunds` ergaenzt | `admin_routes.py` | 2026-03-31 |
| RC17 | SMTP von Hetzner → Hostpoint nicht erreichbar | Umstellung auf Resend HTTP API | `stripe_routes.py` | 2026-04-06 |
| RC18 | Resend-API lehnte Request ab (Error 1010/403) | `User-Agent: AZA-MedWork/1.0` Header ergaenzt | `stripe_routes.py` | 2026-04-06 |
---
## 12. Wichtige Dateien im Projekt
### Backend (auf Hetzner unter `/root/aza-app/`)
| Datei | Rolle |
|---|---|
| `backend_main.py` | FastAPI-Hauptanwendung, mountet alle Router |
| `stripe_routes.py` | Stripe-Webhook, Lizenz-DB, Mailversand, Lizenzschluessel-Erzeugung |
| `admin_routes.py` | Admin Control Panel v1+v2 Endpunkte |
| `aza_license_logic.py` | `compute_license_decision()` Lizenzgueltigkeit berechnen |
| `aza_device_enforcement.py` | Device-Bindings verwalten |
| `aza_security.py` | `require_api_token`, `require_admin_token` |
### Desktop (lokal)
| Datei | Rolle |
|---|---|
| `basis14.py` | Haupt-Desktop-App (8900+ Zeilen), UI, Lizenzcheck, Aktivierung |
| `aza_version.py` | `APP_VERSION`, `APP_CHANNEL` zentrale Versionsquelle |
| `aza_style.py` | UI-Styling-Konstanten |
| `desktop_update_check.py` | Update-Checker beim App-Start |
| `aza_desktop.spec` | PyInstaller-Spezifikation |
### Build / Release (lokal)
| Datei | Rolle |
|---|---|
| `build_exe.ps1` | Baut EXE mit PyInstaller |
| `build_installer.ps1` | Baut Installer mit Inno Setup |
| `build_release_manifest.ps1` | Erzeugt `release/version.json` |
| `release.ps1` | Lokaler Release-Prozess (Build + Verify) |
| `publish_update.ps1` | Upload nach Hetzner |
| `ship_release.ps1` | **Verbindlicher Ein-Knopf-Release** (Build + Upload) |
---
## Abschluss
**Wenn der naechste Chat startet:**
1. Zuerst diese Datei lesen
2. Nicht wieder bei alten SMTP-/Deploy-/Pfadfehlern beginnen
3. Hostpoint-SMTP ist nicht der produktive Weg Resend funktioniert
4. Lizenzschluessel-Flow ist produktiv nicht neu bauen
5. Admin-Endpunkte sind produktiv nicht neu bauen
6. Direkt beim naechsten sinnvollen Block weitermachen (siehe Abschnitt 10)