"""Admin users endpoints.""" from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from sqlmodel.ext.asyncio.session import AsyncSession from app.core.database import get_db from app.core.dependencies import get_admin_user from app.models.plan import Plan from app.models.user import User from app.repositories.plan import PlanRepository from app.repositories.user import UserRepository from app.schemas.auth import UserResponse from app.schemas.user import UserUpdate router = APIRouter( prefix="/users", tags=["admin-users"], dependencies=[Depends(get_admin_user)], ) def _user_to_response(user: User) -> UserResponse: """Convert User model to UserResponse.""" return UserResponse( id=user.id, email=user.email, name=user.name, picture=user.picture, role=user.role, credits=user.credits, is_active=user.is_active, plan={ "id": user.plan.id, "name": user.plan.name, "max_credits": user.plan.max_credits, "features": [], # Add features if needed } if user.plan else {}, created_at=user.created_at, updated_at=user.updated_at, ) @router.get("/") async def list_users( session: Annotated[AsyncSession, Depends(get_db)], limit: int = 100, offset: int = 0, ) -> list[UserResponse]: """Get all users (admin only).""" user_repo = UserRepository(session) users = await user_repo.get_all_with_plan(limit=limit, offset=offset) return [_user_to_response(user) for user in users] @router.get("/{user_id}") async def get_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> UserResponse: """Get a specific user by ID (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) return _user_to_response(user) @router.patch("/{user_id}") async def update_user( user_id: int, user_update: UserUpdate, session: Annotated[AsyncSession, Depends(get_db)], ) -> UserResponse: """Update a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) update_data = user_update.model_dump(exclude_unset=True) # If plan_id is being updated, validate it exists if "plan_id" in update_data: plan_repo = PlanRepository(session) plan = await plan_repo.get_by_id(update_data["plan_id"]) if not plan: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found", ) updated_user = await user_repo.update(user, update_data) # Need to refresh the plan relationship after update await session.refresh(updated_user, ["plan"]) return _user_to_response(updated_user) @router.post("/{user_id}/disable") async def disable_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> dict[str, str]: """Disable a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) await user_repo.update(user, {"is_active": False}) return {"message": "User disabled successfully"} @router.post("/{user_id}/enable") async def enable_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> dict[str, str]: """Enable a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) await user_repo.update(user, {"is_active": True}) return {"message": "User enabled successfully"} @router.get("/plans/list") async def list_plans( session: Annotated[AsyncSession, Depends(get_db)], ) -> list[Plan]: """Get all plans for user editing (admin only).""" plan_repo = PlanRepository(session) return await plan_repo.get_all()