from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.auth import get_current_admin, get_current_user, get_password_hash from app.database import get_db from app.models.user import User, UserRole from app.schemas import AdminUserCreate, UserResponse, UserUpdate from app.services.user_cleanup import cleanup_user_files router = APIRouter(prefix="/api/users", tags=["users"]) def _count_admins(db: Session) -> int: return db.query(User).filter(User.role == UserRole.admin, User.is_active == True).count() @router.get("", response_model=list[UserResponse]) def list_users( db: Annotated[Session, Depends(get_db)], _: Annotated[User, Depends(get_current_admin)], ): return db.query(User).order_by(User.created_at).all() @router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def create_user( user_data: AdminUserCreate, db: Annotated[Session, Depends(get_db)], _: Annotated[User, Depends(get_current_admin)], ): from app.auth import get_user_by_email, get_user_by_username if get_user_by_email(db, user_data.email): raise HTTPException(status_code=400, detail="Email already registered") if get_user_by_username(db, user_data.username): raise HTTPException(status_code=400, detail="Username already taken") user = User( email=user_data.email, username=user_data.username, hashed_password=get_password_hash(user_data.password), role=user_data.role, is_active=True, ) db.add(user) db.commit() db.refresh(user) return user @router.patch("/{user_id}", response_model=UserResponse) def update_user( user_id: int, update: UserUpdate, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_admin)], ): from app.auth import get_user_by_email user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="User not found") if user.id == current_user.id: if update.role is not None and update.role != UserRole.admin: raise HTTPException(status_code=400, detail="Cannot demote yourself") if update.is_active is False: raise HTTPException(status_code=400, detail="Cannot deactivate yourself") if update.role is not None and update.role != UserRole.admin and user.role == UserRole.admin: if _count_admins(db) <= 1: raise HTTPException(status_code=400, detail="Cannot demote the last admin") if update.is_active is False and user.role == UserRole.admin: if _count_admins(db) <= 1: raise HTTPException(status_code=400, detail="Cannot deactivate the last admin") if update.email is not None and update.email != user.email: if get_user_by_email(db, update.email): raise HTTPException(status_code=400, detail="Email already registered") user.email = update.email if update.username is not None and update.username != user.username: from app.auth import get_user_by_username if get_user_by_username(db, update.username): raise HTTPException(status_code=400, detail="Username already taken") user.username = update.username if update.role is not None: user.role = update.role if update.is_active is not None: user.is_active = update.is_active if update.password: user.hashed_password = get_password_hash(update.password) db.commit() db.refresh(user) return user @router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_user( user_id: int, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_admin)], ): user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="User not found") if user.id == current_user.id: raise HTTPException(status_code=400, detail="Cannot delete yourself") if user.role == UserRole.admin and _count_admins(db) <= 1: raise HTTPException(status_code=400, detail="Cannot delete the last admin") cleanup_user_files(db, user) db.delete(user) db.commit()