# 前端 14 页 API 契约审计（含凭据轮询）

**日期**: 2026-06-06  
**基线**: [audit-frontend-14p-2026-06-04-closure.md](audit-frontend-14p-2026-06-04-closure.md)（43 H，0 P0/P1）  
**增量范围**: 凭据轮询 `add-by-ip` / `pending_servers`；预设 `username` 字段  
**方法**: 静态对照 `frontend/src/pages/*` + composables ↔ `server/api/*.py` 路由与响应形

## 结论

| 维度 | 结果 |
|------|------|
| 14 页 API 路径 | **PASS** — 全部端点在后端存在且 HTTP 方法一致 |
| 新增契约（Servers / Credentials） | **PASS** — 请求/响应字段前后端对齐 |
| 类型集中化 | **PASS** — `PendingServerItem` / `AddByIpResponse` / `PollErrorItem` 已入 `types/api.ts` |
| 单元测试 | **PASS** — `tests/test_credential_poller.py` 7 passed |
| 本地门控 | **WARN** — `local_verify.sh` 因本地 MySQL 断连未跑通（环境问题，非契约缺口） |
| 生产 SPA 部署 | **PASS** — `ServersPage`/`CredentialsPage` 分块已含新契约；见 [production-verification](2026-06-06-credential-polling-production-verification.md) |

**新 FINDING**: 0 × P0/P1，0 × P2（契约层）

---

## 1. 凭据轮询契约（增量）

### 1.1 后端

| 方法 | 路径 | 实现 |
|------|------|------|
| POST | `/api/servers/add-by-ip` | `servers.py` → `poll_credentials` → 成功 `create_server` / 失败 `pending_servers` |
| GET | `/api/servers/pending` | `PendingServerRepositoryImpl.list_all` |
| POST | `/api/servers/pending/{id}/retry` | 再轮询；域名已存在 → 409 |
| DELETE | `/api/servers/pending/{id}` | 204 |

预设 `username`: `settings.py` `GET/POST/PUT` `/presets/`、`/ssh-key-presets/`。

### 1.2 前端

| 组件 | 调用 |
|------|------|
| `ServersPage.vue` | 上述四端点；`onMounted` 加载 pending |
| `CredentialsPage.vue` | 预设 CRUD 含 `username`（默认 `root`） |

### 1.3 响应形对照

| 字段 | 后端 | 前端消费 |
|------|------|----------|
| `success` | ✓ | 分支成功/失败 |
| `server` | `Server` 序列化 | 刷新列表 |
| `matched_preset` / `matched_username` | ✓ | 成功 toast |
| `pending_id` | ✓ | 失败后可查列表 |
| `errors[]` | `PollError` 列表 | 失败对话框表格 |
| `message` | 聚合摘要 | 对话框标题区 |

---

## 2. 十四页 API 矩阵

前缀均为 `/api`；`http` 客户端已含前缀，下表写相对路径。

### 2.1 DashboardPage `/`

| 端点 | 方法 | 后端 | 状态 |
|------|------|------|------|
| `/servers/stats` | GET | `servers.py` | OK |
| `/alert-history/` | GET | `alert_history.py` | OK |
| `/audit/` | GET | `audit.py` | OK |
| `/servers/` | GET | `servers.py` | OK |
| `/settings/bing-wallpapers/sync` | POST | `settings.py` | OK |

### 2.2 ServersPage `/servers`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/servers/` | GET/POST/PUT/DELETE | OK |
| `/servers/stats` | GET | OK |
| `/servers/check` | POST | OK |
| `/servers/batch/install-agent` 等 | POST | OK |
| `/servers/{id}/logs` | GET | OK |
| `/servers/add-by-ip` | POST | **新增 OK** |
| `/servers/pending` | GET | **新增 OK** |
| `/servers/pending/{id}/retry` | POST | **新增 OK** |
| `/servers/pending/{id}` | DELETE | **新增 OK** |
| `/presets/`、`/ssh-key-presets/`、`/assets/nodes` | GET | OK（表单 composable） |

### 2.3 TerminalPage `/terminal`

| 端点 | 方法 | 位置 | 状态 |
|------|------|------|------|
| `/auth/webssh-token` | POST | `useTerminalSessions` | OK |
| `/servers/` | GET | `useTerminalSessions` | OK |
| `/terminal/quick-commands` | GET/POST/PUT/DELETE | `useTerminalQuickCommands` | OK |
| `/terminal/quick-commands/reorder` | PUT | `useTerminalQuickCommands` | OK |
| WebSocket `/ws/terminal` | WS | 页内 | OK（RISK 已接受） |

### 2.4 FilesPage `/files`

