Files
aza/AzA march 2026 - Kopie (14)/AZA_DETAILED_HANDOVER.md
2026-04-19 20:41:37 +02:00

18 KiB
Raw Blame History

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)