fastapi_hf / routes /Admin_Users.py
looh2's picture
Add endpoint to clear cached multi-agent responses in Redis
20e3813
Raw
History Blame Contribute Delete
3.45 kB
from typing import Optional
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
from pydantic import BaseModel
from .agents.clients import get_redis_client
from .auth import _service_client, invalidate_status_cache, verify_token
router = APIRouter(prefix="/admin", tags=["Admin"])
# Prefix used by the multi-agent response cache (_cache_key in
# agents_orchestration.py builds keys as "mac:<route>:<scope>:<hash>").
_PROMPT_CACHE_PREFIX = "mac:"
_ALLOWED_STATUSES = {"pending", "approved", "rejected"}
def require_admin(token_user_id: str = Depends(verify_token)) -> str:
"""Only users whose profiles.role is 'admin' may manage approvals."""
try:
res = _service_client().table("profiles").select("role").eq("id", token_user_id).execute()
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
role = res.data[0].get("role") if res.data else None
if role != "admin":
raise HTTPException(status_code=403, detail="Admin access required")
return token_user_id
@router.get("/users")
async def list_users(
status: Optional[str] = Query(default=None),
_admin_id: str = Depends(require_admin),
):
if status is not None and status not in _ALLOWED_STATUSES:
raise HTTPException(status_code=422, detail=f"status must be one of {sorted(_ALLOWED_STATUSES)}")
try:
query = (
_service_client()
.table("profiles")
.select("id, email, full_name, role, status, created_at")
.order("created_at", desc=True)
)
if status is not None:
query = query.eq("status", status)
res = query.execute()
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
return {"data": res.data or []}
class StatusUpdate(BaseModel):
status: str
@router.post("/users/{user_id}/status")
async def set_user_status(
user_id: str = Path(...),
payload: StatusUpdate = Body(...),
admin_id: str = Depends(require_admin),
):
if payload.status not in ("approved", "rejected"):
raise HTTPException(status_code=422, detail="status must be 'approved' or 'rejected'")
if user_id == admin_id:
raise HTTPException(status_code=400, detail="Admins cannot change their own status")
try:
res = (
_service_client()
.table("profiles")
.update({"status": payload.status})
.eq("id", user_id)
.execute()
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if not res.data:
raise HTTPException(status_code=404, detail="User not found")
invalidate_status_cache(user_id)
return {"data": res.data[0]}
@router.post("/cache/clear-prompts")
async def clear_prompt_cache(_admin_id: str = Depends(require_admin)):
"""Delete every cached multi-agent response (the "mac:*" keys in Redis).
These are the prompt/response cache entries built by the chat pipeline; they
are regenerated on demand, so clearing them just forces fresh answers.
"""
try:
redis = get_redis_client()
keys = [key async for key in redis.scan_iter(match=f"{_PROMPT_CACHE_PREFIX}*", count=500)]
deleted = await redis.delete(*keys) if keys else 0
except Exception as e:
raise HTTPException(status_code=502, detail=f"Failed to clear cache: {e}")
return {"deleted": deleted}