Files
sdb2-backend/app/api/v1/admin/users.py

149 lines
4.4 KiB
Python

"""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()