"""User management (admin only).""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import require_admin from app.core.security import hash_password from app.database import get_db from app.models.user import User from app.schemas import UserCreate, UserOut, UserUpdate from app.services.audit import record_audit router = APIRouter(prefix="/api/users", tags=["users"]) @router.get("", response_model=list[UserOut]) async def list_users(db: AsyncSession = Depends(get_db), _: User = Depends(require_admin)): res = await db.execute(select(User).order_by(User.id)) return res.scalars().all() @router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED) async def create_user( body: UserCreate, db: AsyncSession = Depends(get_db), admin: User = Depends(require_admin), ): exists = await db.execute(select(User).where(User.email == body.email)) if exists.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Email already registered") user = User( email=body.email, full_name=body.full_name, hashed_password=hash_password(body.password), role=body.role, ) db.add(user) await db.commit() await db.refresh(user) await record_audit(db, admin, "user.create", "user", user.id, {"email": user.email, "role": user.role.value}) return user @router.patch("/{user_id}", response_model=UserOut) async def update_user( user_id: int, body: UserUpdate, db: AsyncSession = Depends(get_db), admin: User = Depends(require_admin), ): res = await db.execute(select(User).where(User.id == user_id)) user = res.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") if body.full_name is not None: user.full_name = body.full_name if body.role is not None: user.role = body.role if body.is_active is not None: user.is_active = body.is_active if body.password: user.hashed_password = hash_password(body.password) await db.commit() await db.refresh(user) await record_audit(db, admin, "user.update", "user", user.id) return user @router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_user( user_id: int, db: AsyncSession = Depends(get_db), admin: User = Depends(require_admin), ): if user_id == admin.id: raise HTTPException(status_code=400, detail="Cannot delete yourself") res = await db.execute(select(User).where(User.id == user_id)) user = res.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") await db.delete(user) await db.commit() await record_audit(db, admin, "user.delete", "user", user_id)