fix: 全量修复批量选择、JWT 60min、script-callback 认证与心跳数据流
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+17
-12
@@ -147,7 +147,7 @@ async def receive_heartbeat(
|
||||
1. Write heartbeat to Redis (frontend reads Redis for live status)
|
||||
2. Detect alerts (CPU/mem/disk > threshold) → WebSocket + Telegram push
|
||||
3. Detect recoveries (previously alerted metrics now normal) → push
|
||||
4. Also update MySQL via ServerService (for persistent record)
|
||||
4. MySQL history via heartbeat_flush (10min), not per-request writes
|
||||
"""
|
||||
server_id = payload.server_id
|
||||
|
||||
@@ -171,6 +171,14 @@ async def receive_heartbeat(
|
||||
if not await _verify_server_api_key(server_id, api_key, service):
|
||||
raise HTTPException(status_code=401, detail="Invalid API key for this server")
|
||||
|
||||
server = await service.get_server(server_id)
|
||||
if not server:
|
||||
logger.warning(
|
||||
"Heartbeat discarded: unknown server_id=%s (unregistered host)",
|
||||
server_id,
|
||||
)
|
||||
return {"status": "discarded", "reason": "unknown_server"}
|
||||
|
||||
# ── 1. Write to Redis (real-time data) ──
|
||||
|
||||
# Time drift detection: compare agent_time with central server time
|
||||
@@ -221,14 +229,8 @@ async def receive_heartbeat(
|
||||
logger.error(f"Redis heartbeat write failed for server {server_id}: {e}")
|
||||
# Redis write failed — still continue with MySQL update
|
||||
|
||||
# ── 1b. Look up server name for alert broadcasts ──
|
||||
server_name = f"server-{server_id}"
|
||||
try:
|
||||
server = await service.get_server(server_id)
|
||||
if server:
|
||||
server_name = server.name
|
||||
except Exception:
|
||||
logger.debug(f"Failed to look up server name for server {server_id}")
|
||||
# ── 1b. Server name for alert broadcasts ──
|
||||
server_name = server.name or f"server-{server_id}"
|
||||
|
||||
# ── 1c. Time drift Telegram alert (critical only, 5-min cooldown like metric alerts) ──
|
||||
if drift_level == "crit":
|
||||
@@ -284,8 +286,7 @@ async def receive_heartbeat(
|
||||
except Exception as e:
|
||||
logger.error(f"Recovery detection failed for server {server_id}: {e}")
|
||||
|
||||
# ── 4. Also update MySQL (persistent record for non-real-time queries) ──
|
||||
await service.update_heartbeat(server_id, payload.model_dump())
|
||||
# MySQL history is flushed every 10min by heartbeat_flush (Redis → MySQL).
|
||||
|
||||
return {"status": "ok", "server_id": server_id}
|
||||
|
||||
@@ -293,12 +294,16 @@ async def receive_heartbeat(
|
||||
async def script_job_callback(
|
||||
payload: AgentScriptCallback,
|
||||
request: Request,
|
||||
api_key: str = Depends(_verify_api_key),
|
||||
service: ServerService = Depends(get_server_service),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Receive long-task completion from child host (curl after nohup script exits).
|
||||
|
||||
Authenticated by one-time job_id + secret stored in Redis at exec time.
|
||||
Authenticated by X-API-Key (per-server or global) + one-time job_id + secret.
|
||||
"""
|
||||
if not await _verify_server_api_key(payload.server_id, api_key, service):
|
||||
raise HTTPException(status_code=401, detail="Invalid API key for this server")
|
||||
from server.application.services.script_job_callback import apply_script_job_callback
|
||||
from server.api.websocket import broadcast_system_event
|
||||
from server.infrastructure.database.script_repo import ScriptExecutionRepositoryImpl
|
||||
|
||||
Reference in New Issue
Block a user