from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File from sqlmodel import Session, select from datetime import timedelta import os import uuid from pathlib import Path from app.database import get_session from app.models import User from app.schemas import UserCreate, UserResponse, UserLogin, Token from app.auth import get_password_hash, authenticate_user, create_access_token, get_current_user from app.config import get_settings router = APIRouter(prefix="/auth", tags=["Authentication"]) settings = get_settings() @router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def register(user_data: UserCreate, session: Session = Depends(get_session)): """Register a new user""" # Check if username already exists statement = select(User).where(User.username == user_data.username) existing_user = session.exec(statement).first() if existing_user: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Username already registered" ) # Check if email already exists statement = select(User).where(User.email == user_data.email) existing_email = session.exec(statement).first() if existing_email: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered" ) # Create new user hashed_password = get_password_hash(user_data.password) new_user = User( username=user_data.username, email=user_data.email, full_name=user_data.full_name, hashed_password=hashed_password ) session.add(new_user) session.commit() session.refresh(new_user) return new_user @router.post("/login", response_model=Token) def login(login_data: UserLogin, session: Session = Depends(get_session)): """Login and get access token""" user = authenticate_user(session, login_data.username, login_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=settings.access_token_expire_minutes) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} @router.get("/me", response_model=UserResponse) def get_current_user_info(current_user: User = Depends(get_current_user)): """Get current user information""" return current_user @router.put("/me", response_model=UserResponse) def update_profile( profile_data: dict, current_user: User = Depends(get_current_user), session: Session = Depends(get_session) ): """Update current user profile""" # Check if email is being changed and if it already exists if "email" in profile_data and profile_data["email"] != current_user.email: statement = select(User).where(User.email == profile_data["email"]) existing_email = session.exec(statement).first() if existing_email: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered" ) current_user.email = profile_data["email"] # Update full_name if provided if "full_name" in profile_data: current_user.full_name = profile_data["full_name"] # Update password if provided if "password" in profile_data and profile_data["password"]: current_user.hashed_password = get_password_hash(profile_data["password"]) # Update profile_picture if provided if "profile_picture" in profile_data: current_user.profile_picture = profile_data["profile_picture"] # Update theme if provided if "theme" in profile_data: if profile_data["theme"] in ["light", "dark"]: current_user.theme = profile_data["theme"] session.add(current_user) session.commit() session.refresh(current_user) return current_user @router.post("/me/profile-picture", response_model=UserResponse) async def upload_profile_picture( file: UploadFile = File(...), current_user: User = Depends(get_current_user), session: Session = Depends(get_session) ): """Upload profile picture""" # Validate file type if not file.content_type or 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 = Path("uploads/profile_pictures") upload_dir.mkdir(parents=True, exist_ok=True) # Generate unique filename file_extension = os.path.splitext(file.filename)[1] unique_filename = f"{uuid.uuid4()}{file_extension}" file_path = upload_dir / unique_filename # Save file with open(file_path, "wb") as buffer: content = await file.read() buffer.write(content) # Delete old profile picture if exists if current_user.profile_picture: old_file_path = Path(current_user.profile_picture) if old_file_path.exists(): old_file_path.unlink() # Update user profile picture path current_user.profile_picture = str(file_path) session.add(current_user) session.commit() session.refresh(current_user) return current_user