fix: implement proper avatar upload and display
- Add file upload endpoint to auth router - Mount static files for avatar serving - Implement real file upload in frontend - Add error handling and fallback for broken images - Fix avatar persistence and state management - Add loading states and proper error messages Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
"""RehearsalHub FastAPI application entry point."""
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
import os
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from rehearsalhub.config import get_settings
|
||||
from rehearsalhub.routers import (
|
||||
@@ -64,6 +66,11 @@ def create_app() -> FastAPI:
|
||||
async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
# Mount static files for avatar uploads
|
||||
upload_dir = "uploads/avatars"
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
app.mount("/api/static/avatars", StaticFiles(directory=upload_dir), name="avatars")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from rehearsalhub.db.engine import get_session
|
||||
from rehearsalhub.db.models import Member
|
||||
@@ -62,3 +64,44 @@ async def update_settings(
|
||||
else:
|
||||
member = current_member
|
||||
return MemberRead.from_model(member)
|
||||
|
||||
|
||||
@router.post("/me/avatar", response_model=MemberRead)
|
||||
async def upload_avatar(
|
||||
file: UploadFile = File(...),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
current_member: Member = Depends(get_current_member),
|
||||
):
|
||||
"""Upload and set user avatar image."""
|
||||
# Validate file type and size
|
||||
if not file.content_type.startswith("image/"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Only image files are allowed"
|
||||
)
|
||||
|
||||
# Create uploads directory if it doesn't exist
|
||||
upload_dir = "uploads/avatars"
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
|
||||
# Generate unique filename
|
||||
file_ext = file.filename.split(".")[-1] if "." in file.filename else "jpg"
|
||||
filename = f"{uuid.uuid4()}.{file_ext}"
|
||||
file_path = f"{upload_dir}/{filename}"
|
||||
|
||||
# Save file
|
||||
try:
|
||||
with open(file_path, "wb") as buffer:
|
||||
buffer.write(await file.read())
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to save avatar: {str(e)}"
|
||||
)
|
||||
|
||||
# Update member's avatar URL
|
||||
repo = MemberRepository(session)
|
||||
avatar_url = f"/api/static/avatars/{filename}"
|
||||
member = await repo.update(current_member, avatar_url=avatar_url)
|
||||
|
||||
return MemberRead.from_model(member)
|
||||
|
||||
Reference in New Issue
Block a user