Files
Nexus/docs/project/nexus-functional-development-guide-appendix.md
T
2026-07-08 22:31:31 +08:00

18 KiB
Raw Blame History

Nexus 6.0 — 功能开发指南 · 附录(详细参考)

主文档: nexus-functional-development-guide.md
版本: 2026-06-06
用途: 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 copycp -rmovemv -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_id12 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 推送流程(时序)

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/schedulessettings.py 内)

方法 路径 说明
GET / 列表(含 next_runtarget_pathschedule_typerun_mode
POST / 创建 pushsource_pathtarget_path?sync_modecron_expr/fire_atscriptscript_id/script_content
PUT /{id} 更新(allowlist 含 target_path
DELETE /{id} 删除

PushSchedule.target_path 留空时 runner / runNow 与各机 servers.target_path 一致。

后台 schedule_runner 每分钟扫描 enabled=1 且到期项 → SyncEngineV2 或 ScriptService。

E.2 重试 /api/retries

方法 路径 说明
GET / 待重试队列
POST /{id}/retry 手动触发
DELETE /{id} 移出队列

retry_runner 每 5minSELECT … 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(摘要)

{
  "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 统一入口

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.mjs15 用例)
Unit --with-unit 时跑路径/权限单测

功能 rollup 映射: tests/acceptance_catalog.pyFEATURES[]

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 #/ 主题切换 + 登出

环境变量

NEXUS_E2E_BASE=http://127.0.0.1:8600
NEXUS_E2E_USER=admin
NEXUS_E2E_PASSWORD=***   # 必填;测试账号勿开 TOTP

G.3 API 测试环境

与 E2E 共用 .envNEXUS_TEST_ADMIN_PASSWORDseed_local_admin.py 写入)。

G.4 本地验证脚本

bash scripts/local_verify.sh — ruff、import、pytest 子集、前端 typecheck(若可用)。


附录 J — 凭据轮询与 pending servers

定义: server/application/services/credential_poller.py · 路由: server/api/servers.py · 表: pending_servers

J.1 API

方法 路径 请求体 成功响应 失败/待定
POST /api/servers/add-by-ip AddByIpRequest: domain(必填), port?(默认 22, name? { success: true, server, matched_preset, matched_username } { success: false, pending_id, errors[], message } — 写入 pending
GET /api/servers/pending { items: PendingServer[], total }
POST /api/servers/pending/{id}/retry {} 同 add-by-ip 同 add-by-ip;域名已存在 → 409 并删除 pending
DELETE /api/servers/pending/{id} 204 404

PollErrorItemerrors[]: preset_typepassword | ssh_key)、preset_nameusernameerror

PendingServer(列表项): id, name, domain, port, attempts, last_error, last_attempt_at, created_at 及可选 agent_port, target_path, category, platform_id, node_id

J.2 轮询顺序

  1. 解密全部 password_presets(按 id 升序),再全部 ssh_key_presets
  2. 每条使用预设 username(缺省 root)调用 try_ssh_login
  3. 首条成功 → create_server + 审计 create_server;全部失败 → pending_servers upsert + 审计 add_by_ip_failed

J.3 前端(ServersPage

用户操作 API
打开「快速添加」对话框
提交 IP POST /servers/add-by-ip
失败弹窗展示 errors[]
页面加载 / 刷新失败列表 GET /servers/pending
行内重试 POST /servers/pending/{id}/retry
行内删除 DELETE /servers/pending/{id}

类型定义: frontend/src/types/api.tsPendingServerItem, AddByIpResponse, PollErrorItem

J.4 预设 usernameCredentialsPage

端点 新增字段
POST/PUT /api/presets/ username(默认 root
POST/PUT /api/ssh-key-presets/ username(默认 root
GET 列表 每项含 username

附录 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.pytest_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 时同步更新对应附录章节。