"""User management router endpoints.""" from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update from typing import Optional from app.core.database import get_db from app.core.security import get_current_user, hash_password from app.models.models import User from app.schemas.schemas import UserUpdate, UserResponse router = APIRouter() @router.get("/{user_id}", response_model=UserResponse) async def get_user( user_id: int, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Get user by ID.""" stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) user = result.scalar_one_or_none() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) return UserResponse.model_validate(user) @router.patch("/{user_id}", response_model=UserResponse) async def update_user( user_id: int, request: UserUpdate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Update user profile.""" # Users can only update their own profile unless admin if int(current_user["user_id"]) != user_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Cannot update another user's profile", ) stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) user = result.scalar_one_or_none() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) # Update fields update_data = request.model_dump(exclude_unset=True) # Hash new password if provided if "new_password" in update_data: update_data["password_hash"] = hash_password(update_data.pop("new_password")) # Update user stmt = ( update(User) .where(User.id == user_id) .values(**update_data) ) await db.execute(stmt) await db.flush() # Fetch updated user stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) updated_user = result.scalar_one_or_none() return UserResponse.model_validate(updated_user) @router.post("/{user_id}/ban") async def ban_user( user_id: int, reason: str, expiration_time: Optional[str] = None, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Ban a user (admin only).""" # Check if current user is admin if current_user.get("privlevel") not in ["Admin", "Judge"]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Admin privileges required", ) stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) user = result.scalar_one_or_none() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) # Update user ban status from datetime import datetime ban_ends = None if expiration_time: ban_ends = datetime.fromisoformat(expiration_time) stmt = ( update(User) .where(User.id == user_id) .values( is_banned=True, ban_reason=reason, ban_ends=ban_ends, ) ) await db.execute(stmt) await db.flush() return {"message": f"User {user_id} has been banned"} @router.post("/{user_id}/unban") async def unban_user( user_id: int, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Unban a user (admin only).""" # Check if current user is admin if current_user.get("privlevel") not in ["Admin", "Judge"]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Admin privileges required", ) stmt = select(User).where(User.id == user_id) result = await db.execute(stmt) user = result.scalar_one_or_none() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) # Update user ban status stmt = ( update(User) .where(User.id == user_id) .values( is_banned=False, ban_reason=None, ban_ends=None, ) ) await db.execute(stmt) await db.flush() return {"message": f"User {user_id} has been unbanned"}