ADK-gateway/backend/app/api/admin.py
2026-08-05 23:24:57 +08:00

175 lines
6.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""后台管理接口:任务/Agent/日志/手动管控。"""
from typing import Optional
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Request
from app.constants import TaskStatus
from app.models.schemas import AgentInfo, LogEntry, TaskInfo
from app.repository.log_repo import LogRepo
from app.constants import AgentStatus
from app.services.agent_service import AgentService
from app.services.security import require_admin
from app.services.task_service import TaskService
router = APIRouter()
def require_admin_dep(x_admin_auth: str = Header(..., alias="X-Admin-Auth")) -> None:
"""后台管理接口鉴权:校验 X-Admin-Auth 头与 ADMIN_AUTH 一致。"""
require_admin(x_admin_auth)
def get_task_service(request: Request) -> TaskService:
return TaskService(request.app.state.redis)
def get_agent_service(request: Request) -> AgentService:
return AgentService(request.app.state.redis)
def get_log_repo(request: Request) -> LogRepo:
return LogRepo(request.app.state.redis)
# ---------- 任务管理 ----------
@router.get("/tasks", response_model=list[TaskInfo], summary="任务列表")
async def list_tasks(
status: Optional[TaskStatus] = None,
limit: int = 100,
svc: TaskService = Depends(get_task_service),
_admin: None = Depends(require_admin_dep),
):
return await svc.list(status=status, limit=limit)
@router.get("/tasks/{request_id}", response_model=TaskInfo, summary="任务详情")
async def task_detail(
request_id: str,
svc: TaskService = Depends(get_task_service),
_admin: None = Depends(require_admin_dep),
):
task = await svc.get(request_id)
if not task:
raise HTTPException(status_code=404, detail="task not found")
return task
@router.post("/tasks/{request_id}/cancel", summary="取消任务")
async def admin_cancel(
request_id: str,
svc: TaskService = Depends(get_task_service),
_admin: None = Depends(require_admin_dep),
):
if not await svc.cancel(request_id):
raise HTTPException(status_code=400, detail="cannot cancel task")
return {"ok": True}
@router.post("/tasks/{request_id}/reset", summary="重置任务状态")
async def admin_reset(
request_id: str,
svc: TaskService = Depends(get_task_service),
_admin: None = Depends(require_admin_dep),
):
if not await svc.reset(request_id):
raise HTTPException(status_code=400, detail="cannot reset task")
return {"ok": True}
# ---------- Agent 管理 ----------
@router.get("/agents", response_model=list[AgentInfo], summary="Agent 列表")
async def list_agents(
svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
return await svc.all()
@router.post("/agents/{agent_id}/unavailable", summary="置 Agent 不可用")
async def agent_unavailable(
agent_id: str,
svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
if not await svc.set_unavailable(agent_id):
raise HTTPException(status_code=404, detail="agent not found")
return {"ok": True}
@router.post("/agents/{agent_id}/available", summary="置 Agent 可用")
async def agent_available(
agent_id: str,
svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
if not await svc.set_available(agent_id):
raise HTTPException(status_code=404, detail="agent not found")
return {"ok": True}
@router.delete("/agents/{agent_id}", summary="删除 Agent无心跳的僵尸 Agent")
async def agent_delete(
agent_id: str,
svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
"""删除 Agent 池中的 Agent。
用于清理无心跳的僵尸 Agent。若 Agent 进程仍存活,删除后其心跳/注册会
触发重新注册,自动重新连接回池中。
"""
if not await svc.unregister(agent_id):
raise HTTPException(status_code=404, detail="agent not found")
return {"ok": True}
@router.post("/agents/{agent_id}/priority", response_model=AgentInfo, summary="设置 Agent 优先级")
async def agent_priority(
agent_id: str,
priority: int = Body(..., ge=1, le=5, description="优先级 1-5越小越先分配"),
svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
agent = await svc.set_priority(agent_id, priority)
if not agent:
raise HTTPException(status_code=404, detail="agent not found")
return agent
# ---------- 日志审计 ----------
@router.get("/logs", response_model=list[LogEntry], summary="日志审计")
async def logs(
limit: int = 200,
request_id: Optional[str] = None,
agent_id: Optional[str] = None,
repo: LogRepo = Depends(get_log_repo),
_admin: None = Depends(require_admin_dep),
):
return await repo.list(limit=limit, request_id=request_id, agent_id=agent_id)
# ---------- 概览 ----------
@router.get("/overview", summary="概览统计")
async def overview(
task_svc: TaskService = Depends(get_task_service),
agent_svc: AgentService = Depends(get_agent_service),
_admin: None = Depends(require_admin_dep),
):
tasks = await task_svc.list(limit=1000)
agents = await agent_svc.all()
from collections import Counter
status_count = Counter(t.status.value for t in tasks)
return {
"task_total": len(tasks),
"task_pending": status_count.get(TaskStatus.PENDING.value, 0),
"task_running": status_count.get(TaskStatus.RUNNING.value, 0),
"task_success": status_count.get(TaskStatus.SUCCESS.value, 0),
"task_failed": status_count.get(TaskStatus.FAILED.value, 0),
"agent_total": len(agents),
"agent_ready": sum(1 for a in agents if a.status == AgentStatus.READY),
"agent_processing": sum(1 for a in agents if a.status == AgentStatus.PROCESSING),
"agent_stopping": sum(1 for a in agents if a.status == AgentStatus.STOPPING),
"agent_offline": sum(1 for a in agents if a.status == AgentStatus.OFFLINE),
"agent_unavailable": sum(1 for a in agents if a.status == AgentStatus.UNAVAILABLE),
}