# 审计 — 服务器批量任务 · 执行记录 · 凭据合并（2026-06-08）

**Changelog**:
- `docs/changelog/2026-06-08-server-batch-background-jobs.md`
- `docs/changelog/2026-06-08-server-batch-job-persist.md`
- `docs/changelog/2026-06-08-install-agent-auto-api-key.md`
- `docs/changelog/2026-06-08-agent-install-bt-pyenv-python.md`
- `docs/changelog/2026-06-08-unified-execution-records.md`
- `docs/changelog/2026-06-08-credentials-merge-servers-page.md`

**生产已热更（部分）**: 2026-06-08 `docker cp` — 执行记录 + 批量任务；凭据合并待下次前端部署。

## 审计范围

### 本批次（批量 · 执行记录 · 凭据）

| 模块 | 文件 |
|------|------|
| 批量后台 | `server_batch_service.py`、`server_batch_common.py`、`server_batch_store.py` |
| 批量 API | `server/api/servers.py`（`BatchJobStarted`、`GET /batch/jobs`） |
| 结果持久化 | `ServerBatchJob` 模型、`server_batch_job_repo.py` |
| 统一看板 API | `execution_record_service.py`、`execution_records.py`、`main.py` |
| Agent 安装 | `web/agent/install.sh`、`ensure_agent_api_key()` |
| 前端队列 | `useScriptExecutionQueue.ts`、`useServerBatchJobViewer.ts`、`App.vue` |
| 执行记录页 | `ScriptRunsPage.vue`、`executionRecordRoute.ts` |
| 审计入口 | `AuditPage.vue`、`auditBatchDetail.ts`、`auditLabels.ts` |
| 凭据合并 | `CredentialsManager.vue`、`CredentialsDialog.vue`、`ServersPage.vue` |
| 单测 | `test_ensure_agent_api_key.py`、`test_server_batch_job_persist.py`、`test_batch_server_display_name.py` |

### 已提交 `b8425cc`（实时排序，交叉引用）

| 模块 | 文件 |
|------|------|
| 排序服务 | `server_list_sort.py` |
| 仓储 | `server_repo.py` |
| 前端分页 | `useServerPagination.ts` |
| 看板 | `DashboardPage.vue` |
| 单测 | `test_server_list_sort.py` |
| 构建入口 | `web/app/index.html` |

> 排序专项 Step3/Closure 见 `docs/audit/2026-06-08-servers-live-sort-fix.md`。

## 安全

| 项 | 结论 |
|----|------|
| 批量端点鉴权 | PASS 全部 `Depends(get_current_admin)` |
| SSH 批量命令 | PASS 经 `sudo_wrap` + 既有 `exec_ssh_command` |
| Agent Key 自动生成 | PASS 服务端 `secrets.token_urlsafe`，不入响应体 |
| 执行记录 API | PASS `/api/execution-records` 需 JWT |
| 凭据 CRUD | PASS 复用原 `/presets/`、`/ssh-key-presets/`、`/scripts/credentials` |
| 审计 detail JSON | PASS 仅含 job_id/摘要/失败原因，无密钥 |
| CUD 审计 | PASS `batch_install_agent` 等 + `target_type=server_batch_job` |

## Step 3 规则扫描

| H | 规则 | 结论 |
|---|------|------|
| H1 | 批量 SSH 不得阻塞 HTTP | PASS 立即返回 `job_id`，后台 `asyncio.create_task` |
| H2 | 无静默吞错 | PASS 单台失败写入 `results[].error`；审计/落库失败 `logger.warning` |
| H3 | Redis 过期可回看 | PASS MySQL `server_batch_jobs` + 审计「查看结果」 |
| H4 | 脚本/批量 ID 冲突 | PASS 详情须带 `?kind=script\|server_batch` |
| H5 | 安装 Agent 无 Key 不阻断 | PASS `ensure_agent_api_key()` 自动写入 |
| H6 | install.sh 不破坏系统 Python | PASS 并行装 `python3.12`，不改 alternatives |
| H7 | 凭据合并不丢功能 | PASS 三 Tab CRUD 原样迁入对话框 |
| H8 | WS 进度复用 | PASS `task_kind: server_batch` 与脚本队列共用 |

## Closure

| H | 判定 | 依据 |
|---|------|------|
| H1 | PASS | `_start_server_batch` → `start_batch_job` |
| H2 | PASS | `_run_background` except 标记 failed + warning 日志 |
| H3 | PASS | `_persist_job_finalize` + `AuditPage` 查看结果 |
| H4 | PASS | `GET /execution-records/{id}?kind=` |
| H5 | PASS | `server_batch_common.ensure_agent_api_key` |
| H6 | PASS | `install.sh` `_install_system_python312` |
| H7 | PASS | `CredentialsManager.vue` 全量迁移 |
| H8 | PASS | `broadcast_script_progress` + `registerServerBatchJob` |

## Step 8 DoD

- [x] `bash scripts/local_verify.sh` — 26/26
- [x] `pytest tests/test_ensure_agent_api_key.py tests/test_server_batch_job_persist.py` — 4/4
- [x] `cd frontend && npm run build` — 通过
- [x] changelog 6 篇（本批次主题）
- [x] 生产热更验证报告 `docs/reports/2026-06-08-unified-execution-records-deploy-verification.md`
- [ ] 凭据对话框生产终验（待前端部署）
- [ ] 用户浏览器终验（执行记录 + 批量 Agent 三操作）

## 测试证据

```bash
bash scripts/local_verify.sh          # 26/26
pytest tests/test_ensure_agent_api_key.py tests/test_server_batch_job_persist.py -q  # 4 passed
cd frontend && npm run build          # OK
curl https://api.synaglobal.vip/health  # ok
curl https://api.synaglobal.vip/api/execution-records  # 401
```

## 残留风险（可接受）

| 风险 | 说明 |
|------|------|
| 热更新非镜像 | 下次 `nx update` 未 push 代码会被覆盖 |
| 合并列表分页 | 脚本+批量 total 为两表之和，大 offset 可能略偏 |
| 历史任务 | Redis-only 旧任务无法补录 MySQL |
| 工作区其它 diff | 登录门控/页面缓存等 changelog 已有，**未纳入本审计部署包**，勿混部 |

## Gate 7 变更文件对照

本审计覆盖上述「审计范围」表内全部路径；工作区另有 `LoginPage`、`*useCachedQuery*` 等变更属并行会话，部署时须拆分或一并补审计。
