95 lines
3.3 KiB
Markdown
95 lines
3.3 KiB
Markdown
|
|
# 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
|