# 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 ` | | 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 ` - 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)