import logging import os import uuid from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from sqlalchemy.orm import Session from app.core.config import settings from app.core.database import get_db from app.core.security import get_password_hash, verify_password from app.dependencies.auth import get_current_active_user, get_current_admin from app.models.user import User, UserRole from app.schemas.user import ( User as UserSchema, ) from app.schemas.user import ( UserPasswordChange, UserUpdate, UserWithAddress, ) router = APIRouter() logger = logging.getLogger(__name__) @router.get("/", response_model=list[UserSchema]) async def get_users( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin) ): """ Get all users (admin only). """ users = db.query(User).offset(skip).limit(limit).all() return users @router.get("/{user_id}", response_model=UserWithAddress) async def get_user( user_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Get a specific user by ID. Regular users can only get their own user details. Admin users can get any user details. """ if current_user.id != user_id and current_user.role != UserRole.ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions to access this user's data" ) user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) return user @router.put("/me", response_model=UserWithAddress) async def update_user_me( user_update: UserUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Update current user information. """ for key, value in user_update.dict(exclude_unset=True).items(): setattr(current_user, key, value) db.commit() db.refresh(current_user) logger.info(f"User {current_user.email} updated their profile") return current_user @router.post("/me/change-password", response_model=dict) async def change_password( password_data: UserPasswordChange, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Change current user's password. """ if not verify_password(password_data.current_password, current_user.hashed_password): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Incorrect current password" ) current_user.hashed_password = get_password_hash(password_data.new_password) db.commit() logger.info(f"User {current_user.email} changed their password") return {"message": "Password updated successfully"} @router.post("/me/profile-image", response_model=dict) async def upload_profile_image( file: UploadFile = File(...), db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Upload a profile image for the current user. """ # Validate the file content_type = file.content_type if not content_type.startswith("image/"): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="File must be an image" ) # Create user images directory if it doesn't exist user_images_dir = settings.USER_IMAGES_DIR user_images_dir.mkdir(parents=True, exist_ok=True) # Generate a unique filename file_extension = os.path.splitext(file.filename)[1] unique_filename = f"{uuid.uuid4()}{file_extension}" file_path = user_images_dir / unique_filename # Save the file with open(file_path, "wb") as buffer: buffer.write(await file.read()) # Update the user's profile image in the database relative_path = f"/storage/user_images/{unique_filename}" current_user.profile_image = relative_path db.commit() logger.info(f"User {current_user.email} uploaded a new profile image") return {"filename": unique_filename, "path": relative_path} @router.delete("/{user_id}", response_model=dict) async def delete_user( user_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """ Delete a user. Regular users can only delete their own account. Admin users can delete any user. """ if current_user.id != user_id and current_user.role != UserRole.ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions to delete this user" ) user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) # Soft delete the user by setting is_active to False # In a real-world application, you might want to consider: # 1. Hard deleting the user data for GDPR compliance # 2. Anonymizing the user data instead of deleting # 3. Setting up a scheduled task to actually delete inactive users after a certain period user.is_active = False db.commit() logger.info(f"User {user.email} was marked as inactive (deleted)") return {"message": "User successfully deleted"}