""" web/routes/users.py — User CRUD (superadmin only). GET /users → list all users POST /users → create a new user PATCH /users/{id} → update role / password / active flag DELETE /users/{id} → deactivate (cannot delete self or other superadmins) """ from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi.responses import RedirectResponse from web import db from web.dependencies import require_role from web.models import CreateUserRequest, UpdateUserRequest router = APIRouter() def _templates(request: Request): return request.app.state.templates @router.get("/users") async def list_users(request: Request, user=Depends(require_role("superadmin"))): users = db.list_users() return _templates(request).TemplateResponse( request, "users.html", {"user": dict(user), "users": [dict(u) for u in users]}, ) @router.post("/users") async def create_user( payload: CreateUserRequest, _user=Depends(require_role("superadmin")), ): try: user_id = db.create_user(payload.username, payload.password, payload.role) except Exception as e: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) return {"id": user_id} @router.patch("/users/{user_id}") async def update_user( user_id: str, payload: UpdateUserRequest, acting_user=Depends(require_role("superadmin")), ): target = db.get_user_by_id(user_id) if target is None: raise HTTPException(status_code=404, detail="User not found") # Cannot demote another superadmin via role patch if target["role"] == "superadmin" and payload.role and payload.role != "superadmin": raise HTTPException(status_code=403, detail="Cannot demote another superadmin") updates: dict = {} if payload.password is not None: updates["password"] = payload.password if payload.role is not None: updates["role"] = payload.role if payload.is_active is not None: updates["is_active"] = 1 if payload.is_active else 0 db.update_user(user_id, **updates) return {"status": "ok"} @router.delete("/users/{user_id}") async def deactivate_user( user_id: str, acting_user=Depends(require_role("superadmin")), ): if user_id == acting_user["id"]: raise HTTPException(status_code=403, detail="Cannot deactivate yourself") target = db.get_user_by_id(user_id) if target is None: raise HTTPException(status_code=404, detail="User not found") db.deactivate_user(user_id) return {"status": "ok"}