Files

81 lines
2.5 KiB
Markdown
Raw Permalink Normal View History

# Changelog — 长任务回调与 Redis 执行态
**日期**: 2026-05-22
**类型**: 功能
**上级索引**: [2026-05-22-script-execution-platform.md](./2026-05-22-script-execution-platform.md)
**SSOT**: [docs/project/script-execution.md](../project/script-execution.md)
## 变更摘要
1. 长任务:`nohup` + 子机结束 `curl``POST /api/agent/script-callback`
2. 执行热数据 RedisTTL 2h),MySQL 仅 60s 分批刷盘
3. 短任务 stop、执行历史页、自动卡住检测(详见平台 changelog)
## 长任务流程
勾选「长任务」执行脚本时:
1. 主站为每台子机生成 `job_id` + 一次性 `secret`,写入 RedisTTL 7 天)
2. 主站用 `nohup` 包装脚本,脚本结束后在子机执行 `curl``POST /api/agent/script-callback`
3. 主站更新 `script_executions.results`,并通过 WebSocket 推送 `script_job_done`
## 配置要求
- `.env` 中必须配置 **`NEXUS_API_BASE_URL`**(子机可访问的主站 HTTPS 地址),否则长任务接口返回 400
## API
| 端点 | 认证 | 说明 |
|------|------|------|
| `POST /api/scripts/exec` | JWT | 请求体增加 `long_task: true` |
| `POST /api/agent/script-callback` | `job_id` + `secret`(一次性) | 子机 curl,无需 JWT |
回调 JSON 示例:
```json
{
"job_id": "…",
"server_id": 12,
"secret": "…",
"exit_code": 0,
"message": "done"
}
```
## Redis → MySQL
| 常量 | 值 | 文件 |
|------|-----|------|
| `REDIS_TTL_SECONDS` | 7200 | `script_execution_store.py` |
| `FLUSH_INTERVAL_SECONDS` | 60 | 同上 |
| `FLUSH_BATCH_LIMIT` | 50 | 同上 |
| `STUCK_NO_PROGRESS_SECONDS` | 3600 | 同上 |
- 全部状态变更只写 Redis;读接口优先 Redis
- 后台 `script_execution_flush`:刷盘 + `detect_stuck_executions`
- `results._meta.events` 时间线;`audit_logs` 即时写入
## 审计 action
`execute_started``execute_command``stop_execution``retry_execution``mark_stuck``script_job_callback``auto_stuck``server_auto_stuck`
## 子机 Agent(短任务停止)
| 接口 | 说明 |
|------|------|
| `POST /exec` + `wait: false` | 返回 `pid` |
| `POST /exec/wait` | 等待结束 |
| `POST /exec/kill` | 终止进程 |
未升级 Agent 时无法短任务中途停止。
## 涉及文件
- `server/application/services/script_jobs.py`
- `server/application/services/script_job_callback.py`
- `server/infrastructure/redis/script_execution_store.py`
- `server/background/script_execution_flush.py`
- `server/api/agent.py`
- `web/app/scripts.html``web/app/script-executions.html`
- `web/agent/agent.py`