Files
Nexus/docs/project/nexus-functional-development-guide-appendix.md
T
Nexus Deploy 0100ab2582 docs: consolidate archive, SSOT guide, and agent entry stubs
Move line-walk/delta/phase2/frontend audits and legacy memory into
docs/archive/, add functional development SSOT, thin AGENTS/CLAUDE stubs,
and regenerate changelog/audit indexes via consolidate_docs.py.
2026-06-04 23:01:03 +08:00

459 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Nexus 6.0 — 功能开发指南 · 附录(详细参考)
> **主文档**: [nexus-functional-development-guide.md](nexus-functional-development-guide.md)
> **版本**: 2026-06-04
> **用途**: API 请求体、前端调用链、E2E/L4 对照、组件映射 — 接续开发时查表用
---
## 附录 A — `/api/sync` 端点与 Pydantic 模型
定义: `server/api/schemas.py` · 路由: `server/api/sync_v2.py` · 前缀: `/api/sync`
| 方法 | 路径 | 请求模型 | 说明 |
|------|------|----------|------|
| POST | `/files` | `SyncFiles` | 批量 rsync 推送 |
| POST | `/preview` | `SyncPreview` | dry-run 预览(单台代表服务器) |
| POST | `/file-ops` | `FileOperation` | 远程 delete/rename/mkdir |
| POST | `/file-clipboard` | `FileClipboardApply` | 远程 copy/move 批量粘贴 |
| POST | `/browse` | `SyncBrowse` | 远程目录列表(POST 兼容路径) |
| POST | `/browse-local` | `SyncBrowseLocal` | Nexus 本机 staging 目录浏览 |
| POST | `/local-file-ops` | `LocalFileOperation` | staging 内 delete/rename |
| POST | `/local-file-preview` | `LocalFilePreview` | staging 文件预览 |
| POST | `/cancel` | `SyncCancel` | 按 batch_id 取消推送 |
| POST | `/reconcile-stale-logs` | `ReconcileStaleLogs` | 将 stuck `running` 日志标失败 |
| POST | `/validate-source-path` | `ValidateSourcePath` | 校验本机源目录 |
| POST | `/diagnose` | `SyncDiagnose` | 单台推送失败诊断 |
| POST | `/file-diff` | `FileSyncDiff` | 本地 vs 远程单文件 diff |
| POST | `/upload` | multipart | 上传到 staging |
| POST | `/upload-zip` | multipart | ZIP 解压到 staging |
| POST | `/verify` | `SyncVerify` | 推送后 md5 校验 |
| POST | `/download` | `FileDownload` | 远程文件下载流 |
| POST | `/read-file` | `FileRead` | 读远程文本(≤5MB |
| POST | `/write-file` | `FileWrite` | 写远程文本 + 可选 etag 乐观锁 |
| POST | `/chmod` | `FileChmod` | chmod/chown |
| POST | `/compress` | `FileCompress` | tar.gz/zip 压缩 |
| POST | `/decompress` | `FileDecompress` | 解压到绝对路径 |
### A.1 核心模型字段
**SyncFiles**(推送)
| 字段 | 类型 | 默认 | 约束 |
|------|------|------|------|
| `server_ids` | `int[]` | — | min 1 |
| `source_path` | `str` | — | Nexus 本机绝对路径,禁系统敏感路径 |
| `target_path` | `str?` | null | 远程绝对路径;空则用各服务器 `target_path` |
| `sync_mode` | `str` | `incremental` | `incremental\|full\|overwrite\|checksum` |
| `batch_size` | `int` | 50 | 1200 |
| `concurrency` | `int` | 10 | 150 |
| `batch_id` | `str?` | null | 12 位 hex;须先连 `/ws/sync` 再 POST |
**SyncPreview**
| 字段 | 说明 |
|------|------|
| `server_id` | 代表服务器(单台 dry-run |
| `verbose` | true 时返回文件列表(≤200 行) |
| 其余 | 同 SyncFiles 的 path/mode |
**FileOperation**
| operation | 必填 | 远程命令 |
|-----------|------|----------|
| `delete` | `path` | `rm -rf` |
| `rename` | `path`, `new_path` | `mv` |
| `mkdir` | `path` | `mkdir -p` |
**FileClipboardApply**
| 字段 | 约束 |
|------|------|
| `mode` | `copy``cp -r``move``mv -t` |
| `sources` | 150 条远程绝对路径 |
| `dest_dir` | 目标目录绝对路径 |
**FileRead / FileWrite**
| 字段 | FileRead | FileWrite |
|------|----------|-----------|
| `max_size` | 默认 1MBmax 5MB | — |
| `content` | — | max 5MB |
| `etag` | — | 乐观锁,冲突 409 |
**FileChmod**
| 字段 | 说明 |
|------|------|
| `mode` | 34 位八进制 `^[0-7]{3,4}$` |
| `owner` / `group` | 可选;group 须同时有 owner |
| `recursive` | 目录递归 |
### A.2 推送 WS 协议(`/ws/sync`
1. 前端生成 `batch_id`12 hex
2. **先** `WebSocket` 连接 `/ws/sync?token=&batch_id=`
3. **再** `POST /api/sync/files` 带相同 `batch_id`
4. 消息: per-server 状态(pending/running/success/failed/cancelled)、进度百分比、错误摘要
5. 取消: `POST /api/sync/cancel` `{ batch_id }`
### A.3 文件浏览双通道
| 通道 | 方法 | 优势 |
|------|------|------|
| REST | `GET /api/files/browse?server_id=&path=` | ETag + 304,前端首选 |
| Sync 兼容 | `POST /api/sync/browse` | 旧网关/部署回退 |
前端 `useFilesBrowse.fetchBrowseListing` 先 GET,失败再 POST。
---
## 附录 B — 文件页(Files)调用链
### B.1 组件树
```
FilesPage.vue
├── FilesToolbar.vue # 服务器选择、刷新、上传、新建
├── FilesPermissionAlert.vue # files-capability / sudo 提示
├── FilesStatusBar.vue # 条目统计、SSH 用户
├── FilesBreadcrumb.vue # 路径面包屑
├── FilesList.vue # v-data-table + 行操作
├── FilesDialogs.vue # 删除/重命名/chmod/压缩等对话框
└── FileEditorWorkbench.vue # Monaco 编辑 + 预览
```
`provideFilesPage(page)` → 子组件通过 `injectFilesPage()` 取上下文(`filesPageContext.ts`)。
### B.2 Composable 依赖图
```
useFilesPage
├── useFilesStore (Pinia) # selectedServer, currentPath, files, cache
├── useFilesSelection # selectedFiles 多选
├── useFilesBrowse(fileFilter) # browse(), ETag, preload Monaco
│ └── GET /api/files/browse | POST /api/sync/browse
├── useFilesPermissions # files-capability, setup-files-sudo
│ └── GET /api/servers/{id}/files-capability
│ └── POST /api/servers/{id}/setup-files-sudo
├── useFilesNavigation # 面包屑、goUp、openEntry、?server_id=
├── useFilesEditor # read/write/preview
│ └── POST /api/sync/read-file | write-file
├── useFilesActions # CRUD、clipboard、upload、compress
│ └── POST /api/sync/file-ops | file-clipboard | upload | chmod | compress…
├── useFilesClipboard # 跨目录 copy/cut/paste 状态
└── useFilesHotkeys # Ctrl+C/V、Delete、F5 刷新
```
### B.3 关键 API 映射(用户操作 → 端点)
| 用户操作 | Composable | API |
|----------|------------|-----|
| 选服务器/进目录 | `useFilesBrowse.browse` | `GET /files/browse` |
| 保存编辑器 | `useFilesEditor.save` | `POST /sync/write-file` |
| 删除/重命名/新建目录 | `useFilesActions` | `POST /sync/file-ops` |
| 复制/剪切/粘贴 | `useFilesActions` + clipboard | `POST /sync/file-clipboard` |
| 上传 | `useFilesActions.doUpload` | `POST /sync/upload` |
| chmod | `useFilesActions.doChmod` | `POST /sync/chmod` |
| 压缩/解压 | `useFilesActions` | `POST /sync/compress` / `decompress` |
| 下载 | `useFilesActions.downloadFile` | `POST /sync/download` |
| 配置 sudo | `useFilesPermissions.setupFilesSudo` | `POST /servers/{id}/setup-files-sudo` |
| 打开终端 | `useFilesNavigation.openInTerminal` | 路由 `#/terminal?server_id=` |
### B.4 Store 缓存策略(`stores/files.ts`
- Key: `{serverId}:{path}`
- 存 ETag + entries + fetchedAt
- `invalidateAfterMutation` 在写操作后清除当前目录及父目录缓存
- `pageReady`: 首次 browse 完成后为 true(避免空态闪烁)
### B.5 路由 Query 参数
| 参数 | 行为 |
|------|------|
| `?server_id=123` | `useFilesNavigation` 自动选中并 browse |
| `?path=/var/www` | 初始路径(须与 server_id 同用) |
---
## 附录 C — 推送页(Push)调用链
### C.1 Composable 依赖图
```
usePushPage
├── usePushForm # source_path, target_path, sync_mode, batch 参数
├── usePushServers # 服务器列表、分类、全选、在线过滤
├── usePushLogs # sync_logs 历史分页
├── usePushProgress # WS + doPush + cancel
│ └── WS /ws/sync
│ └── POST /api/sync/files, /cancel
└── usePushPreview # 预览对话框、confirmAndPush
└── POST /api/sync/preview (并发代表服务器)
```
### C.2 推送流程(时序)
```mermaid
sequenceDiagram
participant UI as PushPage
participant WS as /ws/sync
participant API as /api/sync/files
participant Eng as SyncEngineV2
UI->>UI: generate batch_id (12 hex)
UI->>WS: connect(token, batch_id)
UI->>API: POST /files {batch_id, server_ids, ...}
Eng->>WS: per-server progress
WS->>UI: update pushStatus
Eng->>Eng: rsync over SSH (semaphore)
UI->>API: POST /cancel (optional)
```
### C.3 相关端点(Push 页间接使用)
| 功能 | API |
|------|-----|
| 校验源路径 | `POST /sync/validate-source-path` |
| ZIP 源 | `POST /sync/upload-zip` |
| 失败诊断 | `POST /sync/diagnose` |
| 对账 stuck 日志 | `POST /sync/reconcile-stale-logs` |
| 推送后校验 | `POST /sync/verify` |
---
## 附录 D — 终端页(Terminal)调用链
```
TerminalPage.vue
├── TerminalToolbar.vue
├── TerminalTabBar.vue
├── TerminalServerPicker.vue
├── TerminalArea.vue # xterm 实例
├── TerminalCmdBar.vue # 命令输入 + 高亮
└── TerminalQuickBar.vue # 快捷命令
useTerminalSessions
├── POST /api/auth/webssh-token { server_id }
├── WebSocket /ws/terminal/{session_uuid}?token=
├── Koko: TERMINAL_INIT, DATA, RESIZE, CLOSE
└── onBeforeUnmount → disposeAllSessions
useTerminalQuickCommands → GET/POST /api/terminal/quick-commands
useTerminalSettings → localStorage 主题/字体(非敏感)
```
**WebSSH JWT**: `purpose=webssh`, 绑定 `server_id`, 15min;路径 param `id` 须与 token 内 server_id 一致。
---
## 附录 E — 其余 API 模块明细
### E.1 调度 `/api/schedules``settings.py` 内)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 列表 |
| POST | `/` | 创建(cron + server_ids + source/target + sync_mode 或 script_id |
| PUT | `/{id}` | 更新 |
| DELETE | `/{id}` | 删除 |
后台 `schedule_runner` 每分钟扫描 `enabled=1` 且 cron 到期项 → 调 SyncEngine 或 ScriptService。
### E.2 重试 `/api/retries`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 待重试队列 |
| POST | `/{id}/retry` | 手动触发 |
| DELETE | `/{id}` | 移出队列 |
`retry_runner` 每 5min`SELECT … FOR UPDATE SKIP LOCKED`,指数退避,max 3 次。
### E.3 审计 `/api/audit`
| 方法 | 路径 | Query |
|------|------|-------|
| GET | `/` | `page`, `page_size`, `action`, `username`, `date_from`, `date_to` |
### E.4 告警 `/api/alert-history`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 分页列表 |
| GET | `/stats` | 汇总统计 |
### E.5 预设
| 前缀 | 用途 |
|------|------|
| `/api/presets` | 密码预设 CRUD |
| `/api/ssh-key-presets` | SSH 密钥预设 CRUD |
| `/api/scripts/credentials` | DB 凭据(脚本 `$DB_*` |
### E.6 脚本 `/api/scripts`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/exec` | `{ script_id?, command?, server_ids, timeout }` |
| GET | `/executions` | 分页执行记录 |
| POST | `/executions/{id}/stop` | SIGTERM/SIGKILL |
| POST | `/executions/{id}/retry` | 重新执行 |
| GET | `/exec-config` | 超时阈值等前端展示用 |
### E.7 Agent `/api/agent`
**Heartbeat body(摘要)**
```json
{
"server_id": 1,
"cpu": 12.5,
"memory": 45.0,
"disk": 60.0,
"load": [0.1, 0.2, 0.15],
"agent_version": "6.0.x",
"system_info": { "hostname": "...", "os": "..." }
}
```
**Script callback**: Agent 长任务完成后 POST,更新 Redis + MySQL execution。
### E.8 搜索 `/api/search`
`GET /?q=` — 并行查 servers、scripts、db_credentials、scheduleslimit 各 10)。
---
## 附录 F — 前端页面 ↔ 组件 ↔ Composable 映射
| 页面 | 主要 Composable | 关键子组件 |
|------|-----------------|------------|
| DashboardPage | `useWebSocket`, `useServerPagination` | StatCardsRow |
| ServersPage | `useServerFormDialog`, `useServerPagination` | ServerFormDialog |
| TerminalPage | `useTerminalSessions`, `useTerminalCmdBar` | TerminalArea, TerminalTabBar |
| FilesPage | `useFilesPage` | FilesToolbar, FilesList, FileEditorWorkbench |
| PushPage | `usePushPage` | PushToolbar, PushServerGrid, PushProgressList |
| ScriptsPage | 页内逻辑 + Monaco | ScriptEditorDialog |
| CredentialsPage | 三 Tab CRUD | — |
| SchedulesPage | cron 表单 | ScheduleFormDialog |
| RetriesPage | 列表 + retry | — |
| CommandsPage | 双视图切换 | — |
| AlertsPage | 过滤 + 分页 | — |
| AuditPage | `normalizeAuditList` | — |
| SettingsPage | TOTP/API Key/IP 白名单 | TerminalQuickCommandsSettings |
| LoginPage | `useAuthStore.login` | — |
### F.1 全局布局(`App.vue`
- 侧栏 14 项导航(v-list
- 顶栏:全局搜索 → `GET /api/search`
- 用户菜单:主题切换、退出
- `useWebSocket` 告警 toast + Dashboard 刷新触发
### F.2 类型定义
所有 API 响应类型优先维护在 `frontend/src/types/api.ts`;新增字段先改类型再改 composable。
---
## 附录 G — 验收与 E2E 对照
### G.1 L4 统一入口
```bash
python3 scripts/run_nexus_acceptance.py --base http://127.0.0.1:8600 --with-ui
```
阶段:
| Phase | 内容 |
|-------|------|
| API | `tests/test_api.py` + `tests/test_api_extended.py` |
| UI | `frontend/e2e/full-acceptance.spec.mjs`15 用例) |
| Unit | `--with-unit` 时跑路径/权限单测 |
功能 rollup 映射: `tests/acceptance_catalog.py``FEATURES[]`
### G.2 Playwright 用例 ↔ 页面
| 用例 ID | 路由 | 验证点 |
|---------|------|--------|
| 01 | `#/` | 登录 + Dashboard 卡片 |
| 02 | `#/servers` | 添加对话框、批量选择 |
| 03 | `#/` | 全局搜索输入 |
| 04 | `#/terminal` | 终端页可见 |
| 05 | `#/files` | 文件页可见 |
| 06 | `#/push` | 预览按钮(不真推送) |
| 07 | `#/scripts` | 新建脚本对话框 |
| 08 | `#/credentials` | 三 Tab 切换 |
| 09 | `#/schedules` | 新建调度对话框 |
| 10 | `#/retries` | 重试列表 |
| 11 | `#/commands` | 命令/会话视图 |
| 12 | `#/alerts` | 告警页 |
| 13 | `#/audit` | 审计页 |
| 14 | `#/settings` | 设置区块 |
| 15 | `#/` | 主题切换 + 登出 |
**环境变量**
```bash
NEXUS_E2E_BASE=http://127.0.0.1:8600
NEXUS_E2E_USER=admin
NEXUS_E2E_PASSWORD=*** # 必填;测试账号勿开 TOTP
```
### G.3 API 测试环境
与 E2E 共用 `.env``NEXUS_TEST_ADMIN_PASSWORD``seed_local_admin.py` 写入)。
### G.4 本地验证脚本
`bash scripts/local_verify.sh` — ruff、import、pytest 子集、前端 typecheck(若可用)。
---
## 附录 H — 错误码与前端处理约定
| HTTP | 场景 | 前端处理 |
|------|------|----------|
| 401 | JWT 过期 | `fetchAuthed` 自动 refresh 一次 |
| 202 | 需 TOTP | `TotpRequiredError` → 显示 TOTP 输入 |
| 304 | Browse ETag 命中 | 用缓存 entries |
| 409 | write-file etag 冲突 | 提示合并/覆盖 |
| 429 | 登录锁定 | Login 页显示剩余时间 |
| 503 | 安装模式 | 引导 `/app/install.html` |
统一错误文案: `formatApiError(err)` + `$snackbar`
---
## 附录 I — 新增功能检查清单(扩展)
### 新 Sync 相关端点
- [ ] `schemas.py` Pydantic + path validator
- [ ] `sync_v2.py` 路由 + `_audit_sync`
- [ ] `tests/test_api.py``test_sync_*.py`
- [ ] 前端 `http.post('/sync/...')` + types
- [ ] 更新附录 A + 主文档 §10
- [ ] changelog ≥10 行
### 新 Files 操作
- [ ] `useFilesActions` 方法 + `FilesDialogs` UI
- [ ] 热键(若适用)注册到 `useFilesHotkeys`
- [ ] browse 缓存 invalidate
- [ ] E2E 05 或专项用例
### 新页面(第 15 页)
- [ ] `pages/XxxPage.vue`
- [ ] `router/index.ts` + `App.vue` nav
- [ ] `acceptance_catalog.py` + Playwright 用例
- [ ] `run_nexus_acceptance` 报告可 rollup
---
**维护**: 改 API/composable 时同步更新对应附录章节。