175 lines
6.1 KiB
Python
175 lines
6.1 KiB
Python
"""后台管理接口:任务/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),
|
||
} |