108 lines
4.4 KiB
Python
108 lines
4.4 KiB
Python
|
|
"""
|
|||
|
|
Profile Management Endpoints for Mitai Jinkendo
|
|||
|
|
|
|||
|
|
Handles profile CRUD operations for both admin and current user.
|
|||
|
|
"""
|
|||
|
|
import uuid
|
|||
|
|
from typing import Optional
|
|||
|
|
from datetime import datetime
|
|||
|
|
|
|||
|
|
from fastapi import APIRouter, HTTPException, Header, Depends
|
|||
|
|
|
|||
|
|
from db import get_db, get_cursor, r2d
|
|||
|
|
from auth import require_auth
|
|||
|
|
from models import ProfileCreate, ProfileUpdate
|
|||
|
|
|
|||
|
|
router = APIRouter(prefix="/api", tags=["profiles"])
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Helper ────────────────────────────────────────────────────────────────────
|
|||
|
|
def get_pid(x_profile_id: Optional[str] = Header(default=None)) -> str:
|
|||
|
|
"""Get profile_id - from header for legacy endpoints."""
|
|||
|
|
if x_profile_id:
|
|||
|
|
return x_profile_id
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("SELECT id FROM profiles ORDER BY created LIMIT 1")
|
|||
|
|
row = cur.fetchone()
|
|||
|
|
if row: return row['id']
|
|||
|
|
raise HTTPException(400, "Kein Profil gefunden")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Admin Profile Management ──────────────────────────────────────────────────
|
|||
|
|
@router.get("/profiles")
|
|||
|
|
def list_profiles(session=Depends(require_auth)):
|
|||
|
|
"""List all profiles (admin)."""
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("SELECT * FROM profiles ORDER BY created")
|
|||
|
|
rows = cur.fetchall()
|
|||
|
|
return [r2d(r) for r in rows]
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/profiles")
|
|||
|
|
def create_profile(p: ProfileCreate, session=Depends(require_auth)):
|
|||
|
|
"""Create new profile (admin)."""
|
|||
|
|
pid = str(uuid.uuid4())
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("""INSERT INTO profiles (id,name,avatar_color,sex,dob,height,goal_weight,goal_bf_pct,created,updated)
|
|||
|
|
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)""",
|
|||
|
|
(pid,p.name,p.avatar_color,p.sex,p.dob,p.height,p.goal_weight,p.goal_bf_pct))
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("SELECT * FROM profiles WHERE id=%s", (pid,))
|
|||
|
|
return r2d(cur.fetchone())
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.get("/profiles/{pid}")
|
|||
|
|
def get_profile(pid: str, session=Depends(require_auth)):
|
|||
|
|
"""Get profile by ID."""
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("SELECT * FROM profiles WHERE id=%s", (pid,))
|
|||
|
|
row = cur.fetchone()
|
|||
|
|
if not row: raise HTTPException(404, "Profil nicht gefunden")
|
|||
|
|
return r2d(row)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.put("/profiles/{pid}")
|
|||
|
|
def update_profile(pid: str, p: ProfileUpdate, session=Depends(require_auth)):
|
|||
|
|
"""Update profile by ID (admin)."""
|
|||
|
|
with get_db() as conn:
|
|||
|
|
data = {k:v for k,v in p.model_dump().items() if v is not None}
|
|||
|
|
data['updated'] = datetime.now().isoformat()
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute(f"UPDATE profiles SET {', '.join(f'{k}=%s' for k in data)} WHERE id=%s",
|
|||
|
|
list(data.values())+[pid])
|
|||
|
|
return get_profile(pid, session)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.delete("/profiles/{pid}")
|
|||
|
|
def delete_profile(pid: str, session=Depends(require_auth)):
|
|||
|
|
"""Delete profile (admin)."""
|
|||
|
|
with get_db() as conn:
|
|||
|
|
cur = get_cursor(conn)
|
|||
|
|
cur.execute("SELECT COUNT(*) as count FROM profiles")
|
|||
|
|
count = cur.fetchone()['count']
|
|||
|
|
if count <= 1: raise HTTPException(400, "Letztes Profil kann nicht gelöscht werden")
|
|||
|
|
for table in ['weight_log','circumference_log','caliper_log','nutrition_log','activity_log','ai_insights']:
|
|||
|
|
cur.execute(f"DELETE FROM {table} WHERE profile_id=%s", (pid,))
|
|||
|
|
cur.execute("DELETE FROM profiles WHERE id=%s", (pid,))
|
|||
|
|
return {"ok": True}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Current User Profile ──────────────────────────────────────────────────────
|
|||
|
|
@router.get("/profile")
|
|||
|
|
def get_active_profile(x_profile_id: Optional[str] = Header(default=None), session: dict = Depends(require_auth)):
|
|||
|
|
"""Legacy endpoint – returns active profile."""
|
|||
|
|
pid = get_pid(x_profile_id)
|
|||
|
|
return get_profile(pid, session)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.put("/profile")
|
|||
|
|
def update_active_profile(p: ProfileUpdate, x_profile_id: Optional[str] = Header(default=None), session: dict = Depends(require_auth)):
|
|||
|
|
"""Update current user's profile."""
|
|||
|
|
pid = get_pid(x_profile_id)
|
|||
|
|
return update_profile(pid, p, session)
|