d
This commit is contained in:
@@ -4,6 +4,7 @@ from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.core.config import settings
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.detect_job_service import (
|
||||
append_detect_job_event,
|
||||
@@ -12,6 +13,7 @@ from app.services.detect_job_service import (
|
||||
get_detect_job_summary,
|
||||
get_detect_queue_health,
|
||||
list_detect_jobs,
|
||||
normalize_detect_step_code,
|
||||
)
|
||||
from app.services.detect_service import get_detect_status
|
||||
from app.services.detect_run_service import create_detect_run_snapshot, finalize_detect_run, mark_detect_run_stopping
|
||||
@@ -22,6 +24,13 @@ from app.services.worker_control_service import send_worker_command, start_worke
|
||||
router = APIRouter(tags=["detect"])
|
||||
|
||||
|
||||
def _local_worker_expected_on_this_node() -> bool:
|
||||
return not (
|
||||
str(settings.node_region or "").strip() == "overseas"
|
||||
and str(settings.node_role or "").strip() == "control"
|
||||
)
|
||||
|
||||
|
||||
def _build_detect_action_result(
|
||||
*,
|
||||
action: str,
|
||||
@@ -255,6 +264,11 @@ def detect_queue_summary(window_minutes: int = 15) -> ApiResponse:
|
||||
return ApiResponse(data=get_detect_queue_health(window_minutes=window_minutes))
|
||||
|
||||
|
||||
@router.get("/detect/queue-health", response_model=ApiResponse)
|
||||
def detect_queue_health_alias(window_minutes: int = 15) -> ApiResponse:
|
||||
return ApiResponse(data=get_detect_queue_health(window_minutes=window_minutes))
|
||||
|
||||
|
||||
@router.get("/detect/jobs/{job_id}", response_model=ApiResponse)
|
||||
def detect_job_detail(job_id: int) -> ApiResponse:
|
||||
data = get_detect_job_summary(job_id, event_limit=100)
|
||||
@@ -264,8 +278,17 @@ def detect_job_detail(job_id: int) -> ApiResponse:
|
||||
|
||||
|
||||
@router.post("/detect/start", response_model=ApiResponse)
|
||||
def start_detect() -> ApiResponse:
|
||||
job_summary = create_detect_job_if_needed(limit=1000, created_by="api")
|
||||
def start_detect(step_code: str | None = None) -> ApiResponse:
|
||||
normalized_step_code = normalize_detect_step_code(step_code)
|
||||
if step_code and not normalized_step_code:
|
||||
result = _build_detect_action_result(
|
||||
action="start",
|
||||
ok=False,
|
||||
message=f"暂不支持步骤任务: {step_code}",
|
||||
data={"job": None, "step_code": str(step_code or "").strip()},
|
||||
)
|
||||
return ApiResponse(code=1, message=f"暂不支持步骤任务: {step_code}", data=result)
|
||||
job_summary = create_detect_job_if_needed(limit=10000, created_by="api", step_code=step_code)
|
||||
if not job_summary:
|
||||
result = _build_detect_action_result(
|
||||
action="start",
|
||||
@@ -292,42 +315,58 @@ def start_detect() -> ApiResponse:
|
||||
},
|
||||
)
|
||||
|
||||
ok, message = start_worker()
|
||||
if not ok:
|
||||
result = _build_detect_action_result(
|
||||
action="start",
|
||||
ok=False,
|
||||
message=message,
|
||||
data={"job": job_summary},
|
||||
local_worker_expected = _local_worker_expected_on_this_node()
|
||||
if local_worker_expected:
|
||||
ok, message = start_worker()
|
||||
if not ok:
|
||||
result = _build_detect_action_result(
|
||||
action="start",
|
||||
ok=False,
|
||||
message=message,
|
||||
data={"job": job_summary},
|
||||
)
|
||||
append_detect_job_event(
|
||||
job_summary["job_id"],
|
||||
event_type="job_dispatch_failed",
|
||||
level="error",
|
||||
message=f"启动 Worker 失败: {message}",
|
||||
payload={"cycle_token": cycle_token},
|
||||
)
|
||||
return ApiResponse(
|
||||
code=1,
|
||||
message=message,
|
||||
data=result,
|
||||
)
|
||||
|
||||
command_ok, command_message = send_worker_command(
|
||||
"start_detection",
|
||||
payload={
|
||||
"cycle_token": cycle_token,
|
||||
"job_id": job_summary["job_id"],
|
||||
"job_code": job_summary["job_code"],
|
||||
"task_mode": job_summary.get("task_mode", ""),
|
||||
"step_code": job_summary.get("step_code", ""),
|
||||
},
|
||||
)
|
||||
append_detect_job_event(
|
||||
job_summary["job_id"],
|
||||
event_type="job_dispatch_failed",
|
||||
level="error",
|
||||
message=f"启动 Worker 失败: {message}",
|
||||
event_type="job_dispatch_sent" if command_ok else "job_dispatch_rejected",
|
||||
level="info" if command_ok else "error",
|
||||
message=command_message,
|
||||
payload={"cycle_token": cycle_token},
|
||||
)
|
||||
return ApiResponse(
|
||||
code=1,
|
||||
message=message,
|
||||
data=result,
|
||||
else:
|
||||
ok = True
|
||||
message = "当前节点为海外控制面,仅负责派单,不启动本机 Worker"
|
||||
command_ok = True
|
||||
command_message = "已跳过本机 Worker 启动,转为仅向大陆执行节点派发检测动作"
|
||||
append_detect_job_event(
|
||||
job_summary["job_id"],
|
||||
event_type="job_dispatch_skipped_local",
|
||||
level="info",
|
||||
message=command_message,
|
||||
payload={"cycle_token": cycle_token},
|
||||
)
|
||||
|
||||
command_ok, command_message = send_worker_command(
|
||||
"start_detection",
|
||||
payload={
|
||||
"cycle_token": cycle_token,
|
||||
"job_id": job_summary["job_id"],
|
||||
"job_code": job_summary["job_code"],
|
||||
},
|
||||
)
|
||||
append_detect_job_event(
|
||||
job_summary["job_id"],
|
||||
event_type="job_dispatch_sent" if command_ok else "job_dispatch_rejected",
|
||||
level="info" if command_ok else "error",
|
||||
message=command_message,
|
||||
payload={"cycle_token": cycle_token},
|
||||
)
|
||||
snapshot = get_detect_status()
|
||||
settings_payload = get_settings_payload()
|
||||
settings_summary = _build_settings_summary(settings_payload)
|
||||
|
||||
@@ -9,6 +9,7 @@ from app.services.ops_agent_service import (
|
||||
agent_heartbeat,
|
||||
agent_mark_job_started,
|
||||
agent_pull_jobs,
|
||||
agent_pull_runtime_config,
|
||||
agent_register,
|
||||
build_node_agent_bootstrap_plan,
|
||||
issue_node_agent_token,
|
||||
@@ -72,6 +73,12 @@ def ops_agent_pull(payload: dict, limit: int = 1, x_domaincheck_agent_token: Opt
|
||||
return _build_agent_response(ok, message, data)
|
||||
|
||||
|
||||
@router.post("/ops/agent/runtime-config", response_model=ApiResponse)
|
||||
def ops_agent_runtime_config(payload: dict, x_domaincheck_agent_token: Optional[str] = Header(default=None)) -> ApiResponse:
|
||||
ok, message, data = agent_pull_runtime_config(payload, token=_resolve_agent_token(x_domaincheck_agent_token))
|
||||
return _build_agent_response(ok, message, data)
|
||||
|
||||
|
||||
@router.post("/ops/agent/jobs/{job_id}/start", response_model=ApiResponse)
|
||||
def ops_agent_job_start(job_id: int, payload: dict, x_domaincheck_agent_token: Optional[str] = Header(default=None)) -> ApiResponse:
|
||||
ok, message, data = agent_mark_job_started(job_id, payload, token=_resolve_agent_token(x_domaincheck_agent_token))
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Header
|
||||
from fastapi import APIRouter, Body, Header
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.build_info_service import get_runtime_build_info
|
||||
from app.services.cluster_runtime_service import get_cluster_snapshot
|
||||
from app.services.debug_event_service import get_debug_diagnosis, get_debug_event_overview, get_debug_handoff_report, ingest_debug_event, list_debug_events
|
||||
from app.services.detect_job_service import get_detect_queue_health
|
||||
from app.services.runtime_control_service import runtime_action
|
||||
from app.services.runtime_status_service import get_runtime_preflight, get_runtime_status
|
||||
from app.services.sync_push_service import (
|
||||
@@ -103,6 +104,36 @@ def runtime_debug_handoff(window_minutes: int = 10, source_region: Optional[str]
|
||||
)
|
||||
|
||||
|
||||
@router.get("/runtime/queue-health", response_model=ApiResponse)
|
||||
def runtime_queue_health(window_minutes: int = 15) -> ApiResponse:
|
||||
return ApiResponse(data=get_detect_queue_health(window_minutes=window_minutes))
|
||||
|
||||
|
||||
@router.get("/runtime/health-handover", response_model=ApiResponse)
|
||||
def runtime_health_handover(
|
||||
node_code: Optional[str] = None,
|
||||
window_minutes: int = 10,
|
||||
source_region: Optional[str] = None,
|
||||
) -> ApiResponse:
|
||||
sync_summary = get_sync_summary()
|
||||
runtime_status_payload = get_runtime_status()
|
||||
data = get_debug_handoff_report(
|
||||
window_minutes=window_minutes,
|
||||
source_region=source_region,
|
||||
sync_summary=sync_summary,
|
||||
readiness=runtime_status_payload.get("readiness") or {},
|
||||
)
|
||||
normalized_node_code = str(node_code or "").strip()
|
||||
if normalized_node_code:
|
||||
data = dict(data)
|
||||
data["nodes"] = [
|
||||
item
|
||||
for item in list(data.get("nodes") or [])
|
||||
if str(item.get("node_code") or "").strip() == normalized_node_code
|
||||
]
|
||||
return ApiResponse(data=data)
|
||||
|
||||
|
||||
@router.post("/runtime/sync-ingest", response_model=ApiResponse)
|
||||
def runtime_sync_ingest(payload: dict, x_domaincheck_sync_token: Optional[str] = Header(default=None)) -> ApiResponse:
|
||||
ok, message, data = ingest_runtime_projection(payload, shared_token=x_domaincheck_sync_token)
|
||||
@@ -110,7 +141,7 @@ def runtime_sync_ingest(payload: dict, x_domaincheck_sync_token: Optional[str] =
|
||||
|
||||
|
||||
@router.get("/runtime/task-export", response_model=ApiResponse)
|
||||
def runtime_task_export(limit: int = 200, x_domaincheck_sync_token: Optional[str] = Header(default=None)) -> ApiResponse:
|
||||
def runtime_task_export(limit: int = 1000, x_domaincheck_sync_token: Optional[str] = Header(default=None)) -> ApiResponse:
|
||||
ok, message, data = export_detect_task_projection(limit=limit, shared_token=x_domaincheck_sync_token)
|
||||
return ApiResponse(code=0 if ok else 1, message=message, data=data)
|
||||
|
||||
@@ -128,6 +159,6 @@ def runtime_debug_ingest(payload: dict, x_domaincheck_sync_token: Optional[str]
|
||||
|
||||
|
||||
@router.post("/runtime/actions/{action}", response_model=ApiResponse)
|
||||
def runtime_action_trigger(action: str) -> ApiResponse:
|
||||
ok, message, data = runtime_action(action)
|
||||
def runtime_action_trigger(action: str, payload: Optional[dict] = Body(default=None)) -> ApiResponse:
|
||||
ok, message, data = runtime_action(action, payload=payload)
|
||||
return ApiResponse(code=0 if ok else 1, message=message, data=data)
|
||||
|
||||
Reference in New Issue
Block a user