Align the project baseline with the latest admin interface styling and layout structure while documenting setup and usage updates in README.
60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile
|
|
|
|
from app.core.dependencies import get_current_user
|
|
from app.core.media_signing import build_signed_media_url
|
|
from app.core.redis import check_rate_limit
|
|
from app.modules.media.service import AvatarValidationError
|
|
from app.modules.users.schemas import PasswordChangeIn, UserPatchIn
|
|
from app.modules.users.service import change_password, get_me, update_me, upload_avatar
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _profile_response(data: dict) -> dict:
|
|
return {
|
|
"user": {
|
|
"id": data["user"].id,
|
|
"email": data["user"].email,
|
|
"role": data["user"].role,
|
|
"status": data["user"].status,
|
|
},
|
|
"profile": {
|
|
"display_name": data["profile"].display_name,
|
|
"avatar_url": build_signed_media_url(data["profile"].avatar_url),
|
|
},
|
|
}
|
|
|
|
|
|
@router.get("/me")
|
|
async def me_route(current_user=Depends(get_current_user)):
|
|
return _profile_response(get_me(current_user))
|
|
|
|
|
|
@router.patch("/me")
|
|
async def patch_me_route(payload: UserPatchIn, current_user=Depends(get_current_user)):
|
|
return _profile_response(update_me(current_user, payload.display_name))
|
|
|
|
|
|
@router.post("/me/password")
|
|
async def change_password_route(payload: PasswordChangeIn, current_user=Depends(get_current_user)):
|
|
try:
|
|
change_password(current_user, payload.current_password, payload.new_password)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="INVALID_CURRENT_PASSWORD")
|
|
return {"message": "password_changed"}
|
|
|
|
|
|
@router.post("/me/avatar")
|
|
async def upload_avatar_route(
|
|
request: Request,
|
|
file: UploadFile = File(...),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
check_rate_limit(f"avatar:{current_user.id}", limit=10, window_seconds=3600)
|
|
content = await file.read()
|
|
try:
|
|
data = upload_avatar(current_user, content)
|
|
except AvatarValidationError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
return _profile_response(data)
|