- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren - DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues - .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08) - Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben - docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md - CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht Made-with: Cursor
3.3 KiB
3.3 KiB
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)
from routers import auth, profiles, weight
app.include_router(auth.router, prefix="/api")
app.include_router(profiles.router, prefix="/api")
Neuen Router anlegen
backend/routers/mein_modul.pyerstellenrouter = APIRouter()definieren- Endpoints mit
@router.get/post/put/deletedefinieren - In
main.pyimportieren und registrieren - Schema-Änderungen in
schema.sql+ Migration
Datenbank-Zugriff
# ✅ 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
# 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
# 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
backend/schema.sqlanpassen- Migration schreiben (ALTER TABLE oder neues Skript)
- Container neu bauen:
docker compose build --no-cache backend - NIEMALS Spalten direkt löschen/umbenennen ohne Datenmigration