| 端点 | 方法 | Composable | 状态 |
|------|------|------------|------|
| `/files/browse` | GET | `useFilesBrowse`（首选） | OK |
| `/sync/browse` | POST | `useFilesBrowse` 回退 | OK |
| `/servers/{id}/files-capability` | GET | `useFilesPermissions` | OK |
| `/servers/{id}/setup-files-sudo` | POST | `useFilesPermissions` | OK |
| `/sync/read-file`、`write-file`、`file-ops`、`chmod`、`compress`、`decompress`、`file-clipboard`、`upload` | POST | `useFilesActions` 等 | OK |

### 2.5 PushPage `/push`

| 端点 | 方法 | Composable | 状态 |
|------|------|------------|------|
| `/servers/` | GET | `usePushServers` | OK |
| `/sync/preview` | POST | `usePushPreview` | OK |
| `/sync/files` | POST | `usePushProgress` | OK |
| `/sync/cancel` | POST | `usePushProgress` | OK |
| `/servers/logs` | GET | `usePushLogs` | OK |
| `/sync/reconcile-stale-logs` | POST | `usePushLogs` | OK |
| `/sync/diagnose` | POST | `usePushLogs` | OK |
| WebSocket `/ws/sync` | WS | `usePushProgress` | OK（RISK 已接受） |

### 2.6 ScriptsPage `/scripts`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/scripts/` | GET/POST/PUT/DELETE | OK |
| `/scripts/exec` | POST | OK |
| `/scripts/executions` | GET | OK |
| `/scripts/executions/{id}` | GET | OK |
| `/scripts/executions/{id}/stop` | POST | OK |

### 2.7 CredentialsPage `/credentials`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/presets/` | GET/POST/PUT/DELETE | OK；**username 新增** |
| `/ssh-key-presets/` | GET/POST/PUT/DELETE | OK；**username 新增** |
| `/scripts/credentials` | GET/POST/PUT/DELETE | OK |

### 2.8 SchedulesPage `/schedules`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/schedules/` | GET/POST/PUT/DELETE | OK |
| `/sync/files` | POST（runNow） | OK |

### 2.9 RetriesPage `/retries`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/retries/` | GET | OK |
| `/retries/{id}/retry` | POST | OK |
| `/retries/{id}` | DELETE | OK |

### 2.10 CommandsPage `/commands`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/assets/command-logs` | GET | OK |
| `/assets/ssh-sessions` | GET | OK |

### 2.11 AlertsPage `/alerts`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/alert-history/stats` | GET | OK |
| `/alert-history/` | GET | OK |

### 2.12 AuditPage `/audit`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/audit/` | GET | OK |

### 2.13 SettingsPage `/settings`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/settings/` | GET | OK |
| `/settings/{key}` | PUT | OK |
| `/settings/telegram/test` | POST | OK |
| `/settings/telegram/reveal-token` | POST | OK |
| `/settings/api-key/reveal` | POST | OK |
| `/settings/ip-allowlist` | GET/POST | OK |
| `/settings/ip-allowlist/toggle` | POST | OK |
| `/auth/password` | PUT | OK |
| `/auth/totp/setup` | POST | OK |
| `/auth/totp/enable` | POST | OK |
| `/auth/totp/disable` | POST | OK |

### 2.14 LoginPage `/login`

| 端点 | 方法 | 状态 |
|------|------|------|
| `/settings/bing-wallpapers` | GET | OK（公开只读） |
| `/auth/login` | POST | `stores/auth`（非页内直接调） | OK |

### 2.15 全局（App / auth store）

| 端点 | 方法 | 状态 |
|------|------|------|
| `/search/` | GET | `App.vue` | OK |
| `/auth/me` | GET | `stores/auth` | OK |
| `/auth/logout` | POST | `stores/auth` | OK |

---

## 3. 与 2026-06-04 closure 的差异

| 变更 | 说明 |
|------|------|
| ServersPage +4 端点 | 凭据轮询快速添加与失败队列 |
| CredentialsPage 预设 | `username` 字段贯通 DB / API / UI |
| `types/api.ts` | 新增 pending 相关类型 |
| 功能指南 §12.2、§12.7 | 已更新 |
| 附录 J | 新增凭据轮询专章 |

跨页 RISK（WS JWT、危险命令、bing 公开 GET）**无变化**，仍见 `risk-acceptance-single-operator.md`。

---

## 4. 验证记录

```bash
.venv/bin/pytest tests/test_credential_poller.py -q   # 7 passed
cd frontend && npx vite build                          # OK
```

```bash
# 待环境恢复后
bash scripts/local_verify.sh
cd frontend && NEXUS_E2E_BASE=http://127.0.0.1:8600 npm run test:e2e
```

---

## 5. 后续建议

1. 生产部署前端构建物（`ServersPage` / `CredentialsPage` 新 UI）。
2. 可选 E2E：快速添加 IP → 失败列表 → 重试/删除。
3. `acceptance_catalog.py` 可增加 `add-by-ip` 条目（若纳入 L4 回归）。

**维护**: 改 API 时同步更新本报告对应行、附录 J、功能指南 §12。
