from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.core.deps import require_roles from app.core.security import hash_password from app.models.models import User from app.schemas.user import UserCreate, UserOut, UserUpdate from app.services.audit import write_audit_log router = APIRouter() @router.get("", response_model=list[UserOut]) async def list_users(admin: User = Depends(require_roles("admin")), db: AsyncSession = Depends(get_db)) -> list[UserOut]: users = (await db.scalars(select(User).order_by(User.id.asc()))).all() _ = admin return [UserOut.model_validate(user) for user in users] @router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED) async def create_user(payload: UserCreate, admin: User = Depends(require_roles("admin")), db: AsyncSession = Depends(get_db)) -> UserOut: exists = await db.scalar(select(User).where(User.email == payload.email)) if exists: raise HTTPException(status_code=409, detail="Email already exists") user = User(email=payload.email, password_hash=hash_password(payload.password), role=payload.role) db.add(user) await db.commit() await db.refresh(user) await write_audit_log(db, "admin.user.create", admin.id, {"created_user_id": user.id}) return UserOut.model_validate(user) @router.put("/{user_id}", response_model=UserOut) async def update_user( user_id: int, payload: UserUpdate, admin: User = Depends(require_roles("admin")), db: AsyncSession = Depends(get_db), ) -> UserOut: user = await db.scalar(select(User).where(User.id == user_id)) if not user: raise HTTPException(status_code=404, detail="User not found") update_data = payload.model_dump(exclude_unset=True) if "password" in update_data and update_data["password"]: user.password_hash = hash_password(update_data.pop("password")) for key, value in update_data.items(): setattr(user, key, value) await db.commit() await db.refresh(user) await write_audit_log(db, "admin.user.update", admin.id, {"updated_user_id": user.id}) return UserOut.model_validate(user) @router.delete("/{user_id}") async def delete_user(user_id: int, admin: User = Depends(require_roles("admin")), db: AsyncSession = Depends(get_db)) -> dict: if user_id == admin.id: raise HTTPException(status_code=400, detail="Cannot delete yourself") user = await db.scalar(select(User).where(User.id == user_id)) if not user: raise HTTPException(status_code=404, detail="User not found") await db.delete(user) await db.commit() await write_audit_log(db, "admin.user.delete", admin.id, {"deleted_user_id": user_id}) return {"status": "deleted"}