136 lines
4.3 KiB
Python
136 lines
4.3 KiB
Python
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
|
from fastapi import status
|
|
from sqlalchemy.orm import Session as DBSession
|
|
from pathlib import Path
|
|
import shutil
|
|
from typing import Optional
|
|
from pydantic import BaseModel, EmailStr, field_validator
|
|
|
|
from app.core.database import SessionLocal
|
|
from app.core.auth import get_current_user
|
|
from app.models.user import User
|
|
|
|
router = APIRouter(prefix="/profile", tags=["profile"])
|
|
|
|
def get_db():
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
# -----------------------------
|
|
# 1) Neuer PIN anfordern (Mock)
|
|
# -----------------------------
|
|
@router.post("/request-new-pin", status_code=status.HTTP_202_ACCEPTED)
|
|
def request_new_pin(current_user: User = Depends(get_current_user)):
|
|
return {"status": "queued", "message": "PIN request queued (mock)"}
|
|
|
|
# -----------------------------
|
|
# 2) Profil-Avatar hochladen (persistiert avatar_url)
|
|
# -----------------------------
|
|
UPLOAD_DIR = Path("media/avatars")
|
|
|
|
@router.post("/avatar", status_code=status.HTTP_200_OK)
|
|
def upload_avatar(
|
|
file: UploadFile = File(...),
|
|
db: DBSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
filename = file.filename or "avatar"
|
|
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
|
if ext not in {"png", "jpg", "jpeg", "webp", "gif", ""}:
|
|
raise HTTPException(status_code=400, detail="Unsupported file type")
|
|
final_ext = ext if ext else "png"
|
|
|
|
dest = UPLOAD_DIR / f"{current_user.id}.{final_ext}"
|
|
with dest.open("wb") as out:
|
|
shutil.copyfileobj(file.file, out)
|
|
|
|
# User in diese Session laden
|
|
u = db.query(User).filter(User.id == current_user.id).first()
|
|
if not u:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
|
|
u.avatar_url = f"/media/avatars/{u.id}.{final_ext}"
|
|
db.commit()
|
|
db.refresh(u)
|
|
|
|
import time
|
|
return {
|
|
"status": "ok",
|
|
"avatar_url": f"{u.avatar_url}?t={int(time.time())}",
|
|
"path": str(dest),
|
|
}
|
|
|
|
# -----------------------------
|
|
# 3) Eigene Basisdaten ändern (inkl. PayPal + Passwort)
|
|
# -----------------------------
|
|
class ProfileUpdate(BaseModel):
|
|
alias: Optional[str] = None
|
|
paypal_email: Optional[EmailStr] = None
|
|
visible_in_stats: Optional[bool] = None
|
|
public_stats: Optional[bool] = None
|
|
|
|
# Passwortwechsel
|
|
current_password: Optional[str] = None
|
|
new_password: Optional[str] = None
|
|
|
|
@field_validator("new_password")
|
|
@classmethod
|
|
def _min_len_pw(cls, v):
|
|
if v is not None and len(v) < 8:
|
|
raise ValueError("Password too short (min. 8)")
|
|
return v
|
|
|
|
@router.put("/me", response_model=dict)
|
|
def update_profile(
|
|
payload: ProfileUpdate,
|
|
db: DBSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
u = db.query(User).filter(User.id == current_user.id).first()
|
|
if not u:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
|
|
changed = False
|
|
|
|
# Alias
|
|
if payload.alias is not None:
|
|
u.alias = payload.alias
|
|
changed = True
|
|
|
|
# PayPal-Mail
|
|
if payload.paypal_email is not None:
|
|
u.paypal_email = str(payload.paypal_email).lower()
|
|
changed = True
|
|
|
|
# Sichtbarkeit (Kompatibilität: beide Keys akzeptieren)
|
|
flag = payload.public_stats if payload.public_stats is not None else payload.visible_in_stats
|
|
if flag is not None:
|
|
u.public_stats = bool(flag)
|
|
changed = True
|
|
|
|
# Passwortwechsel nur mit current_password
|
|
if payload.new_password is not None:
|
|
from passlib.context import CryptContext
|
|
pwd = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
|
|
# Wenn ein Passwort gesetzt ist, muss current_password mitgeschickt werden
|
|
if getattr(u, "hashed_password", None):
|
|
if not payload.current_password:
|
|
raise HTTPException(status_code=400, detail="Current password required")
|
|
if not pwd.verify(payload.current_password, u.hashed_password):
|
|
raise HTTPException(status_code=400, detail="Current password invalid")
|
|
|
|
u.hashed_password = pwd.hash(payload.new_password)
|
|
changed = True
|
|
|
|
if changed:
|
|
db.commit()
|
|
db.refresh(u)
|
|
|
|
return {"status": "ok"}
|