# Backend-Architektur ## Struktur ``` backend/ ├── main.py # App-Setup + Router-Registration (~75 Zeilen) ├── db.py # PostgreSQL Connection Pool + get_cursor() Helper ├── auth.py # hash_pin · verify_pin · require_auth · require_admin ├── models.py # Alle Pydantic Models (LoginRequest, ProfileUpdate, etc.) ├── schema.sql # Vollständiges PostgreSQL-Schema └── routers/ ├── auth.py # Login, Logout, Password Reset, PIN (7 Endpoints) ├── profiles.py # Profile CRUD + Current User (7 Endpoints) ├── weight.py # Weight Tracking (5 Endpoints) ├── circumference.py # Body Measurements (4 Endpoints) ├── caliper.py # Skinfold Tracking (4 Endpoints) ├── activity.py # Workout Logging + CSV Import (6 Endpoints) ├── nutrition.py # Nutrition + FDDB Import (4 Endpoints) ├── photos.py # Progress Photos (3 Endpoints) ├── insights.py # AI Analysis + Pipeline (8 Endpoints) ├── prompts.py # AI Prompt Management (2 Endpoints) ├── admin.py # User Management (7 Endpoints) ├── stats.py # Dashboard Stats (1 Endpoint) ├── exportdata.py # CSV/JSON/ZIP Export (3 Endpoints) └── importdata.py # ZIP Import (1 Endpoint) ``` ## Router registrieren (main.py Pattern) ```python from routers import auth, profiles, weight app.include_router(auth.router, prefix="/api") app.include_router(profiles.router, prefix="/api") ``` ## Neuen Router anlegen 1. `backend/routers/mein_modul.py` erstellen 2. `router = APIRouter()` definieren 3. Endpoints mit `@router.get/post/put/delete` definieren 4. In `main.py` importieren und registrieren 5. Schema-Änderungen in `schema.sql` + Migration ## Datenbank-Zugriff ```python # ✅ Immer so: from db import get_db, get_cursor with get_db() as conn: cur = get_cursor(conn) # RealDictCursor – gibt Dicts zurück cur.execute("SELECT * FROM weight_log WHERE profile_id = %s", (pid,)) rows = cur.fetchall() # Liste von Dicts # ❌ Nie so (SQLite-Syntax): conn.execute("SELECT * FROM weight_log WHERE profile_id=?", (pid,)) ``` ## Auth-Pattern ```python # Standard-Endpoint mit Auth: @router.get("/mein-endpoint") def mein_endpoint(session: dict = Depends(require_auth)): pid = session['profile_id'] role = session['role'] # Admin-only: @router.get("/admin-endpoint") def admin_endpoint(session: dict = Depends(require_admin)): pass # ❌ NIEMALS so (session innerhalb Header eingebettet): def endpoint(x: str = Header(default=None, session=Depends(require_auth))): ``` ## PostgreSQL vs. SQLite Unterschiede ```python # Platzhalter: %s # PostgreSQL ✅ ? # SQLite ❌ # Boolean: WHERE active = true # PostgreSQL ✅ WHERE active = 1 # SQLite ❌ # Returning: INSERT INTO ... RETURNING id # PostgreSQL ✅ (für neue IDs) # JSON: JSONB # PostgreSQL (für flexible Felder) ``` ## DB-Schema ändern 1. `backend/schema.sql` anpassen 2. Migration schreiben (ALTER TABLE oder neues Skript) 3. Container neu bauen: `docker compose build --no-cache backend` 4. NIEMALS Spalten direkt löschen/umbenennen ohne Datenmigration