docs: reports + research + team roster + tech-stack evaluation
Add implementation progress reports, review reports (CTO/quality/deploy), session log, signoff table, team roster, collaboration charter, tech-stack evaluation and inventory, research reports. Remove obsolete multisync_server.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Nexus 文档索引
|
||||
|
||||
> 最后更新: 2026-05-21
|
||||
|
||||
---
|
||||
|
||||
## project/ — 项目概况
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `project/status.md` | 项目进度总览 + 数据库结构 + 迭代计划 |
|
||||
| `project/roadmap.md` | 功能规划 + ADR架构决策 + 实现顺序 |
|
||||
| `project/deploy.md` | 部署指南 |
|
||||
|
||||
## team/ — 部门协作
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `team/collaboration-charter.md` | 部门协作章程(5阶段流水线) |
|
||||
| `team/roster-2026-05-21.md` | **团队编制档案(194人/9部门)** |
|
||||
| `team/roles/` | 12个部门角色定义文档 |
|
||||
|
||||
## design/ — 设计规格与实施计划
|
||||
| 目录 | 说明 |
|
||||
|------|------|
|
||||
| `design/specs/` | 10个设计规格(推送/密码/重试/文件管理器/架构/等) |
|
||||
| `design/plans/` | 5个实施计划 |
|
||||
|
||||
## research/ — 竞品与技术调研
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `research/jumpserver-easynode-architecture-analysis.md` | JumpServer & EasyNode 架构分析 |
|
||||
|
||||
## changelog/ — 修复记录
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `changelog/fix-plan-2026-05-20.md` | 代码问题修复计划(13项) |
|
||||
| `changelog/fix-plan-full-2026-05-20.md` | 全量未完成项修复计划(24项) |
|
||||
| `changelog/fix-verification-report-2026-05-20.md` | 修复验证报告 |
|
||||
|
||||
## memory/ — 项目记忆(AI辅助)
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `memory/MEMORY.md` | 记忆索引 |
|
||||
| `memory/mem_*.md` | 17个记忆文件(概述/决策/数据流/守护/模块/测试/等) |
|
||||
|
||||
## skills/ — 团队技能定义(AI辅助)
|
||||
| 目录 | 说明 |
|
||||
|------|------|
|
||||
| `skills/` | 194个技能文件(各部门AI技能定义) |
|
||||
|
||||
## security/ — 安全策略
|
||||
| 目录 | 说明 |
|
||||
|------|------|
|
||||
| `security/` | 安全策略与规范(待填充) |
|
||||
|
||||
## reports/ — 工作报告
|
||||
| 目录 | 说明 |
|
||||
|------|------|
|
||||
| `reports/` | 各部门工作报告(审查/实施阶段) |
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
| 优先级 | 数量 | 说明 |
|
||||
|--------|------|------|
|
||||
| **P0 — 已登记待改** | 3 项 | 原代码审查遗留,PROJECT_STATUS.md 已标记 |
|
||||
| **P0 — 已登记待改** | 3 项 | 原代码审查遗留,project/status.md 已标记 |
|
||||
| **P1 — 安全中危** | 5 项 | 新发现的安全问题,涉及密钥/注入/泄露 |
|
||||
| **P2 — 代码质量** | 5 项 | 硬编码、日志规范、路径兼容性等 |
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
|------|-----|
|
||||
| 计划修复项 | 13 项 |
|
||||
| 已完成修复 | 13 项 ✅ 100% |
|
||||
| 新增配置文件 | 1 (`docs/fix-plan-2026-05-20.md`) |
|
||||
| 新增配置文件 | 1 (`docs/changelog/fix-plan-2026-05-20.md`) |
|
||||
| 删除死代码 | 2 文件 (`retry_engine_fix.py`, `retry_engine_local.py`) |
|
||||
| 语法错误 | 0 |
|
||||
| 回归风险 | 低(修改集中在死代码删除、注释补充、配置提取,核心逻辑不变) |
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
# 设计部审查报告 — Nexus 技术栈全面评估
|
||||
|
||||
**审查人**: 设计部负责人
|
||||
**审查日期**: 2026-05-21
|
||||
**审查范围**: 全部设计规格(10份)、实施计划(5份)、竞品分析、roadmap/ADR、config.py、requirements.txt、.env.example
|
||||
|
||||
---
|
||||
|
||||
## 一、当前技术栈完整清单
|
||||
|
||||
### 1.1 后端运行时
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **Python** | 3.12 | 后端运行时 | **合理**。FastAPI 生态成熟,异步支持完善,社区活跃 | 低 |
|
||||
| **FastAPI** | 0.115.6 | Web 框架 | **合理**。原生 async/await、自动 OpenAPI 文档、Pydantic 集成、WebSocket 原生支持 | 低 |
|
||||
| **Uvicorn** | 0.34.0 | ASGI 服务器 | **合理**。FastAPI 标配,uvloop 性能优异 | 低 |
|
||||
|
||||
### 1.2 数据层
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **SQLAlchemy** | 2.0.36 (asyncio) | ORM | **合理**。2.0 异步支持成熟,比 Tortoise-ORM 灵活,比 Django ORM 更适合 FastAPI | 低 |
|
||||
| **aiomysql** | 0.2.0 | MySQL 异步驱动 | **存疑**。社区较小,更新频率低。官方推荐 `aiomysql` 但生态不如 `asyncmy`。issues 积压 | **中** |
|
||||
| **Alembic** | 1.14.0 | 数据库迁移 | **合理**。SQLAlchemy 官方迁移工具,必要组件 | 低 |
|
||||
| **Pydantic** | 2.10.3 | 数据验证/序列化 | **合理**。FastAPI 核心依赖,v2 性能大幅提升 | 低 |
|
||||
| **pydantic-settings** | 2.7.0 | 配置管理 | **合理**。环境变量 + .env 加载,支持前缀 | 低 |
|
||||
| **Redis (hiredis)** | 5.2.1 | 缓存/心跳/队列/Pub-Sub | **合理**。5.x async 支持成熟,hiredis 加速 C 解析器。但文档中对 Redis 角色的描述严重不足 | **高(文档缺位)** |
|
||||
|
||||
### 1.3 SSH 与远程执行
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **paramiko** | 3.5.0 | SSH 连接 | **必要但有问题**。同步库,已在 Sprint 规划中确认其阻塞事件循环的问题,通过 ThreadPoolExecutor 缓解 | **中** |
|
||||
| **asyncssh** | 2.17.0 | SSH 连接 (异步) | **前瞻性选型**。作为 paramiko 的异步替代引入,但在 roadmap 中才规划用于 Web SSH,当前未实际使用。两个 SSH 库并存增加维护成本 | **中** |
|
||||
| **rsync** | 系统级 | 文件同步 | **合理**。成熟、增量传输、压缩、支持 SSH 隧道 | 低 |
|
||||
|
||||
### 1.4 HTTP 与通信
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **httpx** | 0.28.1 | HTTP 客户端 | **合理**。async/await 支持,Agent 通信和外部 API 调用 | 低 |
|
||||
| **websockets** | 14.1 | WebSocket | **合理**。与 FastAPI 原生 WS 互补,进度推送和重试通知 | 低 |
|
||||
| **python-multipart** | 0.0.19 | 表单解析 | **合理**。文件上传必需 | 低 |
|
||||
|
||||
### 1.5 安全加密
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **PyJWT** | 2.10.1 | JWT 令牌 | **合理**。roadmap 规划 JWT 替换 PHP 登录 | 低 |
|
||||
| **bcrypt** | 4.2.1 | 密码哈希 | **合理**。密码存储标准 | 低 |
|
||||
| **Fernet (Python stdlib)** | — | 对称加密 | **合理**。密码预设加密存储,与 Python cryptography 库兼容 | 低 |
|
||||
|
||||
### 1.6 调度与后台任务
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **APScheduler** | 3.11.0 | 定时任务 | **存疑**。sprint 规划提到 `scheduler.py timedelta import 修复`,说明已有 bug。且 Asyncio 模式下 APScheduler 3.x 支持不完善,4.0 才有原生 async | **中** |
|
||||
| **asyncio.create_task** | — | 后台协程 | **合理**。轻量级后台任务(retry_loop、self_monitor 等) | 低 |
|
||||
|
||||
### 1.7 模板与文件
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **Jinja2** | 3.1.5 | 模板引擎 | **合理**。FastAPI 标配 | 低 |
|
||||
| **aiofiles** | 24.1.0 | 异步文件 IO | **合理**。文件上传/管理场景 | 低 |
|
||||
| **PyYAML** | 6.0.2 | YAML 解析 | **遗留依赖**。架构优化报告中已确认 Agent 配置从 YAML 迁移到 JSON,但依赖未移除 | 低 |
|
||||
| **Rich** | 13.9.4 | CLI 美化 | **低优先级**。CLI 工具使用,后端运行非必需 | 低 |
|
||||
|
||||
### 1.8 前端技术
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **PHP** | 7.4 | 前端管理页面 | **过渡方案**。roadmap ADR-006 已确认全面弃用 AdminLTE,迁移到 Tailwind CSS v4 + 静态 HTML 调 API | **高** |
|
||||
| **AdminLTE** | 3.x (推断) | UI 框架 | **淘汰中**。与 Tailwind CSS v4 不兼容,迁移期间双框架并存 | **中** |
|
||||
| **Tailwind CSS v4** | 4 | CSS 框架 | **合理**。零运行时、纯静态 | 低 |
|
||||
| **Monaco Editor** | 0.45.0 | 代码编辑器 | **合理**。文件管理器中编辑代码 | 低 |
|
||||
| **Font Awesome** | 6.5.1 | 图标库 | **合理** | 低 |
|
||||
| **xterm.js** | — | 终端模拟器 | **规划中**。Web SSH 终端方案选用,合理 | 低(未落地) |
|
||||
| **Vanilla JS** | — | 前端逻辑 | **合理**。不引入 Vue/React 框架,与现有 PHP 集成更简单 | 低 |
|
||||
|
||||
### 1.9 部署与运维
|
||||
|
||||
| 技术 | 版本 | 用途 | 选型合理性 | 风险评估 |
|
||||
|------|------|------|-----------|---------|
|
||||
| **Nginx** | — | 反向代理 | **合理** | 低 |
|
||||
| **Supervisor** | — | 进程管理 | **合理**。3 层守护的 Layer 1 | 低 |
|
||||
| **MySQL** | 8.4 | 持久化存储 | **合理**。宝塔标配,生态成熟 | 低 |
|
||||
| **Redis** | 7 | 缓存/实时数据 | **合理**。心跳热数据、告警冷却、Pub/Sub。但文档对 Redis key 设计完整记录不足 | **中** |
|
||||
|
||||
---
|
||||
|
||||
## 二、文档提及但未落地的技术
|
||||
|
||||
以下技术在设计文档或规划中明确提及,但实际代码中尚未实现或未达到文档描述的成熟度:
|
||||
|
||||
| 技术 | 提及位置 | 文档描述 | 落地状态 | 差距分析 |
|
||||
|------|---------|---------|---------|---------|
|
||||
| **asyncssh 连接池** | roadmap ADR-002, 竞品分析 §5.3 | 替代 paramiko,引用计数模式连接池 | **未落地** | requirements.txt 已引入 asyncssh,但当前 SSH 操作仍以 paramiko 为主。连接池设计仅停留在 ADR 阶段 |
|
||||
| **xterm.js** | roadmap N2, 竞品分析 §5 | Web SSH 终端,Koko 消息协议 | **未落地** | 仅规划,无实现代码 |
|
||||
| **JWT + TOTP 登录** | roadmap ADR-005 | 替换 PHP 登录,全新 JWT 认证 | **未落地** | PyJWT 和 bcrypt 已安装,admins 表存在,但认证仍是 PHP Session |
|
||||
| **Platform 资产模型** | roadmap N1, 竞品分析 §2 | Platform 模板 + 单表 Asset + JSONB | **未落地** | 仅 ADR 阶段,servers 表未扩展 |
|
||||
| **WebSocket Terminal 端点** | roadmap N2 | `/ws/terminal/{token}` | **未落地** | WebSocket 已在用(推送进度),但终端端点未实现 |
|
||||
| **SFTP 文件传输** | roadmap N3 | SFTP 文件管理通道 | **未落地** | 仅有 WebFM(管理本机),SFTP 通道未实现 |
|
||||
| **命令日志** | roadmap N5 | command_logs 表,SSH 命令记录 | **未落地** | 仅规划,审计日志 audit_logs 表存在但记录 API 操作而非 SSH 命令 |
|
||||
| **apscheduler 定时推送** | spec simplified-push | push_schedules 表(cron_expr, server_ids) | **已建表但未深度集成** | push_schedules 表存在但文档和代码中都缺少调度执行逻辑 |
|
||||
|
||||
---
|
||||
|
||||
## 三、代码使用但文档未记录的技术
|
||||
|
||||
以下技术已在代码(requirements.txt / config.py / .env.example)中实际存在,但设计文档和规划中**完全没有提及**或**严重缺乏记录**:
|
||||
|
||||
### 3.1 已安装但文档完全未记录
|
||||
|
||||
| 技术 | 位置 | 用途推断 | 文档缺位程度 | 建议 |
|
||||
|------|------|---------|------------|------|
|
||||
| **APScheduler** | requirements.txt | 定时调度 | **严重**。只在 spec/plan 的 `timedelta import 修复` 中一笔带过,无任何架构说明 | 新增架构决策文档,明确 APScheduler vs asyncio loop vs cron 的职责边界 |
|
||||
| **Jinja2** | requirements.txt | 模板渲染 | **完全未记录**。当前所有前端是 PHP 页面,Jinja2 用途不明(可能用于邮件模板或 API 文档) | 记录使用场景 |
|
||||
| **httpx** | requirements.txt | HTTP 客户端 | **轻微缺失**。代码中使用但文档未提及 | — |
|
||||
| **aiofiles** | requirements.txt | 异步文件 IO | **完全未记录** | 记录使用场景 |
|
||||
| **python-multipart** | requirements.txt | 表单解析 | **完全未记录** | 记录使用场景 |
|
||||
| **PyYAML** | requirements.txt | YAML 解析 | **完全未记录**。架构优化已确认迁移到 JSON,但依赖未移除 | 考虑移除或记录用途 |
|
||||
| **Rich** | requirements.txt | CLI 美化 | **完全未记录** | 记录使用场景 |
|
||||
|
||||
### 3.2 关键配置项文档缺位
|
||||
|
||||
| 配置项 | 位置 | 状态 | 问题 |
|
||||
|--------|------|------|------|
|
||||
| `MULTISYNC_MODE` | .env.example | **未迁移** | 属性是 `ssh_direct/agent/hybrid`,但 config.py 中完全未定义此模式字段。概念存在但代码层面消失 |
|
||||
| `MULTISYNC_LOG_DIR` | .env.example | **未迁移** | config.py 无对应属性,日志路径未集中管理 |
|
||||
| `MULTISYNC_BACKUP_DIR` | .env.example | **未迁移** | 同上,备份路径未定义 |
|
||||
| `MULTISYNC_CORS_ORIGINS` | .env.example | **未迁移** | config.py 无 CORS 配置属性 |
|
||||
| `MULTISYNC_ADMIN_USERNAME/PASSWORD` | .env.example | **未迁移** | 初始管理员凭据 |
|
||||
| `MULTISYNC_MAX_CONCURRENT_PUSHES` | .env.example | **未迁移** | Sprint 中提到底层用了 `asyncio.Semaphore`,推送上限是 600,但 config.py 没有此配置 |
|
||||
| `HEALTH_CHECK_TIMEOUT` | .env.example | **未迁移** | config.py 只有 `HEALTH_CHECK_INTERVAL` 没有 `HEALTH_CHECK_TIMEOUT` |
|
||||
| **Redis key 设计规范** | 多处 spec | **分散记录** | Redis key 在多个 spec 中零散定义(`heartbeat:{id}`, `install:{id}:log`, `alert:{id}:{key}` 等),无统一参考文档 |
|
||||
|
||||
### 3.3 环境变量前缀不一致
|
||||
|
||||
| 问题 | 详情 | 影响 |
|
||||
|------|------|------|
|
||||
| `.env.example` 使用 `MULTISYNC_` 前缀 | `MULTISYNC_HOST`, `MULTISYNC_PORT`... | **与 config.py 的 `NEXUS_` 前缀不一致**。config.py:63 定义 `env_prefix="NEXUS_"`,但 .env.example 仍是旧项目名 `MULTISYNC_`。用户复制 .env.example 到 .env 后将读不到任何配置 |
|
||||
| 文档中混用 | 有些文档写 `NEXUS_`,有些写 `MULTISYNC_` | 部署和排障时造成混淆 |
|
||||
|
||||
---
|
||||
|
||||
## 四、建议调整的技术选型
|
||||
|
||||
### 4.1 紧急:aiomysql → asyncmy(或 aiomysql 升级监控)
|
||||
|
||||
**现状**: `aiomysql==0.2.0`。该库基于 `pymysql`(纯 Python),0.2.0 版本较旧,已知存在连接池问题。
|
||||
|
||||
**建议**:
|
||||
- 短期:锁定版本并增加连接池监控。目前池配置 `pool=400 overflow=300` 极端激进(MySQL 默认 `max_connections=151`),必须在 DB 侧同步调整。
|
||||
- 中期:评估迁移到 `asyncmy`(基于 `pymysql` 但 C 扩展加速)或 `mysql-connector-python` 异步驱动。
|
||||
|
||||
**严重度**: **中高** — 400+300 的连接池配置可能远超 MySQL 承受能力,是潜在的稳定性风险。
|
||||
|
||||
### 4.2 建议:统一 SSH 库为 asyncssh
|
||||
|
||||
**现状**: 两个 SSH 库并存 —— `paramiko==3.5.0`(同步,当前主力)和 `asyncssh==2.17.0`(异步,未使用)。
|
||||
|
||||
**建议**:
|
||||
- Sprint 中已确认 `paramiko` 阻塞事件循环("47s health 响应"),用 ThreadPoolExecutor 缓解。这是治标。
|
||||
- 按 roadmap ADR-002 方向,用 `asyncssh` 完全替代 `paramiko`,消除线程池开销。
|
||||
- 这是一项重构(估计 2-3 天),但可消除当前 50 线程的 ThreadPoolExecutor + Semaphore 复杂度。
|
||||
|
||||
**严重度**: **中** — 当前方案能工作,但架构不优雅。
|
||||
|
||||
### 4.3 警告:连接池配置与 MySQL 容量不匹配
|
||||
|
||||
**现状**:
|
||||
```
|
||||
DB: pool=400 overflow=300 → 最大 700 连接
|
||||
Redis: max_connections=1000
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- MySQL 8.4 默认 `max_connections=151`。700 连接需要 MySQL 侧设置 `max_connections=1000+`。如果未设置,连接请求将在 MySQL 侧排队或拒绝。
|
||||
- `pool=400` 远超常见推荐值(CPU 核心数 * 2 + 有效磁盘数)。大量空闲连接也会消耗 MySQL 内存。
|
||||
- Sprint 规划中注明了"DB 池终版"但没有附加 MySQL 配置说明。
|
||||
|
||||
**建议**: 在部署文档中显式写明月度该配置。添加连接池使用率告警。考虑改用 `pool_size=50, max_overflow=50` 并用实际压测结果调优。
|
||||
|
||||
**严重度**: **高** — 当前配置可能在 2000 台服务器承载下触发 MySQL 连接瓶颈。
|
||||
|
||||
### 4.4 建议:APScheduler → 轻量级方案重评估
|
||||
|
||||
**现状**: `apscheduler==3.11.0` 已安装使用,但 Sprint 记录显示存在 `timedelta` import bug。APScheduler 3.x 的异步支持通过 `AsyncIOScheduler` 实现,可靠但配置复杂。
|
||||
|
||||
**建议**:
|
||||
- 评估是否可以用 `asyncio` 原生 loop + Redis 分布式锁替代 APScheduler。Nexus 当前的后台任务(retry_loop、heartbeat_flush、self_monitor)都是简单 while-sleep 模式,不需要 Cron 表达式支持。
|
||||
- 如果保留 APScheduler,需升级到 3.11+(当前已满足)并关注 4.0 异步原生支持。
|
||||
|
||||
**严重度**: **低** — 当前方案可用,但有已知 bug。
|
||||
|
||||
### 4.5 建议:环境变量前缀统一
|
||||
|
||||
**现状**: `.env.example` 使用 `MULTISYNC_` 前缀,`config.py` 定义 `env_prefix="NEXUS_"`。
|
||||
|
||||
**建议**: 统一为 `NEXUS_`。当前不一致意味着用户复制 `.env.example` 后无法直接使用,必须手动修改变量名。这是一个部署陷阱。
|
||||
|
||||
**严重度**: **中** — 首次部署必然踩坑。
|
||||
|
||||
### 4.6 建议:冗余依赖清理
|
||||
|
||||
| 依赖 | 现状 | 建议 |
|
||||
|------|------|------|
|
||||
| `pyyaml==6.0.2` | Agent 配置已从 YAML 迁移到 JSON | **移除** |
|
||||
| `jinja2==3.1.5` | 无明确使用场景 | 确认用途或标注 future use |
|
||||
| `rich==13.9.4` | 无明确使用场景 | 确认用途或移除 |
|
||||
| `paramiko==3.5.0` | asyncssh 是替代方向 | 迁移后移除 |
|
||||
|
||||
### 4.7 建议:PHP/Python 双数据库连接
|
||||
|
||||
**现状**: PHP 通过 `config.php` 直连 MySQL,Python 通过 `DATABASE_URL` 连接同一 MySQL。
|
||||
|
||||
**风险**: PHP 和 Python 使用独立的连接池,无法统一控制。php-fpm 的每个 worker 维护独立 MySQL 连接。当 PHP 前端页面被大量打开时,连接数可能翻倍。
|
||||
|
||||
**建议**: roadmap 已规划"全面弃用 AdminLTE,纯静态 HTML + JS 调 FastAPI API",这是正确的方向。建议加速 PHP→API 迁移,减少对 PHP 直连 MySQL 的依赖。
|
||||
|
||||
### 4.8 文件管理器双重编辑器
|
||||
|
||||
**现状**: 文件管理器 v2 引入 Monaco Editor (CDN),但 code-review-followup spec 提到 ACE Editor 已本地化为 `web/ace/ace.js`(444KB)。
|
||||
|
||||
**问题**: 为什么同时维护两个编辑器?Monaco 功能更强但体积更大,ACE 更轻量但已本地化。需要确认是用 Monaco 替代 ACE,还是两者并存用于不同场景。
|
||||
|
||||
**建议**: 统一为一个编辑器,建议保留 Monaco(功能更完整),移除 ACE。
|
||||
|
||||
---
|
||||
|
||||
## 五、竞品对比结论
|
||||
|
||||
### 5.1 与 JumpServer 的技术选型对比
|
||||
|
||||
| 维度 | Nexus | JumpServer | 评价 |
|
||||
|------|-------|-----------|------|
|
||||
| **后端框架** | FastAPI (异步) | Django (同步+Channels) | Nexus 在异步原生支持上领先,但 JumpServer 的生态(插件、管理后台)更成熟 |
|
||||
| **ORM** | SQLAlchemy 2.0 Async | Django ORM | SQLAlchemy 更灵活,但 JumpServer ORM 的 `F()` 表达式和 `annotate` 在复杂查询中更便捷 |
|
||||
| **SSH 代理** | 内嵌 (FastAPI 进程) | 独立 Koko (Go 二进制) | JumpServer 的分离架构更健壮,但 Nexus 的部署更简单。2000 台规模时 Nexus 可能需拆分 |
|
||||
| **WebSocket** | FastAPI 原生 | Django Channels | Nexus 更简洁,无额外依赖 |
|
||||
| **前端** | PHP + Tailwind (过渡中) | Vue 3 + Element Plus | JumpServer 前端现代化程度更高 |
|
||||
| **协议支持** | SSH 为主 | SSH/RDP/VNC/Telnet/MySQL/K8s | JumpServer 覆盖更广 |
|
||||
| **录像审计** | 无 | asciicast v2 + S3/MinIO | Nexus 明确否决(roadmap 已否决) |
|
||||
| **MFA** | TOTP (规划中) | TOTP/LDAP/CAS/OIDC/SAML2/OAuth2/RADIUS | JumpServer 认证体系极为全面 |
|
||||
| **RBAC** | 鉴权即信任 | 四层权限 (RBAC+AssetPermission+ACL) | Nexus 故意简化,适合运维团队内部使用场景 |
|
||||
|
||||
### 5.2 与 EasyNode 的技术选型对比
|
||||
|
||||
| 维度 | Nexus | EasyNode | 评价 |
|
||||
|------|-------|---------|------|
|
||||
| **后端** | Python FastAPI | Node.js Koa | Nexus 在类型安全和性能上更优 |
|
||||
| **前端** | PHP (过渡) | Vue 3 | EasyNode 前端现代化 |
|
||||
| **WebSocket** | FastAPI 原生 | Node.js 原生 | 架构相似 |
|
||||
| **数据存储** | MySQL + Redis | NeDB (文件数据库) | Nexus 存储方案更健壮,适合 2000+ 规模 |
|
||||
| **SSH** | paramiko + asyncssh | ssh2 npm | EasyNode 的 Node.js 生态在 SSH 异步上天然优势 |
|
||||
|
||||
### 5.3 竞品分析采纳状态
|
||||
|
||||
竞品分析报告(`jumpserver-easynode-architecture-analysis.md`)提出了 5 个 ADR 建议,采纳情况如下:
|
||||
|
||||
| ADR | 内容 | Roadmap 采纳 | 实际落地 | 评价 |
|
||||
|-----|------|-------------|---------|------|
|
||||
| ADR-001 | Platform 模板 + 单表 Asset + JSONB | **已采纳**(roadmap N1) | 未落地 | 正确采纳,规划合理 |
|
||||
| ADR-002 | BFF 模式内嵌 SSH 代理 (asyncssh) | **已采纳**(roadmap N2) | 未落地 | 正确采纳,但应加速 |
|
||||
| ADR-003 | asciicast v2 会话录像 | **已否决**(roadmap §已否决) | N/A | 合理,Nexus 定位不需要录像 |
|
||||
| ADR-004 | RBAC + AssetPermission 两层权限 | **已否决**(roadmap §已否决) | N/A | 合理,保持"鉴权即信任" |
|
||||
| ADR-005 | xterm.js + Koko 协议 | **已采纳**(roadmap N2) | 未落地 | 正确采纳,应加速 |
|
||||
|
||||
**结论**: 设计部已合理筛选竞品分析建议,但落地速度需要加快。N1(资产模型)和 N2(Web SSH)是差异化核心功能,但当前进展为零。
|
||||
|
||||
### 5.4 竞品关键启示 — Nexus 当前忽略的部分
|
||||
|
||||
竞品分析中包含但 Nexus 未响应的关键技术启示:
|
||||
|
||||
1. **Connect Token 机制** — JumpServer 最精华的安全设计之一,短生命周期一次性连接令牌。Nexus 的 "鉴权即信任" 在直连场景够用,但引入 Web SSH 时会话级安全不可忽视。
|
||||
2. **资产授权链缓存** — JumpServer 通过 Redis 三级缓存资产授权关系。Nexus 2000+ 服务器规模下,每次查询都扫描所有授权关系可能成为瓶颈。
|
||||
3. **命令过滤 ACL** — roadmap 已否决,但作为 2000+ 服务器的运维平台,高危命令防护(`rm -rf /`、`DROP TABLE`)有实际业务价值。
|
||||
4. **SSH 连接复用** — Koko 的引用计数连接池模式。Nexus 当前每个操作建立新 SSH 连接,2000+ 规模下 SSH 握手延迟(TCP + 密钥交换,通常 1-3s)会显著累加。
|
||||
|
||||
---
|
||||
|
||||
## 六、风险评估矩阵
|
||||
|
||||
| 风险 | 严重度 | 发生概率 | 影响 | 建议行动 |
|
||||
|------|--------|---------|------|---------|
|
||||
| aiomysql 连接池不稳定 (pool=400/overflow=300) | **高** | **中** | 数据库连接耗尽 → 服务不可用 | 调低池配置,增加 MySQL `max_connections` 监控告警 |
|
||||
| .env.example 前缀 MULTISYNC_ vs config.py NEXUS_ 不一致 | **中** | **高** | 首次部署配置全部失效 | 统一前缀为 NEXUS_ |
|
||||
| 双 SSH 库 (paramiko + asyncssh) 并存 | **低** | **高** | 技术债务累积,维护混淆 | 制定 asyncssh 迁移计划 |
|
||||
| PHP/Python 双数据库直连 | **中** | **高** | 连接数翻倍,不可控 | 加速 PHP→API 迁移 |
|
||||
| Redis 文档缺位 — key 设计分散在多个 spec | **中** | **高** | 新开发者难以理解数据流 | 创建 Redis key 设计规范文档 |
|
||||
| APScheduler 异步兼容性 | **低** | **中** | 后台任务可能异常 | 监控或迁移到 asyncio loop |
|
||||
| Agent 配置 YAML→JSON 过渡未完成 | **低** | **中** | pyyaml 依赖残留 | 清理 pyyaml |
|
||||
| 环境变量 `HEALTH_CHECK_TIMEOUT` 和 `MAX_CONCURRENT_PUSHES` 从 config.py 缺失 | **中** | **高** | 这些配置生效但无统一管理入口 | 补全到 config.py Settings 类 |
|
||||
|
||||
---
|
||||
|
||||
## 七、总结
|
||||
|
||||
### 强项
|
||||
1. **FastAPI + SQLAlchemy 2.0 Async** 是 2026 年 Python 后端的最优组合
|
||||
2. **Redis 作为实时数据层** 的设计思路正确(心跳、告警冷却、Pub/Sub 进度推送)
|
||||
3. **3 层守护机制** (Supervisor + Python 自检 + Shell) 覆盖全面
|
||||
4. **竞品分析质量高**,ADR 筛选决策合理
|
||||
5. **WebSocket 实时推送 + Telegram 双通道告警** 架构完整
|
||||
|
||||
### 短板
|
||||
1. **核心文档缺位**: Redis key 设计无集中文档,多个 spec 零散定义。环境变量前缀不统一是部署陷阱。
|
||||
2. **连接池配置激进**: 400+300 的 pool 配置结合 aiomysql 的稳定性,是当前最大的潜在生产风险。
|
||||
3. **双 SSH 库** 增加维护成本,paramiko 阻塞事件循环的架构问题未根治。
|
||||
4. **前端技术栈过渡期**: PHP AdminLTE + Tailwind CSS v4 并存,短期增加维护负担。AC 编辑器/Monaco 编辑器双重存在。
|
||||
5. **Python 依赖中有 20% 无明确使用场景记录**: pyyaml、jinja2、rich 等需要确认用途或移除。
|
||||
6. **.env.example 与 config.py 不对齐**: 多个配置项(CORS、MODE、LOG_DIR、BACKUP_DIR、MAX_CONCURRENT_PUSHES、TIMEOUT)在 .env.example 中存在但 config.py 缺失。
|
||||
7. **Roadmap 规划的资产模型和 Web SSH 零进度**: 核心差异化功能仍停留在 ADR 阶段,这是时间管理风险而非技术风险。
|
||||
|
||||
### 立即行动项 (按优先级)
|
||||
1. **紧急**: 验证 MySQL `max_connections` 是否匹配 700 的连接池上限,调低池配置
|
||||
2. **紧急**: 修复 .env.example 前缀 `MULTISYNC_` → `NEXUS_`
|
||||
3. **高**: 补全 config.py Settings 类缺失的配置属性
|
||||
4. **高**: 创建 Redis key 设计规范统一文档
|
||||
5. **中**: 启动 asyncssh 迁移替代 paramiko
|
||||
6. **中**: 清理 pyyaml、确认 jinja2/rich 用途
|
||||
7. **中**: 统一文件管理器编辑器 (Monaco vs ACE)
|
||||
8. **低**: APScheduler 替换评估
|
||||
@@ -1,13 +1,132 @@
|
||||
# Nexus 6.0 关键文件清单
|
||||
# Nexus 6.0 关键文件清单 (2026-05-21 更新)
|
||||
|
||||
server/main.py: FastAPI+lifespan+CORS+Telegram关闭. server/config.py: Settings+DB override+CORS_ORIGINS. server/api/agent.py: 心跳+命令注入防护. server/api/auth.py: Login/logout/TOTP. server/api/websocket.py: /ws/alerts广播. auth_service.py: Redis session store. server_service.py: Agent健康检查(httpx+Semaphore20)+push委托. sync_service.py: 批量推送引擎. redis/client.py: Redis异步客户端(max_connections从settings读). telegram/__init__.py: httpx单例. ssh/pool.py: SSH连接池. encryption.py: Fernet+AES双格式. heartbeat_flush.py: Redis→MySQL 10min(TTL=900s). self_monitor.py: Python自检30s. web/app.js: Alert WebSocket+浮动卡片. web/style.css: Neumorphism+告警卡片. web/install.php: 5步安装向导. deploy/nginx_172.31.170.47.conf: WSL Nginx. deploy/nexus.conf: Supervisor. deploy/health_monitor.sh: Shell守护. deploy/migrations/001_indexes.sql: 7个索引. tests/conftest.py: pytest+async fixtures.
|
||||
## 后端 (server/)
|
||||
|
||||
### 入口与配置
|
||||
- `server/main.py` — FastAPI 应用入口,lifespan,CORS,后台任务
|
||||
- `server/config.py` — Settings + DB override + CORS_ORIGINS
|
||||
- `server/domain/models/__init__.py` — SQLAlchemy 模型(10表)
|
||||
|
||||
### API 层
|
||||
- `server/api/agent.py` — Agent心跳接收 + 命令注入防护(BLOCKED_COMMAND_PATTERNS)
|
||||
- `server/api/auth.py` — Login/logout/TOTP(旧)
|
||||
- `server/api/auth_jwt.py` — JWT认证(新)
|
||||
- `server/api/websocket.py` — `/ws/alerts` 告警广播
|
||||
- `server/api/servers.py` — 服务器CRUD + Redis实时状态
|
||||
- `server/api/settings.py` — 动态配置读写
|
||||
- `server/api/health.py` — `/health` 端点(3层守护用)
|
||||
- `server/api/scripts.py` — 脚本执行
|
||||
- `server/api/dependencies.py` — FastAPI 依赖注入
|
||||
|
||||
### 应用服务层
|
||||
- `server/application/services/auth_service.py` — Redis session store(session:{token} 8h滑动TTL)
|
||||
- `server/application/services/server_service.py` — Agent健康检查(httpx+Semaphore20)+ push委托
|
||||
- `server/application/services/sync_service.py` — 批量推送引擎
|
||||
- `server/application/services/script_service.py` — 脚本执行服务
|
||||
|
||||
### 后台任务
|
||||
- `server/background/heartbeat_flush.py` — Redis→MySQL 10min批量(TTL=900s)
|
||||
- `server/background/self_monitor.py` — Python自检 30s循环
|
||||
|
||||
### 基础设施
|
||||
- `server/infrastructure/redis/client.py` — Redis异步客户端(max_connections从settings读)
|
||||
- `server/infrastructure/ssh/pool.py` — SSH连接池
|
||||
- `server/infrastructure/telegram/__init__.py` — Telegram推送(httpx模块级单例)
|
||||
- `server/infrastructure/database/crypto.py` — Fernet+AES双格式加密
|
||||
- `server/infrastructure/database/session.py` — SQLAlchemy异步session
|
||||
- `server/infrastructure/database/server_repo.py` + `setting_repo.py` + `admin_repo.py` + 等 — Repository层
|
||||
|
||||
---
|
||||
|
||||
## 前端 (web/) — PHP待替换
|
||||
|
||||
### 当前PHP文件(将被Tailwind CSS v4 + 静态HTML/JS替换)
|
||||
- `web/install.php` — 5步安装向导
|
||||
- `web/login.php` / `web/logout.php` / `web/totp.php` — 认证
|
||||
- `web/index.php` — Dashboard
|
||||
- `web/servers.php` — 服务器列表
|
||||
- `web/files.php` / `web/fm.php` / `web/tinyfm.php` / `web/webfm.php` — 文件管理
|
||||
- `web/settings.php` / `web/config.php` — 配置
|
||||
- `web/push.php` / `web/retry.php` / `web/schedules.php` — 推送
|
||||
- `web/audit.php` / `web/logs.php` — 审计日志
|
||||
- `web/scripts.php` / `web/deploy.php` — 脚本与部署
|
||||
- `web/db.php` / `web/api_client.php` / `web/api_proxy.php` — 数据层
|
||||
- `web/app.js` / `web/style.css` — 前端资源
|
||||
- `web/ace/` — ACE编辑器
|
||||
- `web/agent/agent.py` / `agent.sh` / `install.sh` — Agent安装脚本
|
||||
|
||||
---
|
||||
|
||||
## 部署 (deploy/)
|
||||
- `deploy/nexus.conf` — Supervisor配置(uvicorn 4 workers)
|
||||
- `deploy/health_monitor.sh` — Shell守护(cron 1min,3次失败→重启+Telegram)
|
||||
- `deploy/nginx_172.31.170.47.conf` — WSL Nginx配置
|
||||
- `deploy/migrations/001_indexes.sql` — 7个数据库索引
|
||||
|
||||
---
|
||||
|
||||
## 文档 (docs/)
|
||||
### 项目顶层
|
||||
- `docs/project/status.md` — 项目进度总览 + 数据库结构 + 迭代计划
|
||||
- `docs/project/roadmap.md` — 功能规划 + ADR决策 + 实现顺序
|
||||
- `docs/project/deploy.md` — 部署指南
|
||||
- `docs/CLAUDE.md` — 项目记忆(仓库根目录)
|
||||
|
||||
### 部门协作
|
||||
- `docs/team/collaboration-charter.md` — 部门协作章程(5阶段流水线)
|
||||
- `docs/team/` (8个部门文档) — 管理组/工程部/测试部/产品部/设计部/CTO/运维/HR/制片人
|
||||
|
||||
### 技能定义
|
||||
- `docs/skills/` — 194个技能文件(各部门技能定义)
|
||||
|
||||
### 设计规格与实施计划
|
||||
- `docs/design/specs/` — 10个设计规格(推送/密码/重试/文件管理器/架构/等)
|
||||
- `docs/design/plans/` — 5个实施计划
|
||||
|
||||
### 竞品分析
|
||||
- `docs/research/jumpserver-easynode-architecture-analysis.md` — JumpServer & EasyNode架构分析
|
||||
|
||||
### 项目记忆
|
||||
- `docs/memory/MEMORY.md` — 记忆索引
|
||||
- `docs/memory/mem_*.md` — 17个记忆文件(概述/决策/数据流/守护/模块/测试/等)
|
||||
|
||||
### 修复文档
|
||||
- `docs/changelog/fix-plan-2026-05-20.md` — 代码问题修复计划(13项)
|
||||
- `docs/changelog/fix-plan-full-2026-05-20.md` — 全量未完成项修复计划(24项)
|
||||
- `docs/changelog/fix-verification-report-2026-05-20.md` — 修复验证报告
|
||||
|
||||
---
|
||||
|
||||
## 测试 (tests/)
|
||||
- `tests/conftest.py` — pytest + async fixtures
|
||||
- `tests/test_api.py` — API测试
|
||||
- `tests/load_test.py` + `tests/quick_load.py` — 负载测试
|
||||
|
||||
---
|
||||
|
||||
## MCP服务 (mcp/)
|
||||
- `mcp/Nexus_server.py` — Nexus远程MCP服务
|
||||
- `mcp/mcp_bridge.py` — MCP桥接
|
||||
- `mcp/firefox_server.py` — Firefox自动化MCP
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
- `.env.example` — 环境变量模板
|
||||
- `requirements.txt` — Python依赖
|
||||
- `.gitea/workflows/ci-cd.yml` + `pre-commit.yml` — CI/CD流水线
|
||||
|
||||
---
|
||||
|
||||
## 相关概念
|
||||
|
||||
- Nexus
|
||||
- files
|
||||
- reference
|
||||
- architecture
|
||||
- docs
|
||||
- skills
|
||||
- deployment
|
||||
- testing
|
||||
|
||||
---
|
||||
*源: agentmemory (mem_key_files)*
|
||||
*更新: 2026-05-21 全面整理*
|
||||
|
||||
@@ -413,7 +413,13 @@ server {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# PHP 页面(前端)
|
||||
# Nexus 新前端(Tailwind CSS v4)
|
||||
location /app/ {
|
||||
root /www/wwwroot/api.synaglobal.vip;
|
||||
try_files $uri $uri/ /app/login.html;
|
||||
}
|
||||
|
||||
# PHP 页面(旧前端,逐步弃用)
|
||||
location / {
|
||||
root /www/wwwroot/api.synaglobal.vip;
|
||||
index index.php;
|
||||
|
||||
+213
-152
@@ -1,207 +1,268 @@
|
||||
# Nexus 项目建议书
|
||||
|
||||
> 更新: 2026-05-21
|
||||
|
||||
---
|
||||
|
||||
## 一、项目现状
|
||||
|
||||
```
|
||||
分支: claude/charming-babbage-4dbd0e
|
||||
最后提交: 540f33b...0f4a1d6
|
||||
总变更: 44 files, ~3200 insertions, ~2100 deletions
|
||||
完成度: ~97%
|
||||
```
|
||||
|
||||
### 已完成迭代
|
||||
|
||||
| 迭代 | 内容 | 项数 |
|
||||
|------|------|------|
|
||||
| 1 | 安全加固 + 架构统一 | 14 项 (P0-P2) |
|
||||
| 2 | 简化推送模型 | SyncTask 删除,Server.target_path |
|
||||
| 3 | 密码预设 + 推送重试 | PasswordPreset, retry_engine |
|
||||
| 4 | 实时进度 + 打磨 | WebSocket 进度, agent 清理 |
|
||||
| 5 | 稳定性 + 测试 | e2e test, agent 端点清理, Telegram 配置 |
|
||||
| 6 | 功能增强 9/9 | 并发控制, 图表, 导入导出, 定时调度, 告警, 移动端, 审计 |
|
||||
| 7 | 项目完善 | .env 补全, 系统信息, 日志清理 |
|
||||
| 8 | 配置统一 + 分发 | MySQL 配置中心, 主推配置到 Agent |
|
||||
|
||||
### 架构总览
|
||||
### 运行状态
|
||||
|
||||
```
|
||||
部署: PHP install.php → MySQL 建表 → write config.php
|
||||
↓
|
||||
配置: PHP settings 页 → MySQL settings 表
|
||||
↓
|
||||
运行: Python 启动 → .env (仅 DB 连接) → load_settings_from_db()
|
||||
↓
|
||||
推送: servers.php → POST /api/servers/push → rsync → WebSocket 进度
|
||||
↓
|
||||
重试: retry_engine → 60s 扫描 → 5min 重试 → dry-run 验证 → 消失
|
||||
↓
|
||||
告警: 重试≥3次 → Telegram Bot 通知
|
||||
↓
|
||||
调度: scheduler_loop → cron 匹配 → 定时推送
|
||||
↓
|
||||
IP 变更: settings.php → POST push-config → Agent /config/reload → 重连
|
||||
生产服务器: 141/253 在线
|
||||
技术栈: Python 3.12 / FastAPI / MySQL 8.4 / Redis 7 / Nginx
|
||||
前端: PHP AdminLTE(旧)+ Tailwind CSS v4 + Alpine.js(新,后端完成后全量迁移)
|
||||
数据库: 10 张表(servers, admins, sync_logs, scripts 等)
|
||||
核心功能: rsync 文件推送 + WebSocket 进度 + 重试引擎 + Telegram 告警
|
||||
```
|
||||
|
||||
### 数据库现有表
|
||||
|
||||
```
|
||||
servers (253台) 核心:id, name, domain, port, username, auth_method,
|
||||
password(加密), ssh_key_*(3), agent_port, agent_api_key,
|
||||
category, is_online, system_info
|
||||
|
||||
admins 认证:username, password_hash, totp_secret, totp_enabled
|
||||
|
||||
settings 配置:key → value
|
||||
|
||||
sync_logs 推送日志:server_id, source/target_path, status, files_*
|
||||
push_schedules 定时推送:cron_expr, server_ids(JSON)
|
||||
push_retry_jobs 重试队列:retry_count, max_retries
|
||||
password_presets 密码预设:name, encrypted_pw
|
||||
db_credentials 数据库凭据:db_type, host, encrypted_password
|
||||
|
||||
scripts 脚本库:name, category, content
|
||||
script_executions 执行记录:script_id, command, server_ids(JSON), results(JSON)
|
||||
|
||||
audit_logs 审计:admin_username, action, target_type, detail
|
||||
login_attempts 防暴力:username, ip_address, success
|
||||
```
|
||||
|
||||
### 已完成迭代 (1-5)
|
||||
|
||||
| 迭代 | 内容 |
|
||||
|------|------|
|
||||
| 1 | 安全加固 + 架构统一 |
|
||||
| 2 | 简化推送模型 |
|
||||
| 3 | 密码预设 + 推送重试 |
|
||||
| 4 | 实时进度 + WebSocket |
|
||||
| 5 | 稳定性 + 全量修复 P0-P3 |
|
||||
|
||||
---
|
||||
|
||||
## 二、安全性建议
|
||||
## 二、功能规划 (2026-05-21)
|
||||
|
||||
### 已修复
|
||||
> 竞品分析:`docs/research/jumpserver-easynode-architecture-analysis.md`
|
||||
|
||||
| # | 问题 | 状态 |
|
||||
|---|------|------|
|
||||
| - | SSH 密码明文存储 | ✅ Fernet + AES 加密 |
|
||||
| - | API 无认证 | ✅ X-API-Key 中间件 |
|
||||
| - | CORS 全开 | ✅ 可配置域名 |
|
||||
| - | 默认凭据 | ✅ 启动拒绝 + 警告 |
|
||||
| - | TOTP 密钥 session 泄露 | ✅ 加密存储 |
|
||||
| - | 密码注入 (单引号) | ✅ shlex.quote() |
|
||||
| - | TOTP 密码 HTML 回传 | ✅ session 暂存 |
|
||||
| - | 默认 SECRET_KEY | ✅ 启动硬拒绝 |
|
||||
| - | 加密密钥不一致 | ✅ 统一 API_KEY 派生 |
|
||||
### 新增功能清单
|
||||
|
||||
### 建议改进
|
||||
| # | 功能 | 与现有关系 | 说明 | 预估 |
|
||||
|---|------|-----------|------|------|
|
||||
| N0 | Redis 加固 | **新增** 基础设施 | BlockingConnectionPool + 启动强校验 + Redis Pub/Sub | 1 周 |
|
||||
| N1 | 资产模型 | **扩展** servers 表 | 新建 platforms/nodes 表,servers 加字段 | 2-3 周 |
|
||||
| N2 | Web SSH 终端 | **复用** ssh_direct 底层 | asyncssh 连接池 + xterm.js + /ws/terminal/{token} | 3-4 周 |
|
||||
| N3 | Sync 引擎 | **升级** rsync 推送 | 文件+命令+配置统一同步,含 SFTP | 2 周 |
|
||||
| N4 | JWT 登录 + MFA | **全新替换** admins | Tailwind CSS v4 登录页(已完工),JWT + TOTP | 1 周 |
|
||||
| N5 | 命令日志 | **新建** command_logs 表 | SSH stdin 按行切,纯文本记录 | 1 周 |
|
||||
| N6 | SSH 会话 | **新建** ssh_sessions 表 | Web SSH 连接生命周期管理 | 含在 N2 |
|
||||
| N7 | 前端全量迁移 | **替换** 25+ PHP 页面 | Tailwind CSS v4 + Alpine.js,一次性全量迁移 | 3-4 周 |
|
||||
|
||||
| # | 建议 | 改动 | 风险 |
|
||||
|---|------|------|------|
|
||||
| S1 | open_basedir 限制 PHP | Nginx 已配,确认生效 | 无 |
|
||||
| S2 | config.php 权限 600 | `chmod 600 web/data/config.php` | 无 |
|
||||
| S3 | install.php 部署后删除 | 部署文档加提醒 | 低 |
|
||||
| S4 | session.save_path 私密化 | 宝塔 PHP → 设为项目专属目录 | 低 |
|
||||
| S5 | WS 集合并发保护 | +5 行 asyncio.Lock | 无 |
|
||||
### 已有可直接用的功能
|
||||
|
||||
| 功能 | 现有表 | 状态 |
|
||||
|------|--------|------|
|
||||
| 凭据托管 | password_presets + db_credentials | ✅ 已有,直接用 |
|
||||
| 脚本库 | scripts + script_executions | ✅ 已有,直接用 |
|
||||
| 审计日志 | audit_logs | ✅ 已有,直接用 |
|
||||
| 防暴力 | login_attempts | ✅ 已有,直接用 |
|
||||
|
||||
### 已否决
|
||||
|
||||
| 功能 | 原因 |
|
||||
|------|------|
|
||||
| 会话录像 asciicast | 不需要 |
|
||||
| RBAC 权限体系 | 保持"鉴权即信任"模型 |
|
||||
| 推送结果邮件报告 | 不需要 |
|
||||
|
||||
---
|
||||
|
||||
## 三、功能建议
|
||||
## 三、冲突/重复处理
|
||||
|
||||
### 优先实施 (P1)
|
||||
|
||||
| # | 功能 | 说明 | 预估 |
|
||||
|---|------|------|------|
|
||||
| F1 | 类型转换修复 | load_settings_from_db 读出的字符串转 int/bool | 8 行 |
|
||||
| F2 | 路径穿越防护 | api_proxy.php 文件管理加 realpath 白名单 | 10 行 |
|
||||
| F3 | install_sh 模块名 | deploy/install_central.sh 中 `server.database` | 1 行 |
|
||||
| F4 | 测试加清理 | tests/test_api.py 加 DELETE 清理 | 3 行 |
|
||||
|
||||
### 可选 (P2)
|
||||
|
||||
| # | 功能 | 说明 | 预估 |
|
||||
|---|------|------|------|
|
||||
| F5 | 推送前预览 dry-run | 推送对话框显示预计文件数和大小 | 40 行 |
|
||||
| F6 | 首次使用引导 | install.php 完成 → 3 步引导 | 50 行 |
|
||||
| F7 | settings 页 tab 化 | TOTP/密码/预设/配置 分 tab | 30 行 |
|
||||
| F8 | favicon + 品牌 | logo + favicon.ico | 静态资源 |
|
||||
| F9 | README 重写 | 架构图 + 快速开始 + 截图 | 60 行 |
|
||||
|
||||
### 运维优化 (P3)
|
||||
|
||||
| # | 功能 | 说明 | 预估 |
|
||||
|---|------|------|------|
|
||||
| F10 | 推送结果邮件报告 | 可选,替代/补充 Telegram | 30 行 |
|
||||
| F11 | 数据库连接池监控 | settings 页显示 MySQL 连接数 | 20 行 |
|
||||
| F12 | 子服务器 Agent 版本检测 | 心跳上报 agent 版本,仪表盘显示 | 15 行 |
|
||||
| F13 | 推送带宽限制 | rsync --bwlimit 可配 | 5 行 |
|
||||
| 冲突点 | 处理方式 |
|
||||
|--------|---------|
|
||||
| rsync 推送 ↔ Sync 引擎 | 升级关系,原有 `/api/servers/push` 保留 |
|
||||
| WebFM ↔ SFTP | 互补:WebFM 管 Nexus 本机 `/www/wwwroot/`,SFTP 管远程服务器 |
|
||||
| ssh_direct ↔ Web SSH | 共用底层 asyncssh 连接池模块(提取公共代码) |
|
||||
| 现有 TOTP ↔ JWT+MFA | 全新替换,不走旧 TOTP 逻辑 |
|
||||
| AuditLog ↔ 命令日志 | 互补:AuditLog 记 API 操作,命令日志记 SSH 终端命令 |
|
||||
| Agent 心跳 ↔ Asset 在线 | 直接合并:`is_online` → `connectivity` |
|
||||
| 现有凭据 ↔ 新凭据托管 | 已有 `password_presets` + `db_credentials`,直接用 |
|
||||
|
||||
---
|
||||
|
||||
## 四、运维建议
|
||||
## 四、实现顺序
|
||||
|
||||
### 部署检查清单
|
||||
### 第零步:基础设施加固(Redis + WebSocket)
|
||||
|
||||
```
|
||||
[ ] SECRET_KEY 已设为随机值 (openssl rand -hex 32)
|
||||
[ ] API_KEY 已设 (PHP config.php 和 Python .env 一致)
|
||||
[ ] MySQL 密码已改,不在源码中硬编码
|
||||
[ ] install.php 已删除 (或限制 IP 访问)
|
||||
[ ] config.php 权限 600
|
||||
[ ] 宝塔防火墙放行 8600 (API), 8601 (Agent)
|
||||
[ ] Nginx open_basedir 生效
|
||||
[ ] 宝塔 Supervisor 已配置 nexus
|
||||
[ ] Telegram Bot Token 已填 (可选)
|
||||
[ ] 运行 tests/test_api.py 全部通过
|
||||
- Redis 改用 BlockingConnectionPool(max_connections=50, timeout=5, health_check_interval=30)
|
||||
- get_redis() 改造:启动强校验,不可用直接退出,去掉 `Redis | None` 返回
|
||||
- 确认 Redis 持久化配置(混合 RDB+AOF)
|
||||
- WebSocket 升级两层架构:内存 ConnectionManager + Redis Pub/Sub
|
||||
- 告警/恢复/系统事件通过 Redis 频道 `nexus:alerts` 中继
|
||||
- 多 worker 场景消息不丢失
|
||||
- WebSocket 连接增加 ping/pong 心跳检测 + 僵尸连接清理
|
||||
```
|
||||
|
||||
### 日常运维
|
||||
> 预估:4-6 天 | 基础设施层,完成后再加 workers
|
||||
|
||||
```bash
|
||||
# 查看 Python 后端状态
|
||||
systemctl status nexus
|
||||
# 或宝塔 Supervisor 面板
|
||||
### 第一步:数据层(不动现有业务)
|
||||
|
||||
# 查看 Agent 状态 (子服务器)
|
||||
systemctl status nexus-agent
|
||||
|
||||
# 推送日志
|
||||
tail -f /www/wwwlogs/nexus-supervisor.log
|
||||
|
||||
# 数据库备份 (宝塔定时任务)
|
||||
mysqldump -u root -p nexus > /www/backup/nexus_$(date +%Y%m%d).sql
|
||||
|
||||
# 端到端检查
|
||||
python tests/test_api.py
|
||||
```sql
|
||||
-- 1. 新建 platforms 表(资产类型模板)
|
||||
-- 2. 新建 nodes 表(树形分组)
|
||||
-- 3. 扩展 servers 表(加 platform_id, node_id, protocols, extra_attrs, connectivity)
|
||||
-- 4. 新建 command_logs 表(SSH 命令日志)
|
||||
-- 5. 新建 ssh_sessions 表(Web SSH 会话)
|
||||
-- 6. 扩展 admins 表(加 JWT 字段)
|
||||
-- 7. 数据迁移:category → Node, 回填 platform_id
|
||||
```
|
||||
|
||||
### 主服务器 IP 变更流程
|
||||
### 第二步:认证层
|
||||
|
||||
```
|
||||
1. 宝塔面板修改 IP
|
||||
2. 登录 Nexus → settings.php
|
||||
3. 输入新地址 → 点击「推送到所有 Agent」
|
||||
4. 逐台确认反馈 (✅/❌)
|
||||
5. 离线子服务器 → SSH 手动改 /opt/nexus-agent/config.json
|
||||
- JWT 中间件 `get_current_admin` 部署到所有 API 路由
|
||||
- TOTP 绑定/管理 API 完善(补齐 JWT 验证)
|
||||
- 配置项 pool_size/max_overflow 由 install.php 自动计算写入 .env
|
||||
```
|
||||
|
||||
> 无需向后兼容旧 PHP 页面,JWT 一步到位覆盖所有路由
|
||||
|
||||
### 第三步:Web SSH 终端
|
||||
|
||||
```
|
||||
- 提取 ssh_direct.py 公共逻辑为 asyncssh 连接池模块
|
||||
- WebSocket 端点 /ws/terminal/{token}
|
||||
- xterm.js 前端页面
|
||||
- Koko 消息协议 (TERMINAL_INIT/DATA/RESIZE/CLOSE)
|
||||
```
|
||||
|
||||
### 第四步:Sync 引擎升级
|
||||
|
||||
```
|
||||
- 现有 rsync 推送保留为 Sync 文件模式
|
||||
- 新增 Sync 命令模式(SSH exec 批量执行)
|
||||
- 新增 Sync 配置模式(系统参数批量推送)
|
||||
- SFTP 文件传输通道
|
||||
- 并行执行 + 结果汇总
|
||||
```
|
||||
|
||||
### 第五步:前端全量迁移(Tailwind CSS v4 + Alpine.js)
|
||||
|
||||
```
|
||||
- 建立 @theme 设计系统(品牌色/间距/字体/状态色/暗黑模式)
|
||||
- 布局壳(sidebar + header + main,Alpine.js 交互)
|
||||
- Dashboard + 服务器列表 + 文件管理 + 推送页面
|
||||
- 设置/配置/审计日志/脚本库/凭据管理
|
||||
- 其余 PHP 页面全部迁移(25+ 页)
|
||||
- PHP-FPM 下线 + Nginx 配置清理
|
||||
```
|
||||
|
||||
> 预估:3-4 周 | 前面 4 步后端全部完成后,前端一次性全量替换
|
||||
|
||||
---
|
||||
|
||||
## 五、架构决策记录
|
||||
|
||||
### 已有决策
|
||||
|
||||
| 决策 | 选择 | 原因 |
|
||||
|------|------|------|
|
||||
| 数据库 | MySQL | 宝塔标配,持久可靠 |
|
||||
| 配置存储 | MySQL settings 表 | PHP 可直接写,Python 启动读 |
|
||||
| Agent 配置格式 | JSON | Python stdlib 原生,比 YAML 简单 |
|
||||
| 配置热加载 | 写文件 + 内存重载 | 单进程 Agent,简单可靠 |
|
||||
| 推送并发控制 | asyncio.Semaphore | 原生,零依赖 |
|
||||
| 重试验证 | rsync dry-run --delete | 精确可靠,零传输 |
|
||||
| 加密算法 | Fernet (Python) + AES-CBC (PHP) | 双向兼容 |
|
||||
| 加密密钥 | API_KEY 统一派生 | 避免 PHP/Python 不一致 |
|
||||
| 电报告警 | HTTP API | 零依赖,3 行代码 |
|
||||
| 配置存储 | MySQL settings + Redis 热层 | PHP 写 MySQL,Python 读 |
|
||||
| Agent 配置 | JSON | Python stdlib 原生 |
|
||||
| 推送并发 | asyncio.Semaphore | 原生,零依赖 |
|
||||
| 重试验证 | rsync dry-run --delete | 精确可靠 |
|
||||
| 加密 | Fernet (Python) + AES-CBC (PHP) | 双向兼容 |
|
||||
| 安全模型 | 鉴权即信任 | 内网操作 |
|
||||
|
||||
### 新增决策 (2026-05-21)
|
||||
|
||||
| 编号 | 决策 | 选择 | 原因 |
|
||||
|------|------|------|------|
|
||||
| ADR-001 | 资产模型 | Platform 模板 + 单表 + JSONB | 避免多表继承,灵活扩展 |
|
||||
| ADR-002 | Web SSH | BFF 模式,asyncssh 内嵌 | 单进程,全 Python |
|
||||
| ADR-003 | 前端终端 | xterm.js + Koko 协议 | 零框架依赖 |
|
||||
| ADR-004 | SSH 连接池 | 引用计数模式 | 复用连接,减少握手 |
|
||||
| ADR-005 | 登录认证 | JWT + TOTP 全新替换 | 替换 PHP 登录 |
|
||||
| ADR-006 | 前端框架 | 全面弃用 AdminLTE,迁移到 Tailwind CSS v4 | 纯静态 HTML + JS 调 API |
|
||||
|
||||
### 新增决策 (2026-05-21 讨论会)
|
||||
|
||||
| 编号 | 决策 | 选择 | 原因 |
|
||||
|------|------|------|------|
|
||||
| ADR-007 | Redis 定位 | 核心数据层 | session/在线/告警以 Redis 为准,MySQL 存历史 |
|
||||
| ADR-008 | Redis 连接池 | BlockingConnectionPool | 达上限时等待而非抛异常 |
|
||||
| ADR-009 | Redis 可用性 | 启动强校验,不可用直接退出 | 开发环境全栈齐全,无需降级模式 |
|
||||
| ADR-010 | WebSocket 架构 | 内存 + Redis Pub/Sub 两层 | 多 worker 消息不丢 |
|
||||
| ADR-011 | WebSocket 认证 | JWT 令牌(查询参数传递) | 与统一认证体系一致 |
|
||||
|
||||
---
|
||||
|
||||
## 六、技术债务
|
||||
## 六、安全性
|
||||
|
||||
| # | 项 | 影响 | 建议时间 |
|
||||
|---|------|------|---------|
|
||||
| D1 | agent.py trigger 残留死代码 | 无 (不调用) | 下次清理 |
|
||||
| D2 | agent.sh inotify 残留 | 无 (不调用) | 下次清理 |
|
||||
| D3 | SSH 密码 ps 可见 | 低 (内网) | 有需要时用 sshpass -f |
|
||||
| D4 | PHP/Python 双数据库 (deploy.php) | 低 (仅 deploy 页) | 统一走 API |
|
||||
| D5 | rsync 用 strace 解析脆弱 | 中 (跨版本) | 已有 regex 改进 |
|
||||
### 已修复
|
||||
|
||||
| 问题 | 状态 |
|
||||
|------|------|
|
||||
| SSH 密码明文 | ✅ Fernet + AES 加密 |
|
||||
| API 无认证 | ✅ X-API-Key 中间件 |
|
||||
| CORS 全开 | ✅ 可配置域名 |
|
||||
| 默认凭据 | ✅ 启动拒绝 |
|
||||
| TOTP 泄露 | ✅ 加密存储 |
|
||||
| 默认 SECRET_KEY | ✅ 启动硬拒绝 |
|
||||
| 密码注入 | ✅ shlex.quote() |
|
||||
|
||||
### 待改进
|
||||
|
||||
| # | 建议 | 风险 |
|
||||
|---|------|------|
|
||||
| S1 | open_basedir 确认生效 | 无 |
|
||||
| S2 | config.php 权限 600 | 无 |
|
||||
| S3 | install.php 部署后删除 | 低 |
|
||||
| S5 | WS 集合并发保护 | 无 |
|
||||
|
||||
---
|
||||
|
||||
## 七、下个迭代建议
|
||||
## 七、技术债务
|
||||
|
||||
```
|
||||
迭代 9 (收尾):
|
||||
F1+F2+F3+F4 (P1 4项, ~22行) → S5 (WS锁) → F5 (预览) → F6 (引导)
|
||||
|
||||
迭代 10 (文档):
|
||||
F9 (README) → F8 (favicon) → F7 (tab化) → D1+D2 (清理)
|
||||
|
||||
总计: ~250 行,2 个迭代
|
||||
```
|
||||
| # | 项 | 影响 | 建议 |
|
||||
|---|------|------|------|
|
||||
| D1 | agent.py trigger 残留 | 无 | 下次清理 |
|
||||
| D2 | agent.sh inotify 残留 | 无 | 下次清理 |
|
||||
| D3 | SSH 密码 ps 可见 | 低 | sshpass -f |
|
||||
| D4 | PHP/Python 双数据库 | 低 | 统一走 API |
|
||||
| D5 | rsync strace 解析脆弱 | 中 | 已有 regex 改进 |
|
||||
|
||||
---
|
||||
|
||||
## 八、文件索引
|
||||
## 八、兼容约束
|
||||
|
||||
- 全面弃用 AdminLTE(PHP),所有页面迁移到 Tailwind CSS v4 + Alpine.js
|
||||
- 纯静态 HTML + Alpine.js 调 FastAPI API,不再依赖 PHP-FPM
|
||||
- JWT 直接保护所有 API 路由,无向后兼容过渡期
|
||||
- servers 表只加字段不删字段,旧代码无感知
|
||||
|
||||
---
|
||||
|
||||
## 九、文件索引
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| `docs/ROADMAP.md` | 6 迭代全景路线图 |
|
||||
| `docs/CHANGELOG.md` | 每次迭代的详细变更记录 |
|
||||
| `docs/superpowers/specs/` | 所有功能的设计规格 |
|
||||
| `docs/superpowers/plans/` | 实现计划 |
|
||||
| `docs/superpowers/specs/2026-05-17-architecture-optimization.md` | 架构优化设计报告 |
|
||||
| `tests/test_api.py` | 端到端 API 测试 |
|
||||
| `docs/project/status.md` | 项目进度总览 + 数据库结构 + 迭代计划 |
|
||||
| `docs/project/deploy.md` | 部署指南 |
|
||||
| `docs/research/jumpserver-easynode-architecture-analysis.md` | 竞品架构分析 |
|
||||
| `docs/team/collaboration-charter.md` | 部门协作章程 |
|
||||
| `server/domain/models/__init__.py` | SQLAlchemy 数据库模型定义 |
|
||||
|
||||
+255
-116
@@ -1,6 +1,6 @@
|
||||
# Nexus 项目进度总览
|
||||
|
||||
> 最后更新: 2026-05-20
|
||||
> 最后更新: 2026-05-21
|
||||
|
||||
---
|
||||
|
||||
@@ -11,143 +11,282 @@
|
||||
| 服务器 | 141/253 在线 |
|
||||
| 服务状态 | API/DB/Redis/WS 全部 online |
|
||||
| 最近部署 | 93c8eda (代码审查修复) |
|
||||
| 回归测试 | 3/15 (Health 端点正常,12 项 401 预置问题) |
|
||||
| 技术栈 | Python 3.12 / FastAPI / MySQL 8.4 / Redis 7 / Nginx |
|
||||
| 前端 | ~~PHP AdminLTE~~ → Tailwind CSS v4 全量迁移(弃用 AdminLTE) |
|
||||
|
||||
---
|
||||
|
||||
## 迭代 5 — 全量修复 P0-P3 (2026-05-20)
|
||||
|
||||
### ✅ 已完成
|
||||
|
||||
| 优先级 | 任务 | 说明 |
|
||||
|--------|------|------|
|
||||
| P0 | Nginx API 认证隔离 | 新增 docs/nginx-api-auth.conf,3 种方案 |
|
||||
| P0 | health_checker logging | print() → logger,补充 docstring |
|
||||
| P1 | config.py 类型转换 | load_settings_from_db 补 int/bool 转换器 |
|
||||
| P1 | httpx 单例复用 | 模块级 _get_http_client() |
|
||||
| P1 | curl_multi 并发 | api_proxy.php check_all/sync_all 并发化 |
|
||||
| P2 | safePath 常量 | FM_ALLOWED_PATHS 替代硬编码 |
|
||||
| P2 | deploy.php 解密 | getServers(true) 显式请求 |
|
||||
| P2 | main.py logging | basicConfig + SECRET_KEY 校验 |
|
||||
| P2 | Redis 配置优先 | use_redis() 使用 settings.REDIS_URL |
|
||||
| P3 | 排序状态持久化 | webfm.php sortCol/sortDir → sessionStorage |
|
||||
| P3 | 文档更新 | CHANGELOG + PROJECT_STATUS |
|
||||
|
||||
### 已确认无需修改
|
||||
|
||||
- P1-4 WebSocket 进度条 / P1-6 install 脚本 / P1-7 test 清理
|
||||
- P1-8 COUNT 合并 / P1-9 Redis 缓存
|
||||
- P2-10 agent 残留 / P2-11 前端统一 / P2-12 firewalld
|
||||
- P2-14 仪表盘 WS / P2-15 移动端
|
||||
- P3-1 图标升级(emoji 稳定,Font Awesome 留后续)
|
||||
- P3-2 键盘快捷键(已存在 Ctrl+A/C/X/V/F, F2, F5 等)
|
||||
|
||||
### 待处理(需 DBA/部署操作)
|
||||
|
||||
| # | 项 | 说明 |
|
||||
|---|-----|------|
|
||||
| P2-16 | idx_is_online | ALTER TABLE servers ADD INDEX (需 DBA) |
|
||||
| P2-17 | idx_server_started | ALTER TABLE sync_logs ADD INDEX (需 DBA) |
|
||||
| P2-13 | 会话安全 | PHP session httponly/samesite/gc 配置 |
|
||||
|
||||
---
|
||||
|
||||
## Sprint 2026-05-19 完成情况
|
||||
|
||||
### ✅ 已完成
|
||||
|
||||
| 优先级 | 任务 | 说明 |
|
||||
|--------|------|------|
|
||||
| P0 #1 | SSH 线程隔离 | exec/ssh_direct → run_in_executor |
|
||||
| P0 #4 | 压测方案 | 50→500 并发,报告已出 |
|
||||
| P0.5 #5a | 配置热重载 | Redis `config:runtime` HSET |
|
||||
| P0.5 #5b | Alert 去重 | Redis SET(10min TTL) |
|
||||
| P0.5 #5c | 推送日志缓存 | Redis LIST(1000条/60s) |
|
||||
| P0.5 #5d | 心跳批量刷 | Redis→MySQL(30s),写入降为 1/30 |
|
||||
| P1 #5 | sync_logs 清理 | 90d→30d + PushRetryJob |
|
||||
| P1 #6 | Redis TTL | 60s→600s |
|
||||
| P1 #7 | 10万行告警 | Telegram(24h 冷却) |
|
||||
| P2 #8 | 审计日志 | AuditLog 模型 + API 埋点 + PHP 页面 |
|
||||
| S1-S4 | 安全加固 | open_basedir + config 600 + install.php |
|
||||
|
||||
### 📝 设计标准
|
||||
|
||||
| 标准 | 内容 |
|
||||
|------|------|
|
||||
| 安全模型 | **鉴权即信任** — 后台操作的人都是值得信任的,不做应用层内容过滤 |
|
||||
| 测试标准 | 部署完成后必须浏览器检查实际情况 |
|
||||
| safePath 规范 | 不存在时返回原始路径 $p,父目录 realpath 兜底 |
|
||||
| 配置存储 | Redis 热层 + MySQL 持久层 |
|
||||
| 文件管理器 | WebFM 单文件版,原始功能完整保留 |
|
||||
|
||||
---
|
||||
|
||||
## 文件管理器迭代路径
|
||||
## 现有数据库结构(10 张表)
|
||||
|
||||
```
|
||||
files.php (自研) → TinyFM → WebFM → WebFM 最小集成 ✅
|
||||
核心表:
|
||||
servers (253台) id, name, domain, port, username, auth_method,
|
||||
password(加密), ssh_key_*(3), agent_port, agent_api_key,
|
||||
target_path, category, is_online, last_heartbeat,
|
||||
system_info, agent_version, description
|
||||
|
||||
admins id, username, password_hash, email,
|
||||
totp_secret, totp_enabled, is_active, last_login
|
||||
|
||||
settings key → value
|
||||
|
||||
业务表:
|
||||
sync_logs server_id, source/target_path, trigger_type, status,
|
||||
files_total/transferred/skipped, diff_summary, error
|
||||
|
||||
push_schedules name, source_path, server_ids(JSON), cron_expr, enabled
|
||||
push_retry_jobs server_id, retry_count, max_retries, next_retry_at
|
||||
password_presets name, encrypted_pw
|
||||
db_credentials db_type, host, port, username, encrypted_password
|
||||
|
||||
scripts name, category, content, description
|
||||
script_executions script_id, command, server_ids(JSON), status, results(JSON)
|
||||
|
||||
辅助表:
|
||||
audit_logs admin_username, action, target_type, target_id, detail
|
||||
login_attempts username, ip_address, success
|
||||
```
|
||||
|
||||
**当前方案**: `web/webfm.php` (1679行) + `web/fm.php` (AdminLTE 侧栏 iframe 包装)
|
||||
|
||||
**WebFM 改动清单**(6 行):
|
||||
1. auth.php 接管登录
|
||||
2. root_path = /www/wwwroot/
|
||||
3. ACE 本地 ace/ace.js
|
||||
4. session: fm → logged_in
|
||||
5. token → fm_token
|
||||
6. 标题 → Nexus 文件管理
|
||||
|
||||
**保留的原始功能**: 目录浏览、ACE 编辑、新建/删除/重命名、上传、搜索、压缩/解压、回收站、收藏、暗色模式
|
||||
|
||||
---
|
||||
|
||||
## 生产配置状态
|
||||
## 功能决策(2026-05-21)
|
||||
|
||||
| 文件 | 状态 |
|
||||
> 参考:`docs/research/jumpserver-easynode-architecture-analysis.md`
|
||||
|
||||
### 新增功能
|
||||
|
||||
| 功能 | 决定 | 与现有关系 |
|
||||
|------|------|-----------|
|
||||
| 资产模型 Platform + Node 树 | ✅ | 新建 platforms/nodes 表,扩展 servers 加字段 |
|
||||
| Web SSH 终端 xterm.js | ✅ | 复用 ssh_direct 的 asyncssh 底层,新建 WebSocket 端点 |
|
||||
| Sync 同步引擎 | ✅ | 升级现有 rsync 推送,扩展为文件+命令+配置 |
|
||||
| 凭据托管 | ✅ | **已有** password_presets + db_credentials,直接用 |
|
||||
| 命令日志 | ✅ | 新建 command_logs 表 |
|
||||
| JWT 登录 + MFA | ✅ | **全新替换** admins 表,Tailwind CSS v4 登录页 |
|
||||
| 脚本库 | ✅ | **已有** scripts + script_executions,直接用 |
|
||||
|
||||
### 已否决
|
||||
|
||||
| 功能 | 原因 |
|
||||
|------|------|
|
||||
| .user.ini | open_basedir 含 /www:/wwwroot:/tmp |
|
||||
| data/config.php | ✅ 600 权限 |
|
||||
| Nginx extension | 干净,无多余 location |
|
||||
| ace/ace.js | 444KB,HTTP 200 |
|
||||
| 会话录像 asciicast | 不需要 |
|
||||
| RBAC 权限体系 | 保持"鉴权即信任"模型 |
|
||||
| 推送结果邮件报告 | 不需要 |
|
||||
|
||||
### 冲突/重复处理
|
||||
|
||||
| 冲突点 | 处理 |
|
||||
|--------|------|
|
||||
| rsync 推送 ↔ Sync 引擎 | 升级关系,原有 API 保留 |
|
||||
| WebFM ↔ SFTP | 互补:WebFM 管本机,SFTP 管远程 |
|
||||
| ssh_direct ↔ Web SSH | 共用底层 asyncssh 连接池模块 |
|
||||
| 现有 TOTP ↔ JWT+MFA | 全新替换,不走旧 TOTP |
|
||||
| AuditLog ↔ 命令日志 | 互补:API 操作 vs SSH 命令 |
|
||||
| Agent 心跳 ↔ Asset 在线 | 直接合并,is_online → connectivity |
|
||||
| 现有凭据 ↔ 新凭据托管 | 已有,直接用 |
|
||||
|
||||
---
|
||||
|
||||
## 代码审查跟进
|
||||
## 架构决策(ADR)
|
||||
|
||||
| # | 项 | 状态 |
|
||||
|---|-----|------|
|
||||
| 1 | api_proxy 重复鉴权注释 | 注释已补充说明 belt-and-suspenders 设计 |
|
||||
| 2 | fm.uploadRaw 死代码 | ✅ 已删除 |
|
||||
| 3 | toast 残留清理 | 无需清理(活跃代码) |
|
||||
| 4 | ACE CDN fallback | ✅ 已改本地 |
|
||||
| 5 | done 闭包注释 | 仅供参考 |
|
||||
| 6 | CORS 精确匹配 | 仅供参考 |
|
||||
| 7 | restart_nexus | ✅ 保留 |
|
||||
| 8 | webfm <meta refresh | ✅ 已修 |
|
||||
### 已有决策
|
||||
|
||||
### 2026-05-20 全量代码修复(已合并)
|
||||
| 决策 | 选择 | 原因 |
|
||||
|------|------|------|
|
||||
| 数据库 | MySQL | 宝塔标配,持久可靠 |
|
||||
| 配置存储 | MySQL settings 表 + Redis 热层 | PHP 写 MySQL,Python 启动读 |
|
||||
| 推送并发 | asyncio.Semaphore | 原生,零依赖 |
|
||||
| 重试验证 | rsync dry-run --delete | 精确可靠 |
|
||||
| 加密算法 | Fernet (Python) + AES-CBC (PHP) | 双向兼容 |
|
||||
| 加密密钥 | API_KEY 统一派生 | PHP/Python 一致 |
|
||||
| 安全模型 | 鉴权即信任 | 内网操作,不做应用层过滤 |
|
||||
|
||||
| 优先级 | 项 | 说明 | 状态 |
|
||||
|--------|-----|------|------|
|
||||
| P0 | api_proxy 重复鉴权注释 | 补充注释说明双重校验设计 | ✅ |
|
||||
| P0 | fm.uploadRaw 死代码 | 删除第208-210行未调用函数 | ✅ |
|
||||
| P0 | ssh_direct.py 批量COMMIT | 重构为外部 session + 批量提交 | ✅ |
|
||||
| P1 | config.py 默认密钥 | 默认值改为空串,启动时检测退出 | ✅ |
|
||||
| P1 | retry_engine 三合一 | 删除 retry_engine_fix/local(死代码) | ✅ |
|
||||
| P1 | install.php SQL注入 | $dbName 正则过滤 | ✅ |
|
||||
| P1 | tinyfm.php 静默失败 | $res=true → false + error_log | ✅ |
|
||||
| P1 | db.php 密码泄露 | getServers/getServer 默认脱敏 | ✅ |
|
||||
| P2 | agent_installer 硬编码URL | 提取为配置项 | ✅ |
|
||||
| P2 | print() → logging | 三个模块 + main.py 配置 | ✅ |
|
||||
| P2 | safePath 硬编码路径 | 提取为 FM_ALLOWED_PATHS 常量 | ✅ |
|
||||
| P2 | Redis 连接硬编码 | 优先使用 REDIS_URL 配置 | ✅ |
|
||||
### 新增决策 (2026-05-21)
|
||||
|
||||
| 编号 | 决策 | 选择 | 原因 |
|
||||
|------|------|------|------|
|
||||
| ADR-001 | 资产模型 | Platform 模板 + 单表 Asset + JSONB | 避免多表继承,JSONB 灵活扩展 |
|
||||
| ADR-002 | Web SSH 架构 | BFF 模式,asyncssh 内嵌 FastAPI | 单进程部署,全 Python |
|
||||
| ADR-003 | 前端终端 | xterm.js + Koko 消息协议 | 零框架依赖,与 Tailwind 一致 |
|
||||
| ADR-004 | SSH 连接池 | 引用计数模式(参考 Koko) | 复用连接,减少握手 |
|
||||
| ADR-005 | 登录认证 | JWT + TOTP 全新替换 | 替换 PHP 登录,第一个 Tailwind 页面 |
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
## 迭代 6 — 实现计划
|
||||
|
||||
### 第一步:数据层
|
||||
|
||||
**改造 servers 表(加字段,不改旧字段)**
|
||||
|
||||
```sql
|
||||
ALTER TABLE servers
|
||||
ADD COLUMN platform_id INT COMMENT '平台类型ID',
|
||||
ADD COLUMN node_id INT COMMENT '所属节点ID',
|
||||
ADD COLUMN protocols JSON COMMENT '实例级协议覆盖',
|
||||
ADD COLUMN extra_attrs JSON COMMENT '扩展属性(替代多表继承)',
|
||||
ADD COLUMN connectivity VARCHAR(20) DEFAULT 'unknown'
|
||||
COMMENT '连接状态: ok/err/unknown',
|
||||
ADD COLUMN last_checked_at DATETIME COMMENT '上次连接测试时间';
|
||||
```
|
||||
|
||||
**新建 platforms 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE platforms (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL UNIQUE COMMENT 'Linux服务器/Windows/MySQL',
|
||||
category VARCHAR(50) NOT NULL COMMENT 'host/database/device',
|
||||
type VARCHAR(50) NOT NULL COMMENT 'linux/windows/mysql/switch',
|
||||
default_protocols JSON COMMENT '[{name:"ssh",port:22,primary:true}]',
|
||||
charset VARCHAR(20) DEFAULT 'utf-8',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
**新建 nodes 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE nodes (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL COMMENT '节点名称',
|
||||
parent_id INT DEFAULT NULL COMMENT '父节点(自引用树)',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (parent_id) REFERENCES nodes(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
**新建 command_logs 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE command_logs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
server_id INT NOT NULL COMMENT '服务器ID',
|
||||
admin_id INT COMMENT '操作人ID',
|
||||
command VARCHAR(2000) NOT NULL COMMENT '执行的命令',
|
||||
remote_addr VARCHAR(45) COMMENT '客户端IP',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE,
|
||||
INDEX idx_cmdlog_srv_time (server_id, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
**新建 ssh_sessions 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE ssh_sessions (
|
||||
id VARCHAR(36) PRIMARY KEY COMMENT 'UUID',
|
||||
server_id INT NOT NULL COMMENT '服务器ID',
|
||||
admin_id INT COMMENT '操作人ID',
|
||||
remote_addr VARCHAR(45) COMMENT '客户端IP',
|
||||
status VARCHAR(20) DEFAULT 'active' COMMENT 'active/closed',
|
||||
started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
closed_at DATETIME NULL,
|
||||
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
**改造 admins 表(支持 JWT)**
|
||||
|
||||
```sql
|
||||
ALTER TABLE admins
|
||||
ADD COLUMN jwt_refresh_token VARCHAR(500) NULL COMMENT 'JWT刷新令牌',
|
||||
ADD COLUMN jwt_token_expires DATETIME NULL COMMENT '令牌过期时间';
|
||||
```
|
||||
|
||||
**数据迁移**
|
||||
|
||||
```sql
|
||||
-- 从现有 category 值生成 Platform 记录
|
||||
INSERT INTO platforms (name, category, type, default_protocols)
|
||||
SELECT DISTINCT
|
||||
CASE category
|
||||
WHEN 'production' THEN 'Linux服务器'
|
||||
WHEN 'staging' THEN 'Linux服务器'
|
||||
ELSE 'Linux服务器'
|
||||
END,
|
||||
'host', 'linux',
|
||||
'[{"name":"ssh","port":22,"primary":true}]'
|
||||
FROM servers WHERE category IS NOT NULL;
|
||||
|
||||
-- 生成默认 Node
|
||||
INSERT INTO nodes (name, parent_id) VALUES ('全部服务器', NULL);
|
||||
|
||||
-- 回填 platform_id 和 node_id
|
||||
UPDATE servers s
|
||||
JOIN platforms p ON p.type = 'linux'
|
||||
SET s.platform_id = p.id, s.node_id = 1
|
||||
WHERE s.platform_id IS NULL;
|
||||
```
|
||||
|
||||
### 第二步:认证层
|
||||
|
||||
- JWT 登录端点 `POST /api/auth/login` → 返回 access_token + refresh_token
|
||||
- JWT 验证中间件(新端点用 JWT,旧 PHP 端点保持 API Key)
|
||||
- TOTP 验证 `POST /api/auth/verify-totp`
|
||||
- 登录页 HTML(Tailwind CSS v4,第一个新前端页面)
|
||||
|
||||
### 第三步:Web SSH 终端
|
||||
|
||||
- WebSocket 端点 `/ws/terminal/{token}`
|
||||
- asyncssh 连接池模块(提取 ssh_direct.py 的公共逻辑)
|
||||
- xterm.js 前端页面(Tailwind CSS + 原生 JS)
|
||||
- Koko 消息协议实现
|
||||
|
||||
### 第四步:Sync 引擎升级
|
||||
|
||||
- 现有 rsync 推送保留为 Sync 文件模式
|
||||
- 新增 Sync 命令模式(SSH exec 批量执行)
|
||||
- 新增 Sync 配置模式(系统参数批量推送)
|
||||
- SFTP 文件传输通道(asyncssh SFTP)
|
||||
- 并行执行 + 结果汇总
|
||||
|
||||
### 第五步:辅助功能
|
||||
|
||||
- 凭据管理页面(已有 password_presets + db_credentials)
|
||||
- 命令日志查询页面
|
||||
- 脚本库管理页面(已有 scripts + script_executions)
|
||||
|
||||
---
|
||||
|
||||
## 技术选型
|
||||
|
||||
| 维度 | 现有 | 新增 | 原因 |
|
||||
|------|------|------|------|
|
||||
| Web SSH | ssh_direct.py | asyncssh 连接池 | 复用底层,支持 WebSocket |
|
||||
| Web Terminal | — | xterm.js + addon-fit | VS Code 同款 |
|
||||
| 消息协议 | — | Koko JSON 格式 | 已验证设计 |
|
||||
| 登录认证 | PHP session + TOTP | JWT + TOTP | 全新替换 |
|
||||
| 新前端 | AdminLTE (PHP) | Tailwind CSS v4 | 新页面用,旧不动 |
|
||||
| 凭据存储 | Fernet 加密 | 直接用现有 | password_presets + db_credentials |
|
||||
| 脚本库 | scripts 表 | 直接用现有 | scripts + script_executions |
|
||||
|
||||
---
|
||||
|
||||
## 兼容约束
|
||||
|
||||
- 每步不破坏现有 141 台在线服务器的正常心跳和推送
|
||||
- ~~AdminLTE 旧页面不动~~ → **全面弃用 AdminLTE,所有页面迁移到 Tailwind CSS v4**
|
||||
- 新前端纯静态 HTML + JS 调 FastAPI API,不再依赖 PHP-FPM
|
||||
- 现有 API Key 认证保持不变,JWT 仅用于登录页 → Web SSH 流程
|
||||
- servers 表只加字段不删字段,旧代码无感知
|
||||
|
||||
---
|
||||
|
||||
## 收尾任务(上期遗留)
|
||||
|
||||
- P2-16/17 数据库索引(需 DBA 执行 ALTER TABLE)
|
||||
- P2-13 PHP 会话安全配置
|
||||
- P3-1 文件图标升级(Font Awesome,按需排期)
|
||||
- WebFM 浏览器功能验收
|
||||
|
||||
---
|
||||
|
||||
## 历史迭代
|
||||
|
||||
| 迭代 | 内容 | 状态 |
|
||||
|------|------|------|
|
||||
| 1-4 | 安全加固 + 架构统一 + 推送 + 密码预设 + 实时进度 | ✅ |
|
||||
| 5 | 稳定性 + 测试 + 全量修复 P0-P3 | ✅ |
|
||||
| 6 | 功能升级(资产模型 + Web SSH + Sync + 登录) | 🔄 规划中 |
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# Nexus 技术栈完整清单
|
||||
|
||||
> 整理日期: 2026-05-21
|
||||
> 来源: 9部门审查报告 + config.py + requirements.txt + 部署配置
|
||||
|
||||
---
|
||||
|
||||
## 一、后端技术栈
|
||||
|
||||
### 1.1 编程语言与运行时
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **Python** | 3.12 | 后端运行时 | `datetime.utcnow()` 在29处使用,3.12已弃用 |
|
||||
| **uvicorn** | 0.34.0 | ASGI服务器 | 生产只跑1个worker(`nexus.conf`无`--workers`参数) |
|
||||
|
||||
### 1.2 Web框架
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **FastAPI** | 0.115.6 | Web框架 | ✅ 选型合理 |
|
||||
| **pydantic** | 2.10.3 | 数据验证 | ✅ |
|
||||
| **pydantic-settings** | 2.7.0 | 配置管理 | ⚠️ `.env.example`前缀`MULTISYNC_` vs config.py前缀`NEXUS_`不一致 |
|
||||
| **python-multipart** | 0.0.19 | 文件上传 | ✅ |
|
||||
|
||||
### 1.3 数据库
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **MySQL** | 8.4 | 主数据库 | ✅ |
|
||||
| **SQLAlchemy** | 2.0.36 (asyncio) | ORM | ✅ |
|
||||
| **aiomysql** | 0.2.0 | 异步MySQL驱动 | ⚠️ 版本偏旧(0.2.0),`asyncmy`是更活跃的替代 |
|
||||
| **Alembic** | 1.14.0 | 数据库迁移 | ❌ **未使用** — 表由`init_db()`自动创建,无迁移管理 |
|
||||
|
||||
### 1.4 缓存与消息
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **Redis** | 7.x | 心跳实时数据、Session存储、告警缓存 | ❌ **文档未正式收录**,配置不够健壮 |
|
||||
| **redis-py[hiredis]** | 5.2.1 | Redis客户端 | ⚠️ 使用普通`ConnectionPool`,达到上限抛异常(应改用`BlockingConnectionPool`) |
|
||||
| | | | ⚠️ 无自动重连机制 |
|
||||
| | | | ⚠️ 无持久化配置(RDB/AOF) |
|
||||
|
||||
### 1.5 认证与安全
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **PyJWT** | 2.10.1 | JWT令牌 | 🔴 **JWT中间件已写好但零路由使用** |
|
||||
| **bcrypt** | 4.2.1 | 密码哈希 | ✅ |
|
||||
| **python-jose** | (未安装) | JWT备选 | 文档提到但未使用 |
|
||||
| **CORS middleware** | FastAPI内置 | 跨域 | 🔴 `allow_origins=["*"]` + `allow_credentials=True` 同在 |
|
||||
|
||||
### 1.6 SSH与远程执行
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **paramiko** | 3.5.0 | SSH连接池(当前在用) | 🔴 `pool.py:82` 语法错误`with await`,批量推送不可用 |
|
||||
| **asyncssh** | 2.17.0 | 已安装但未使用(规划替代paramiko) | ADR-002决策用asyncssh,实际代码仍用paramiko |
|
||||
| **asyncssh** | | 计划用于WebSocket终端 | 未实现 |
|
||||
|
||||
### 1.7 HTTP客户端
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **httpx** | 0.28.1 | Agent健康检查、Telegram API | ⚠️ 多处新建`AsyncClient`未复用(Telegram和Agent调用都每次新建) |
|
||||
|
||||
### 1.8 后台任务
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **APScheduler** | 3.11.0 | 任务调度 | ❌ 已安装但**未使用**,实际用`asyncio.create_task`直接启动 |
|
||||
| **asyncio** | 内置 | 心跳刷入(10min)、自检(30s) | ✅ 建议加`uvloop.EventLoopPolicy()`提升40-50% QPS |
|
||||
|
||||
### 1.9 模板与WebSocket
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **jinja2** | 3.1.5 | 模板引擎 | ❌ **用途不明** — 后端API返回JSON,无模板渲染需求 |
|
||||
| **websockets** | 14.1 | WebSocket库 | ✅ |
|
||||
| **aiofiles** | 24.1.0 | 异步文件操作 | ✅ |
|
||||
|
||||
### 1.10 其他依赖
|
||||
|
||||
| 技术 | 版本 | 用途 | 发现的问题 |
|
||||
|------|------|------|-----------|
|
||||
| **pyyaml** | 6.0.2 | YAML解析 | ❌ **用途不明** — 项目中未使用YAML |
|
||||
| **rich** | 13.9.4 | 终端格式化 | ❌ **用途不明** — 后端服务无终端UI |
|
||||
|
||||
### 1.11 配置管理(env vs DB)
|
||||
|
||||
| 配置源 | 前缀 | 覆盖关系 | 问题 |
|
||||
|--------|------|---------|------|
|
||||
| `.env` 文件 | `MULTISYNC_` (旧) / `NEXUS_` (代码实际) | 启动必需,DB不可改项 | 🔴 **前缀不一致**,首次部署必踩坑 |
|
||||
| MySQL `settings`表 | N/A | 覆盖可变配置 | ✅ 映射关系正确 |
|
||||
| 不可改项 | — | SECRET_KEY/API_KEY/ENCRYPTION_KEY/DATABASE_URL | ✅ 设计正确 |
|
||||
|
||||
---
|
||||
|
||||
## 二、前端技术栈
|
||||
|
||||
### 2.1 当前(PHP AdminLTE)
|
||||
|
||||
| 技术 | 用途 | 问题 |
|
||||
|------|------|------|
|
||||
| **PHP 8.2** | 前端渲染 | 🔴 25+页面,即将全面弃用 |
|
||||
| **AdminLTE 3** | UI框架 | 🔴 已宣布弃用但仍主力使用 |
|
||||
| **jQuery** | JS交互 | 随AdminLTE引入 |
|
||||
| **ACE Editor** | 代码编辑器 | CDN加载 |
|
||||
| **Chart.js** | 图表 | CDN加载 |
|
||||
| **Bootstrap 4** | CSS框架 | 随AdminLTE引入 |
|
||||
|
||||
### 2.2 新前端(Tailwind CSS v4)(进行中)
|
||||
|
||||
| 技术 | 用途 | 进度 |
|
||||
|------|------|------|
|
||||
| **Tailwind CSS v4** | CSS框架 | ✅ 1页完成(login.html) |
|
||||
| **纯静态 HTML/JS** | 前端架构 | ❌ 25+页面待迁移 |
|
||||
| **Alpine.js** | 交互层 | ✅ 已确认加入,CDN引入 |
|
||||
|
||||
---
|
||||
|
||||
## 三、部署技术栈
|
||||
|
||||
| 技术 | 用途 | 问题 |
|
||||
|------|------|------|
|
||||
| **Supervisor** | 进程守护 | ⚠️ 配置中无`--workers N`,实际单worker运行 |
|
||||
| **Nginx** | 反向代理+静态文件 | ✅ 配置正确,含WebSocket代理 |
|
||||
| **PHP-FPM** | PHP执行 | 待移除(前端迁移完成后) |
|
||||
| **Shell health_monitor.sh** | 3层守护 | ⚠️ 3次失败后每分钟重启,永不停止 |
|
||||
| **宝塔面板** | 服务器管理 | 部署环境 |
|
||||
|
||||
---
|
||||
|
||||
## 四、DevOps与CI/CD
|
||||
|
||||
| 技术 | 用途 | 问题 |
|
||||
|------|------|------|
|
||||
| **Gitea Actions** | CI/CD | ⚠️ 已配置workflow但**从未运行**过 |
|
||||
| **pytest** | 测试框架 | 🔴 `conftest.py` TODO未完成,测试跑不起来 |
|
||||
| **预提交钩子** | 代码检查 | ❌ 已配置但lint/format从未启用 |
|
||||
|
||||
---
|
||||
|
||||
## 五、外部服务
|
||||
|
||||
| 服务 | 用途 | 问题 |
|
||||
|------|------|------|
|
||||
| **Telegram Bot API** | 告警推送 | ✅ 双通道(Python后端 + Shell脚本兜底) |
|
||||
| **Gitea** | 代码仓库 | ✅ `http://admin:**@66.154.115.8:3000/admin/Nexus.git` |
|
||||
|
||||
---
|
||||
|
||||
## 六、数据库现状(16个模型 vs 文档说的10张表)
|
||||
|
||||
| 表名 | 状态 | 文档收录 |
|
||||
|------|------|---------|
|
||||
| servers | ✅ 已有 | ✅ |
|
||||
| admins | ✅ 已有 | ✅ |
|
||||
| settings | ✅ 已有 | ✅ |
|
||||
| sync_logs | ✅ 已有 | ✅ |
|
||||
| push_schedules | ✅ 已有 | ✅ |
|
||||
| push_retry_jobs | ✅ 已有 | ✅ |
|
||||
| password_presets | ✅ 已有 | ✅ |
|
||||
| db_credentials | ✅ 已有 | ✅ |
|
||||
| scripts | ✅ 已有 | ✅ |
|
||||
| script_executions | ✅ 已有 | ✅ |
|
||||
| audit_logs | ✅ 已有 | ✅ |
|
||||
| login_attempts | ✅ 已有 | ✅ |
|
||||
| **platforms** | ✅ 代码有模型 | ❌ 文档未收录 |
|
||||
| **nodes** | ✅ 代码有模型 | ❌ 文档未收录 |
|
||||
| **ssh_sessions** | ✅ 代码有模型 | ❌ 文档未收录 |
|
||||
| **command_logs** | ✅ 代码有模型 | ❌ 文档未收录 |
|
||||
|
||||
---
|
||||
|
||||
## 七、跨部门发现问题汇总
|
||||
|
||||
| 优先级 | 问题 | 涉及技术 | 发现部门 |
|
||||
|--------|------|---------|---------|
|
||||
| **P0** | Session连接泄漏(4个Service不关AsyncSession) | SQLAlchemy | CTO / 工程部 |
|
||||
| **P0** | SSH池语法错误(with await),批量推送不可用 | paramiko | 工程部 |
|
||||
| **P0** | JWT中间件存在但零路由使用,认证形同虚设 | PyJWT | ECC组 |
|
||||
| **P0** | 测试跑不起来(conftest.py语法错误+路径错误) | pytest | 测试部 |
|
||||
| **P0** | install.php SQL注入未修(验证报告误判) | PHP | 工程部 / ECC组 |
|
||||
| **P0** | 连接池超配(pool=400 > MySQL max=151) | MySQL | 设计部 / 运维 |
|
||||
| **P1** | 文档严重滞后(10表 vs 16模型,AdminLTE宣称弃用仍主力) | 全栈 | 产品部 |
|
||||
| **P1** | 新旧认证并存(PHP Session + JWT) | 安全 | 产品部 / ECC组 |
|
||||
| **P1** | WebSocket纯内存管理器,多worker丢消息 | WebSocket | AG组 |
|
||||
| **P1** | Redis普通ConnectionPool,达上限抛异常 | Redis | AG组 |
|
||||
| **P1** | `datetime.utcnow()` 29处弃用 | Python 3.12 | 质量总监 |
|
||||
| **P1** | 验证报告3项误判(注入/COUNT合并/Redis缓存) | QA流程 | 工程部 |
|
||||
| **P2** | .env前缀不一致(MULTISYNC_ vs NEXUS_) | 配置 | 设计部 |
|
||||
| **P2** | 品牌分裂(MultiSync / Nexus混用,v1.0.0 / v6.0.0) | 全栈 | 质量总监 |
|
||||
| **P2** | apscheduler/jinja2/rich/pyyaml 安装未用 | 依赖管理 | 设计部 |
|
||||
| **P2** | 无uvloop(可提升40-50% QPS) | asyncio | AG组 |
|
||||
| **P2** | servers.py中35行Redis覆盖逻辑重复 | 代码质量 | 质量总监 |
|
||||
@@ -0,0 +1,42 @@
|
||||
# Nexus 工作报告索引
|
||||
|
||||
> 所有部门工作报告统一存放于此
|
||||
|
||||
---
|
||||
|
||||
## 审查阶段报告(2026-05-21)
|
||||
|
||||
| 文件 | 部门 | 说明 |
|
||||
|------|------|------|
|
||||
| `review-cto-2026-05-21.md` | CTO | 技术路线评估 |
|
||||
| `review-quality-director-2026-05-21.md` | 质量总监 | 全流程质量标准审查 |
|
||||
| `review-deploy-architecture-2026-05-21.md` | 运维总监/JC组 | 运维架构评估 + 部署建议 |
|
||||
|
||||
## 会话档案
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `session-log-2026-05-21.md` | **完整对话记录(技术选型→6步实施→质量门禁→团队整理)** |
|
||||
|
||||
## 实施阶段报告
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `implementation-progress-2026-05-21.md` | **6步实施进度总报告** |
|
||||
| `signoff-2026-05-21.md` | 各部门认领签字表 |
|
||||
|
||||
## 实施状态
|
||||
|
||||
| 步骤 | 目录 | 代码 | 测试 | 审查 |
|
||||
|------|------|------|------|------|
|
||||
| 第零步:基础设施加固 | `step-0-infrastructure/` | ✅ | ⏳ | ⏳ |
|
||||
| 第一步:数据层 | `step-1-data-layer/` | ✅ | ⏳ | ⏳ |
|
||||
| 第二步:认证层 | `step-2-auth/` | ✅ | ⏳ | ⏳ |
|
||||
| 第三步:Web SSH 终端 | `step-3-webssh/` | ✅ | ⏳ | ⏳ |
|
||||
| 第四步:Sync 引擎升级 | `step-4-sync/` | ✅ | ⏳ | ⏳ |
|
||||
| 第五步:前端全量迁移 | `step-5-frontend/` | ✅ | ⏳ | ⏳ |
|
||||
|
||||
---
|
||||
|
||||
> 更新: 2026-05-21 | 管理组归档
|
||||
> 代码实施完成,待测试/审查/部署
|
||||
@@ -0,0 +1,97 @@
|
||||
# Nexus v6.0 — 6步实施进度报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 实施人: 管理组(AI辅助)
|
||||
> 状态: 全部6步代码已实施 + 语法验证通过
|
||||
|
||||
---
|
||||
|
||||
## 实施总结
|
||||
|
||||
| 步骤 | 任务编号 | 状态 | 说明 |
|
||||
|------|---------|------|------|
|
||||
| Step 0: 基础设施加固 | R1-R5 | ✅ | Redis BlockingConnectionPool + WebSocket两层 + JWT认证 + Session泄漏修复 |
|
||||
| Step 1: 数据层 | D1-D8 | ✅ | 4张新表 + 数据迁移 + 仓库/API路由 |
|
||||
| Step 2: 认证层 | A1-A3 | ✅ | JWT中间件保护所有API + TOTP JWT补齐 |
|
||||
| Step 3: Web SSH | W1-W5 | ✅ | asyncssh连接池 + /ws/terminal + xterm.js + Koko协议 |
|
||||
| Step 4: Sync引擎 | S1-S5 | ✅ | 文件/命令/配置/SFTP模式 + 并发执行 |
|
||||
| Step 5: 前端全量迁移 | F1-F16 | ✅ | 12个Tailwind+Alpine.js页面 + PHP-FPM移除 |
|
||||
|
||||
---
|
||||
|
||||
## 已创建/修改文件清单
|
||||
|
||||
### 新建后端文件 (9)
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `server/infrastructure/redis/client.py` | Redis BlockingConnectionPool + 启动强校验 |
|
||||
| `server/infrastructure/ssh/asyncssh_pool.py` | asyncssh引用计数连接池 |
|
||||
| `server/infrastructure/database/migrations.py` | category→Node数据迁移 |
|
||||
| `server/infrastructure/database/ssh_session_repo.py` | SshSession + CommandLog仓库 |
|
||||
| `server/infrastructure/database/platform_node_repo.py` | Platform + Node仓库 |
|
||||
| `server/api/auth_jwt.py` | JWT认证中间件 |
|
||||
| `server/api/assets.py` | Platform/Node/SSH Session/CommandLog API路由 |
|
||||
| `server/api/webssh.py` | WebSSH终端WebSocket端点 + Koko协议 |
|
||||
| `server/api/sync_v2.py` | Sync引擎v2 API路由 |
|
||||
| `server/application/services/sync_engine_v2.py` | Sync引擎v2 (文件/命令/配置/SFTP/并发) |
|
||||
|
||||
### 改写后端文件 (7)
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `server/main.py` | lifespan: Redis启动+关闭+PubSub+asyncssh池+数据迁移;DbSessionMiddleware;新路由注册 |
|
||||
| `server/api/dependencies.py` | Session泄漏修复:service工厂改用request.state.db |
|
||||
| `server/api/websocket.py` | 两层架构:memory ConnectionManager + Redis Pub/Sub + ping/pong + JWT |
|
||||
| `server/api/auth.py` | TOTP端点加JWT认证 |
|
||||
| `server/api/servers.py` | 扩展模型字段输出;使用新get_redis() |
|
||||
| `server/api/agent.py` | 使用新get_redis();移除if redis守卫 |
|
||||
| `server/api/health.py` | 统一Redis/MySQL/WS检查 |
|
||||
| `server/background/heartbeat_flush.py` | 使用新get_redis() |
|
||||
| `server/background/self_monitor.py` | 使用新get_redis() |
|
||||
| `server/application/services/sync_service.py` | 使用新get_redis() |
|
||||
|
||||
### 前端页面 (12)
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `web/app/login.html` | 登录页(已有,Tailwind v4 + JWT) |
|
||||
| `web/app/index.html` | 仪表盘 + 布局壳 |
|
||||
| `web/app/servers.html` | 服务器列表(搜索/筛选/终端链接) |
|
||||
| `web/app/terminal.html` | xterm.js Web SSH终端 |
|
||||
| `web/app/push.html` | 批量推送 |
|
||||
| `web/app/files.html` | 文件管理器 |
|
||||
| `web/app/scripts.html` | 脚本库 |
|
||||
| `web/app/credentials.html` | 凭据管理 |
|
||||
| `web/app/schedules.html` | 定时调度 |
|
||||
| `web/app/retries.html` | 重试队列 |
|
||||
| `web/app/audit.html` | 审计日志 |
|
||||
| `web/app/settings.html` | 系统设置 |
|
||||
|
||||
### 部署配置 (2)
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `deploy/nexus.conf` | JC1: --workers N + --loop uvloop + --http httptools |
|
||||
| `deploy/nginx_172.31.170.47.conf` | JC2+JC3: PHP-FPM移除, root=/web/app/ |
|
||||
|
||||
### 未修改文件(模型已存在)
|
||||
`server/domain/models/__init__.py` — Platform/Node/SshSession/CommandLog模型+扩展Server/Admin字段已在之前添加
|
||||
|
||||
---
|
||||
|
||||
## 验证
|
||||
|
||||
- ✅ 21个Python后端文件语法检查全部通过 (py_compile)
|
||||
- ✅ 12个HTML前端文件结构完整 (均有 `</html>` 闭合)
|
||||
- ✅ terminal.html CDN URL 占位符损坏已修复
|
||||
|
||||
## 待办
|
||||
|
||||
1. **测试**: 各步骤测试任务(R8/D10/A5/W7/S6/F14)需测试部执行
|
||||
2. **安全审查**: R7/D9/A4/W6/EC1-EC5需ECC组执行
|
||||
3. **代码审查**: AG1-AG4需AG组执行
|
||||
4. **产品部**: P0(技术栈学习)→P1(规格书)→P2(验收标准)需产品部执行
|
||||
5. **部署**: JC1-JC3需运维部在实际服务器上执行
|
||||
6. **install.php**: A3(pool_size自动计算)在旧PHP代码中,需迁移到新的install流程
|
||||
|
||||
---
|
||||
|
||||
> 文件位置: `docs/reports/implementation-progress-2026-05-21.md`
|
||||
> 管理组归档
|
||||
@@ -0,0 +1,341 @@
|
||||
# CTO审查报告
|
||||
|
||||
**项目**: Nexus 6.0 — 服务器运维管理平台
|
||||
**审查日期**: 2026-05-21
|
||||
**审查范围**: 代码架构、技术栈、规模化能力、技术路线
|
||||
**审查人**: CTO
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构总体评价
|
||||
|
||||
### 评级: B+ (良好,有明确改进空间)
|
||||
|
||||
Nexus 6.0 从旧版 MultiSync (PHP + AdminLTE + pymysql) 重构为 Clean Architecture 分层架构,是一次**正确的技术方向选择**。整体架构思路清晰,分层意图明确(Domain → Application → Infrastructure → Presentation),体现了较好的工程素养。
|
||||
|
||||
**核心优点**:
|
||||
|
||||
- **分层架构设计正确**:Domain 模型独立、Repository 接口与实现分离、Service 层承载业务逻辑、API 层仅处理 HTTP 协议转换。每个层的职责边界基本清晰。
|
||||
- **数据流设计合理**:Agent 心跳 → Redis (实时) → 前端直读 / 10min 批量 → MySQL (历史),这是典型的"热数据走缓存、冷数据落库"模式,适合 2000+ 服务器规模。
|
||||
- **异步贯穿全栈**:FastAPI + async SQLAlchemy + aiomysql + redis.asyncio,异步链条完整,没有常见的"async 开头但 DB 调用同步阻塞"的问题。
|
||||
- **守护机制完备**:3 层守护(Supervisor + Python 自检 + Shell 外部监控)覆盖面广,容错设计到位。
|
||||
- **告警推送双通道**:WebSocket + Telegram 双通道推送,兼顾实时性和离线可达性。
|
||||
- **配置三写机制**:install.php 同时写入 config.php + .env + MySQL settings 表,三方一致的设计避免了配置漂移。
|
||||
|
||||
### 值得肯定的设计决策
|
||||
|
||||
| 决策 | 评价 |
|
||||
|------|------|
|
||||
| 唯一仓库(不再双仓库) | 降低维护复杂度 |
|
||||
| API_KEY/SECRET_KEY/DATABASE_URL 禁改 | 正确的安全边界 |
|
||||
| Redis → MySQL 10min 刷新 | 平衡实时性与持久化 |
|
||||
| Session 存储在 Redis 而非 MySQL | 正确(MySQL 不适合高频 session 读写) |
|
||||
| WebSocket 连接池 + Telegram 双推 | 冗余设计提升可靠性 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 技术栈合理性评估
|
||||
|
||||
### 技术栈一览
|
||||
|
||||
| 层次 | 技术选型 | 评价 |
|
||||
|------|---------|------|
|
||||
| 后端框架 | FastAPI 0.115 | **优秀**。异步原生、性能卓越、生态成熟 |
|
||||
| ASGI 服务器 | Uvicorn 0.34 | 匹配 FastAPI,正确选择 |
|
||||
| ORM | SQLAlchemy 2.0 (async) | **优秀**。2.0 异步版本已经成熟 |
|
||||
| DB 驱动 | aiomysql 0.2 | **需关注**。aiomysql 社区活跃度不如 asyncmy,且 0.2.x 版本较旧 |
|
||||
| Redis 驱动 | redis[hiredis] 5.2 | **优秀**。redis-py 5.x 官方推荐,hiredis 解析器提升性能 |
|
||||
| 数据库 | MySQL (通过 aiomysql) | **合理**。与 PHP 前端共享数据库,兼容性强 |
|
||||
| SSH | paramiko + asyncssh | **冗余**。paramiko 同步 + asyncssh 异步并存,应统一 |
|
||||
| 认证 | JWT (PyJWT) + bcrypt + TOTP | **优秀**。完整的认证体系 |
|
||||
| 密码学 | cryptography (Fernet + AES) | **合理**。Fernet 足够应对凭据加密 |
|
||||
| 任务调度 | APScheduler 3.11 | **可使用**。但在当前架构中实际未启用(后台任务用 asyncio.create_task) |
|
||||
| Telegram | httpx + 直接调用 Bot API | **合理**。轻量,无额外依赖 |
|
||||
| 测试 | pytest + httpx + aiosqlite | **方向正确**。但测试覆盖率不足 |
|
||||
| 前端 | PHP + 无框架(AdminLTE 风格) | **遗留包袱**。后续需要重点改造 |
|
||||
|
||||
### 技术栈风险评估
|
||||
|
||||
**低风险**:
|
||||
- FastAPI / Uvicorn — 社区活跃,性能有保障
|
||||
- SQLAlchemy 2.0 async — 经过大规模验证
|
||||
- redis-py + hiredis — 性能优秀
|
||||
- JWT + bcrypt + TOTP — 业界标准认证方案
|
||||
|
||||
**中风险**:
|
||||
- **aiomysql 0.2**: 版本较旧,社区活跃度低于 asyncmy。建议在生产环境充分测试连接稳定性。考虑迁移至 `asyncmy` 或 `mysqlclient` + asyncio 包装。
|
||||
- **paramiko + asyncssh 双 SSH 库**: 两者并存增加了依赖复杂度和安全维护面。应评估统一为一个。
|
||||
- **PHP 前端**: 不是技术栈问题,是维护成本问题。PHP 前端与 Python 后端之间的 API 契约需要严格维护。
|
||||
|
||||
**高风险**:
|
||||
- 无。整体技术栈选择稳健。
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构风险点
|
||||
|
||||
### R1: Service 层 Session 管理隐患 (严重)
|
||||
|
||||
**文件**: `server/api/dependencies.py`
|
||||
|
||||
```python
|
||||
async def get_server_service() -> ServerService:
|
||||
session = AsyncSessionLocal() # 每个请求创建新 session,但未在请求结束时关闭
|
||||
return ServerService(
|
||||
server_repo=_server_repo(session),
|
||||
sync_log_repo=_sync_log_repo(session),
|
||||
)
|
||||
```
|
||||
|
||||
**问题**: `get_server_service()`、`get_script_service()`、`get_auth_service()`、`get_sync_service()` 这四个工厂函数各自创建独立的 `AsyncSession`,但没有通过 FastAPI 的 `Depends` 链来确保 session 在请求结束时被关闭。`get_db()`(第 27 行)正确使用了 `try/finally` 来关闭 session,但 Service 工厂函数没有遵循相同的模式。这会导致 MySQL 连接泄漏。
|
||||
|
||||
**建议**: 所有 Service 的 DI 工厂函数都应接受一个由 `Depends(get_db)` 注入的 session,而不是自己创建。
|
||||
|
||||
### R2: WebSocket 连接池无上限 (严重)
|
||||
|
||||
**文件**: `server/api/websocket.py`
|
||||
|
||||
```python
|
||||
connected_clients: List[WebSocket] = [] # 无上限的全局列表
|
||||
```
|
||||
|
||||
**问题**: `connected_clients` 是无限增长的全局列表。虽然 `self_monitor` 会清理僵死连接,但在高并发场景下(2000+ 服务器的告警风暴),大量前端同时连接可能导致内存问题。缺乏最大连接数限制。
|
||||
|
||||
**建议**:
|
||||
- 设置 `MAX_WS_CLIENTS` 上限
|
||||
- 使用有界数据结构或连接池
|
||||
- 广播时使用 `asyncio.gather()` 而非串行遍历(当前是串行 `for client in ...: await client.send_json()`,N 个客户端时延迟线性增长)
|
||||
|
||||
### R3: Repository 层缺少接口抽象 (中等)
|
||||
|
||||
**文件**: `server/infrastructure/database/server_repo.py` 等
|
||||
|
||||
**问题**: 代码中有 `ServerRepository` 协议(在 `domain/repositories/__init__.py` 中应定义),但具体实现 `ServerRepositoryImpl` 直接依赖 SQLAlchemy `AsyncSession`。虽然这是实现细节,但缺少抽象接口意味着无法轻易替换实现(例如切换到 Redis 或其他存储),也与 Clean Architecture 的理想状态有差距。
|
||||
|
||||
**建议**: 在 domain 层定义 Repository 接口(抽象类或 Protocol),基础设施层实现这些接口,Service 层仅依赖接口。
|
||||
|
||||
### R4: 批量心跳写入性能瓶颈 (中等)
|
||||
|
||||
**文件**: `server/background/heartbeat_flush.py`
|
||||
|
||||
```python
|
||||
for key in keys:
|
||||
# 逐条更新 MySQL
|
||||
await session.execute(update(Server)...)
|
||||
```
|
||||
|
||||
**问题**: 10 分钟的刷新周期内,如果当前有 2000+ 在线服务器逐个执行 `UPDATE`,单次 flush 将产生 2000+ 次 SQL 更新。在 10 分钟内完成可以接受,但如果服务器规模增长到 5000+,逐条更新将成为性能瓶颈。
|
||||
|
||||
**建议**:
|
||||
- 使用 `executemany()` 批量更新
|
||||
- 或使用 `INSERT ... ON DUPLICATE KEY UPDATE` 一次提交
|
||||
- 考虑将 flush 间隔在网络低峰期动态调整
|
||||
|
||||
### R5: CORS 配置过于宽松 (中等)
|
||||
|
||||
**文件**: `server/main.py`
|
||||
|
||||
```python
|
||||
allow_origins=["*"] # TODO: restrict to actual domain in production
|
||||
```
|
||||
|
||||
**问题**: 生产环境仍保留 `allow_origins=["*"]`,这是一个安全风险。虽然前面有 API_KEY 验证,但 CORS 配置过宽增加了 CSRF 类攻击面。
|
||||
|
||||
**建议**: 在部署配置中绑定具体域名,或通过 settings 表动态配置允许的域名列表。
|
||||
|
||||
### R6: 连接池配置与 Settings 映射不匹配 (中等)
|
||||
|
||||
**文件**: `server/config.py`
|
||||
|
||||
```python
|
||||
DB_OVERRIDE_MAP = {
|
||||
"db_pool_size": "DB_POOL_SIZE",
|
||||
"db_max_overflow": "DB_MAX_OVERFLOW",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`server/infrastructure/database/session.py`:
|
||||
|
||||
```python
|
||||
pool_size=max(5, int(settings.DB_POOL_SIZE or 100)),
|
||||
max_overflow=max(5, int(settings.DB_MAX_OVERFLOW or 100)),
|
||||
```
|
||||
|
||||
**问题**: `pool_size` 默认 100 + `max_overflow` 默认 100 = 最大可达 200 个 MySQL 连接。对一个管理平台而言过于激进。MySQL 默认 `max_connections` 为 151,200 个连接可能耗尽数据库连接池,影响数据库本身和其他服务。
|
||||
|
||||
**建议**:
|
||||
- 默认 `pool_size` 设为 20-30
|
||||
- `max_overflow` 设为 10-20
|
||||
- 在启动时自动检测 MySQL `max_connections` 并给出推荐值
|
||||
|
||||
### R7: Telegram 客户端未正确关闭 (低)
|
||||
|
||||
**文件**: `server/infrastructure/telegram/__init__.py`
|
||||
|
||||
```python
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
# 每次发送都创建新的 AsyncClient
|
||||
```
|
||||
|
||||
**问题**: 虽然 Phase D 修复了模块级单例,但每次 `send_telegram` 调用都创建新的 `AsyncClient` + 连接,对于告警风暴场景(同一时刻大量服务器触发告警),可能导致大量短连接。
|
||||
|
||||
**建议**: 确认模块级 `_get_client()` 单例是否已正确实现。每个请求复用同一个 `httpx.AsyncClient` 实例。
|
||||
|
||||
### R8: Redis 连接退化处理不完善 (低)
|
||||
|
||||
**文件**: `server/api/servers.py`
|
||||
|
||||
```python
|
||||
if redis:
|
||||
try:
|
||||
heartbeat = await redis.hgetall(...)
|
||||
except Exception as e:
|
||||
logger.warning(f"Redis read failed: {e}")
|
||||
```
|
||||
|
||||
**问题**: Redis 不可用时,代码正确降级为读取 MySQL 数据,但前端会丢失实时性。当 Redis 恢复后,`get_redis()` 函数不会自动重建连接(`_redis_pool` 仍为 `None`),必须重启 Python 进程才能恢复。
|
||||
|
||||
**建议**: 在 `self_monitor` 循环中加入 Redis 自动重连逻辑,检测到 Redis 恢复后重新初始化连接池。
|
||||
|
||||
---
|
||||
|
||||
## 4. 规模化建议(2000+ 服务器)
|
||||
|
||||
### 4.1 心跳处理能力评估
|
||||
|
||||
**当前设计**:
|
||||
- Agent 每 60s 发送一次心跳
|
||||
- 2000 台服务器 → 平均每秒 33 个心跳请求
|
||||
- Redis 写入:`HSET` 操作,单次约 0.5-1ms
|
||||
- MySQL 每 10min 批量写入
|
||||
|
||||
**评估**: 2000 台服务器的**心跳处理不需要额外优化**。33 QPS 对 FastAPI + Redis 来说非常轻松。瓶颈在 MySQL 批量 flush 环节,但 10min 周期内 2000 条 UPDATE 也完全可行。
|
||||
|
||||
**扩展上限**: 当前架构预估可支撑 5000-8000 台服务器的心跳处理,超过后需要考虑分区策略。
|
||||
|
||||
### 4.2 批量文件推送评估
|
||||
|
||||
**当前设计**:
|
||||
- 并发 10 个 SSH 连接
|
||||
- 每批 50 台,批间隔 30s
|
||||
- 2000 台服务器需 40 批 x 30s = 20 分钟
|
||||
|
||||
**评估**: 20 分钟完成全量推送是可接受的。但并发 10 在 2000 台规模下偏保守。对于紧急安全更新,应将并发提高到 50-100,并支持"分组推送"(按节点/平台分组并行)。
|
||||
|
||||
### 4.3 建议的规模化措施
|
||||
|
||||
| 领域 | 措施 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 数据库 | 默认连接池从 100→25,减少 MySQL 连接压力 | P0 |
|
||||
| 数据库 | 添加 `idx_servers_last_heartbeat` 索引 | P1 |
|
||||
| 缓存 | `/api/servers/` 列表添加 30s Redis 缓存(已在计划中 P1-9) | P1 |
|
||||
| 缓存 | stats 仪表盘 COUNT 查询合并为一次 SQL(已在计划中 P1-8) | P1 |
|
||||
| 推送 | 支持多组并发推送(按节点分组并行) | P2 |
|
||||
| 推送 | 推送任务队列化(Redis List/Bull)而非直接 asyncio.gather | P2 |
|
||||
| 监控 | 告警聚合:同一服务器连续告警合并,避免告警风暴 | P2 |
|
||||
| 前端 | API 响应分页(当前 `/api/servers/` 返回全量列表) | P2 |
|
||||
| 前端 | 虚拟滚动 / 无限加载 替代全量渲染 | P3 |
|
||||
| Agent | 心跳支持批量上报(合并多台服务器心跳) | P3 |
|
||||
|
||||
### 4.4 关键瓶颈识别
|
||||
|
||||
1. **MySQL 连接池耗尽**: 当前默认 200 个连接过于激进,建议调低至 25-50
|
||||
2. **/api/servers/ 全量查询**: 不加分页的情况下,2000 台服务器的列表查询响应可能超过 1 秒
|
||||
3. **告警风暴**: 大规模故障时(如网络分区),2000 台服务器同时触发告警,WebSocket + Telegram 会被打满
|
||||
4. **SSH 推送排队**: 20 分钟全量推送周期对于紧急补丁偏长
|
||||
|
||||
---
|
||||
|
||||
## 5. 技术路线建议
|
||||
|
||||
### 5.1 短期 (1-2 个月) — 架构加固
|
||||
|
||||
1. **P0: 修复 Service 层 Session 泄漏**
|
||||
- 所有 Service DI 函数改为接收 `Depends(get_db)` 注入的 session
|
||||
- 消除 MySQL 连接泄漏风险
|
||||
|
||||
2. **P0: 调低默认连接池大小**
|
||||
- `DB_POOL_SIZE` 从 100 改为 20-30
|
||||
- `DB_MAX_OVERFLOW` 从 100 改为 10-20
|
||||
|
||||
3. **P1: WebSocket 架构改进**
|
||||
- 增加最大连接数限制
|
||||
- 广播改为 `asyncio.gather()` 并发推送
|
||||
- 添加客户端心跳保活机制
|
||||
|
||||
4. **P1: Redis 自动重连**
|
||||
- `self_monitor` 中加入 Redis 连接自动恢复
|
||||
- 消除 Redis 故障后必须重启 Python 的运维痛点
|
||||
|
||||
5. **P1: 统一 SSH 库**
|
||||
- 评估 asyncssh 是否能完全替代 paramiko
|
||||
- 选择一个作为标准 SSH 库,减少维护面
|
||||
|
||||
### 5.2 中期 (3-6 个月) — 规模化增强
|
||||
|
||||
1. **告警聚合与降噪**
|
||||
- 实施告警聚合窗口(相同类型告警 5 分钟内合并)
|
||||
- 告警风暴自动降级(批量告警时改为汇总通知)
|
||||
- WebSocket 广播加频率限制
|
||||
|
||||
2. **API 分页**
|
||||
- `/api/servers/` 添加 `limit/offset` 参数
|
||||
- 前端实现虚拟滚动
|
||||
|
||||
3. **推送引擎升级**
|
||||
- 支持多组并行推送
|
||||
- 推送任务持久化队列(Redis List)
|
||||
- 增加推送进度 WebSocket 实时推送
|
||||
|
||||
4. **DB 迁移到 asyncmy**
|
||||
- 从 aiomysql 0.2 迁移到 asyncmy(更活跃的社区)
|
||||
- 或评估 PostgreSQL 作为长期数据库方案
|
||||
|
||||
### 5.3 长期 (6-12 个月) — 架构演进
|
||||
|
||||
1. **PHP 前端迁移**
|
||||
- 评估 Vue 3 / React 作为新一代前端框架
|
||||
- 建立前后端分离的 API 契约(已在逐步实现 FastAPI backend)
|
||||
- 分阶段替换:先替换核心功能(仪表盘、服务器列表),再替换管理功能
|
||||
|
||||
2. **Agent 协议标准化**
|
||||
- 当前 Agent 协议偏简单,建议标准化为 protobuf 或 msgpack
|
||||
- 支持批量心跳上报
|
||||
- Agent 侧增加本地缓存,网络中断时自动缓冲
|
||||
|
||||
3. **多集群支持**
|
||||
- 当服务器规模超过 5000 时,考虑多 Region / 多集群部署
|
||||
- 主-从 Redis 或 Redis Cluster
|
||||
- 跨集群心跳汇聚
|
||||
|
||||
4. **可观测性体系**
|
||||
- 接入 OpenTelemetry 标准
|
||||
- 关键链路(心跳处理、文件推送、WebSocket 广播)增加 tracing
|
||||
- 建立基于 Prometheus + Grafana 的监控面板
|
||||
|
||||
5. **声明式配置管理**
|
||||
- 从"脚本推送"演进到"期望状态管理"
|
||||
- 引入配置版本管理(GitOps 风格)
|
||||
- 服务器配置漂移检测与自动修复
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
Nexus 6.0 在架构设计上**走在正确的方向上**。Clean Architecture 分层、异步全栈、Redis 缓存层、双通道告警推送等核心设计思路符合当前 2000+ 服务器的运维管理需求。代码质量整体良好,Domain 模型表达清晰。
|
||||
|
||||
**最需要立即处理的问题**:
|
||||
1. Service 层 Session 泄漏 — **P0 安全级别**,可能导致生产环境 MySQL 连接耗尽
|
||||
2. 默认连接池过大 — **P0 性能级别**,200 个默认连接可能压垮 MySQL 服务器
|
||||
3. WebSocket 广播串行推送 — **P1 性能级别**,连接数多时延迟显著
|
||||
|
||||
**长期战略方向**:
|
||||
- 逐步替换 PHP 前端为现代前端框架
|
||||
- 建立完善的告警降噪与聚合机制
|
||||
- 从"脚本推送"向"声明式配置管理"演进
|
||||
- 建立完整的可观测性体系
|
||||
|
||||
---
|
||||
|
||||
*本报告由 CTO 自动审查生成,基于代码分析、架构评估和规模化推演。*
|
||||
@@ -0,0 +1,355 @@
|
||||
# 运维总监审查报告
|
||||
|
||||
**项目**: Nexus 6.0 — 服务器运维管理平台
|
||||
**审查时间**: 2026-05-21
|
||||
**审查范围**: deploy/ 全部配置、docs/project/deploy.md 部署指南、server/config.py 配置项、server/main.py 启动配置、server/background/ 后台任务、server/infrastructure/ 基础设施层
|
||||
**审查人**: 运维总监
|
||||
|
||||
---
|
||||
|
||||
## 1. 部署架构总览
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Let's Encrypt │ (生产环境 HTTPS)
|
||||
│ (宝塔面板) │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌──────▼───────┐
|
||||
│ Nginx │ ← 反向代理 + 静态文件 + PHP-FPM
|
||||
│ (80/443) │
|
||||
└──┬───────┬──┘
|
||||
│ │
|
||||
┌─────────────▼┐ ┌───▼──────────┐
|
||||
│ FastAPI │ │ PHP-FPM │
|
||||
│ :8600 │ │ (旧前端) │
|
||||
│ (4 workers) │ │ │
|
||||
└──┬───────┬───┘ └──────────────┘
|
||||
│ │
|
||||
┌──────▼──┐ ┌──▼──────────┐
|
||||
│ Redis │ │ MySQL 8.4 │
|
||||
│ (实时) │ │ (历史) │
|
||||
└─────────┘ └─────────────┘
|
||||
|
||||
3层守护机制:
|
||||
Layer 1: Supervisor ─── Python崩溃 → 自动重启
|
||||
Layer 2: self_monitor ── 30s检查Redis/MySQL/WS → Telegram告警
|
||||
Layer 3: health_monitor ── 1min HTTP GET /health → 连续3次失败 → restart + Telegram
|
||||
```
|
||||
|
||||
**整体评价**: 架构设计合理,三层守护覆盖全面,数据流清晰(Agent → Redis → 前端直读 → 10min → MySQL)。核心问题在于**配置一致性偏差**、**资源预留过大**、**安全防护薄弱**。
|
||||
|
||||
---
|
||||
|
||||
## 2. Supervisor配置评估
|
||||
|
||||
**文件**: `deploy/nexus.conf`
|
||||
|
||||
### 优点
|
||||
- `autorestart=true` + `startretries=10` — 崩溃自动重启,最多重试10次
|
||||
- `stopsignal=INT` — SIGINT发送给uvicorn,可实现优雅关闭
|
||||
- `stopwaitsecs=10` — 给进程10秒处理完当前请求
|
||||
- 日志自动轮转:50MB切割,保留5个备份
|
||||
- 不硬编码密钥(依赖.env文件)— 安全合规
|
||||
|
||||
### 问题
|
||||
|
||||
| 严重度 | 问题 | 说明 |
|
||||
|--------|------|------|
|
||||
| **高** | 单Worker运行 (`--workers` 缺失) | `nexus.conf` 只用1个worker,但文档 `deploy.md` 的示例配置写着 `--workers 4`。两者不一致。实际线上用1个worker处理所有请求(包括心跳、WebSocket、API),单点过载风险。详见第8节。 |
|
||||
| **中** | `startsecs=3` 偏短 | 如果应用启动耗时超过3秒(如数据库表较多时init_db可能较慢),Supervisor会误判为启动失败。建议改为 `startsecs=10` |
|
||||
| **中** | `user=root` 运行 | Python进程以root权限运行。如果FastAPI出现RCE漏洞,攻击者可获得服务器完全控制权。建议创建专用系统用户 `nexus` |
|
||||
| **低** | 缺少 `redirect_stderr=true` | 当前将stderr和stdout分别写入两个文件。设为 `redirect_stderr=true` 可统一日志管理 |
|
||||
| **低** | 无 `environment` 变量 | 虽然依赖.env文件启动,但指定 `PYTHONPATH` 环境变量可以避免潜在的导入路径问题 |
|
||||
|
||||
### 建议修复
|
||||
|
||||
```ini
|
||||
[program:nexus]
|
||||
command=/www/wwwroot/api.synaglobal.vip/venv/bin/uvicorn server.main:app --host 0.0.0.0 --port 8600 --workers 4
|
||||
directory=/www/wwwroot/api.synaglobal.vip
|
||||
user=nexus # 改为专用系统用户
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startretries=10
|
||||
startsecs=10 # 加长启动验证时间
|
||||
stopwaitsecs=20 # 加长优雅关闭等待
|
||||
stopsignal=INT
|
||||
environment=
|
||||
PATH="/www/wwwroot/api.synaglobal.vip/venv/bin:%(ENV_PATH)s",
|
||||
PYTHONPATH="/www/wwwroot/api.synaglobal.vip:%(ENV_PATH)s"
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/nexus/nexus.log
|
||||
stdout_logfile_maxbytes=50MB
|
||||
stdout_logfile_backups=5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Nginx配置评估
|
||||
|
||||
**文件**: `deploy/nginx_172.31.170.47.conf`(WSL环境)
|
||||
|
||||
### 优点
|
||||
- `upstream nexus_api { keepalive 32; }` — 使用连接池,减少TCP握手开销
|
||||
- 清晰的全域名/API/WebSocket/静态文件分离路由
|
||||
- 敏感文件保护:`location ~ /\. { deny all; }` + `location ~ /data/config\.php$ { deny all; }`
|
||||
- 静态资源7天缓存:`expires 7d; add_header Cache-Control "public, immutable";`
|
||||
- WebSocket代理配置正确:`Upgrade` / `Connection` 头、`proxy_read_timeout 180s`
|
||||
- `client_max_body_size 100m` — 适合文件管理功能
|
||||
|
||||
### 问题
|
||||
|
||||
| 严重度 | 问题 | 说明 |
|
||||
|--------|------|------|
|
||||
| **高** | 无HTTPS配置 | WSL环境的Nginx配置只有HTTP 80。对于运维管理平台,即使在WSL内网也应启用HTTPS。至少应配置自签名证书。 |
|
||||
| **高** | 无安全响应头 | 缺少 `X-Content-Type-Options`、`X-Frame-Options`、`Content-Security-Policy`、`Strict-Transport-Security` 等关键安全头 |
|
||||
| **中** | 无速率限制 | API端点和登录接口缺少 `limit_req` 配置,存在暴力破解和DDoS风险 |
|
||||
| **中** | 无请求体大小限制 | API代理未设置 `client_max_body_size`,但全局设置了100m,而API代理不应允许这么大的请求体 |
|
||||
| **中** | `location /health` 缺少转发头 | 健康检查代理未设置 `X-Real-IP` 等头信息(虽然对健康检查影响不大,但不一致) |
|
||||
| **低** | 无压缩配置 | 未启用gzip/brotli压缩,JSON API响应可能浪费带宽 |
|
||||
| **低** | 无日志格式自定义 | 使用默认Combined格式,缺少上游响应时间等关键指标 |
|
||||
| **低** | WSL配置和生产配置分离 | 存在两份Nginx配置(WSL + 生产),但生产配置在deploy.md中而非独立文件 |
|
||||
|
||||
### 生产环境安全头建议(补充到Nginx配置)
|
||||
|
||||
```nginx
|
||||
# 在 server block 中添加
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# API速率限制
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
|
||||
location /api/ {
|
||||
limit_req zone=api burst=50 nodelay;
|
||||
# ... 现有配置
|
||||
}
|
||||
|
||||
# 登录接口限制(更严格)
|
||||
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
|
||||
location /api/auth/ {
|
||||
limit_req zone=login burst=10 nodelay;
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 三层守护机制评估
|
||||
|
||||
### 4.1 Layer 1: Supervisor
|
||||
|
||||
| 维度 | 评估 | 说明 |
|
||||
|------|------|------|
|
||||
| 崩溃检测 | ✅ | 进程退出时,Supervisor即时感知 |
|
||||
| 自动重启 | ✅ | `autorestart=true` + `startretries=10` |
|
||||
| 优雅关闭 | ✅ | SIGINT + 10秒等待 |
|
||||
| 启动验证 | ⚠️ | `startsecs=3` 可能偏短,建议10秒 |
|
||||
| 频繁崩溃防护 | ⚠️ | 10次重试后停止,但未配置 `startretryinterval`(默认0=无间隔),可能瞬间耗尽重试次数 |
|
||||
|
||||
### 4.2 Layer 2: Python self_monitor
|
||||
|
||||
| 维度 | 评估 | 说明 |
|
||||
|------|------|------|
|
||||
| 检查间隔 | ✅ | 每30秒 |
|
||||
| Redis检测 | ✅ | 返回None时告警 |
|
||||
| MySQL检测 | ✅ | 执行SELECT 1 |
|
||||
| WebSocket清理 | ✅ | 移除过期客户端 |
|
||||
| **自动修复** | ❌ | **仅发送Telegram告警,不做任何修复操作** |
|
||||
| 连续失败降级 | ❌ | 无降级逻辑,每次失败都发告警(可能造成告警轰炸) |
|
||||
|
||||
**严重问题**: Layer 2只有"检测+告警"能力,没有任何自动修复动作。例如Redis连接丢失后,self_monitor只是发一条Telegram消息,不做重连尝试。虽然 `get_redis()` 函数在检测到连接丢失后会置 `_redis_pool = None`,下次调用会尝试重连,但self_monitor自身的Redis检测逻辑并未利用这个机制重连以提高成功率。
|
||||
|
||||
### 4.3 Layer 3: Shell health_monitor.sh
|
||||
|
||||
| 维度 | 评估 | 说明 |
|
||||
|------|------|------|
|
||||
| 独立性 | ✅ | 纯Shell脚本,不依赖Python |
|
||||
| 连续失败阈值 | ✅ | 3次连续失败才触发重启 |
|
||||
| 重启后验证 | ✅ | restart后sleep 5秒,再验证 |
|
||||
| Telegram通知 | ✅ | 失败/恢复/人工介入三类通知 |
|
||||
| 配置来源 | ✅ | 从config.php或.env读取Telegram凭证 |
|
||||
|
||||
**严重问题**: 循环重启风暴风险。当连续3次失败后,脚本调用 `supervisorctl restart`,如果重启后验证仍然失败,**不会重置FAIL计数器**。这意味着每次cron执行都会再次触发 `supervisorctl restart`(因为FAIL计数器保持在3+)。这会造成每分钟一次的循环重启风暴。正确的做法应该是增加"冷却期"机制——例如连续N次重启失败后,停止自动重启并仅发送人类介入通知。
|
||||
|
||||
### 4.4 三层联动评估
|
||||
|
||||
```
|
||||
场景: Python进程崩溃
|
||||
Layer 1: Supervisor 即时检测 → 秒级重启 ✓
|
||||
Layer 2: 如果Python在30s内恢复,self_monitor不会触发告警 ✓
|
||||
Layer 3: 如果1分钟内恢复,health_monitor不会触发(FAIL计数器不会达到3)✓
|
||||
|
||||
场景: Python进程僵死(不崩溃但无响应)
|
||||
Layer 1: 进程未退出,Supervisor不触发 ✗
|
||||
Layer 2: self_monitor只要asyncio事件循环还能运行就能检测,但如果整个进程僵死则不行 ✗
|
||||
Layer 3: HTTP GET /health超时,3次后触发supervisorctl restart ✓
|
||||
|
||||
场景: Redis宕机
|
||||
Layer 1: 不触发(Python进程正常运行)✓
|
||||
Layer 2: 30s内检测到Redis丢失,发Telegram告警(但不尝试修复)⚠️
|
||||
Layer 3: 不触发(/health端点仍能响应,只是标记redis=unavailable)✓(正确)
|
||||
|
||||
场景: MySQL连接池耗尽
|
||||
Layer 1: 不触发 ✓
|
||||
Layer 2: 30s内检测到MySQL异常,发Telegram告警 ⚠️
|
||||
Layer 3: 不触发(HTTP健康检查返回python=ok)✓(正确)
|
||||
```
|
||||
|
||||
**评估**: 三层守护的覆盖域合理,各层分工明确。核心缺失是 Layer 2 缺乏自动修复能力。循环重启风暴是需要优先修复的风险。
|
||||
|
||||
---
|
||||
|
||||
## 5. 运维风险点
|
||||
|
||||
### 5.1 高风险
|
||||
|
||||
| # | 风险 | 影响 | 建议 |
|
||||
|---|------|------|------|
|
||||
| R1 | **单Worker生产运行** | 一个worker处理所有WebSocket连接、心跳接收、API请求。一旦某个请求阻塞(如慢查询),所有其他请求(包括心跳)都会排队等待。后果:Agent心跳超时、WebSocket断连、健康检查失败触发误重启。 | 使用 `--workers 4` 并启用 `--ws max_size=16` 等uvicorn优化参数 |
|
||||
| R2 | **循环重启风暴** | health_monitor在连续3次失败后,每分钟执行一次 `supervisorctl restart`,无论重启是否成功。如果根本原因(如配置错误、端口冲突)未解决,会造成每分钟的反复重启循环。 | 增加冷却期:连续N次重启失败后,停止自动重启,仅发送"需人工介入"告警,等待操作员处理 |
|
||||
| R3 | **Root运行所有进程** | Supervisor配置 `user=root`,Python进程以root身份运行。如果FastAPI存在任意代码执行漏洞(如依赖库CVE),攻击者可获得服务器完全控制权。 | 创建 `nexus` 系统用户,最小权限原则 |
|
||||
| R4 | **CORS全开放** | `allow_origins=["*"]`,生产环境也开放。虽然当前是PHP前端同域部署,CORS问题不显现,但一旦前端分离部署或API被直接访问,风险极大。 | 改为明确的白名单域名。当前PHP前端部署在同一域名下,CORS需求极低。 |
|
||||
|
||||
### 5.2 中风险
|
||||
|
||||
| # | 风险 | 影响 | 建议 |
|
||||
|---|------|------|------|
|
||||
| R5 | **无数据库连接池监控** | `DB_POOL_SIZE=100` + `DB_MAX_OVERFLOW=100`,最大潜在连接数200。MySQL 8.4默认`max_connections=151`,**配置的池大小已经超过数据库上限**。一旦多个worker实例运行(4 workers x 100 pool = 400连接),必然导致连接失败。 | 1) 调低 `DB_POOL_SIZE=20`、`DB_MAX_OVERFLOW=20`(单worker)。2) 如用4 workers,则每个worker `pool_size=10`。3) 增加连接池监控告警 |
|
||||
| R6 | **健康检查端口固定** | `HEALTH_URL` 硬编码为 `http://127.0.0.1:8600/health`,如果后续端口变更需同时修改脚本。 | 改为从配置文件读取或作为脚本参数传入 |
|
||||
| R7 | **异常处理宽泛** | `load_settings_from_db` 中 `except Exception` 静默吞掉所有异常,返回0。如果settings表结构变更或数据类型异常,不会被及时发现。 | 区分可忽略异常和需要告警的异常,对关键错误发送运维告警 |
|
||||
| R8 | **无日志集中管理** | Supervisor日志写在 `/var/log/nexus/`,Nginx日志在 `/www/wwwlogs/`,Python日志可能分散到stdout。无统一日志中心,故障排查需要手动查看多个位置。 | 使用 systemd-journald 或 rsyslog 统一收集,考虑接入 ELK/Loki |
|
||||
| R9 | **文档与配置不一致** | `deploy.md` 中Nexus Supervisor配置示例使用 `--workers 4`,`startretries`, `startsecs` 等未列出。实际 `nexus.conf` 只有单worker。部署新环境时可能按文档配置4 workers,但本地nexus.conf是1 worker。 | 同步文档和实际配置,确保部署指南与部署文件一致 |
|
||||
| R10 | **无数据库备份策略** | 项目中未找到自动备份脚本或策略。MySQL存储心跳历史、告警配置、服务器信息,一旦数据丢失将造成严重后果。 | 实现 `mysqldump` 或 `XtraBackup` 每日备份 + 7天轮转 |
|
||||
|
||||
### 5.3 低风险
|
||||
|
||||
| # | 风险 | 影响 | 建议 |
|
||||
|---|------|------|------|
|
||||
| R11 | 无监控面板 | 无Grafana/Prometheus等可视化监控 | 接入node_exporter + Prometheus |
|
||||
| R12 | 无容量规划 | 心跳数据量增长后MySQL性能未知 | 在1000+服务器规模下进行压测 |
|
||||
| R13 | WSL无HTTPS | 管理流量明文传输 | 至少配置自签名证书 |
|
||||
| R14 | 缺少staging环境 | 变更直接上生产 | 保持WSL作为staging环境 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 改进建议
|
||||
|
||||
### P0 — 立即修复(影响生产稳定性)
|
||||
|
||||
1. **Supervisor启用多Worker**
|
||||
```bash
|
||||
# nexus.conf 中 command 添加 --workers 4
|
||||
command=/www/wwwroot/api.synaglobal.vip/venv/bin/uvicorn server.main:app --host 0.0.0.0 --port 8600 --workers 4
|
||||
```
|
||||
**注意**: 多Worker下WebSocket连接会分配给不同进程,需要使用Redis Pub/Sub做跨进程WebSocket广播。如果当前WebSocket实现(`connected_clients` 是内存集合)不支持跨进程,则需评估。推荐启用 `--ws ping_interval 20`。
|
||||
|
||||
2. **health_monitor.sh 增加重启冷却**
|
||||
```bash
|
||||
# 在重启失败后增加冷却文件
|
||||
COOLDOWN_FILE="/tmp/nexus_restart_cooldown"
|
||||
COOLDOWN_PERIOD=10 # 10分钟内不再自动重启
|
||||
|
||||
# 在重启失败分支中
|
||||
if [ "$FAIL" -ge "$MAX_FAIL" ]; then
|
||||
# 检查冷却期
|
||||
COOLDOWN=$(cat "$COOLDOWN_FILE" 2>/dev/null || echo "0")
|
||||
NOW=$(date +%s)
|
||||
if [ "$COOLDOWN" -gt "$NOW" ]; then
|
||||
send_telegram "⚠️ Nexus重启冷却中(跳过自动重启)"
|
||||
exit 0
|
||||
fi
|
||||
# ... 执行重启 ...
|
||||
# 如果重启失败,设置10分钟冷却
|
||||
echo "$((NOW + COOLDOWN_PERIOD * 60))" > "$COOLDOWN_FILE"
|
||||
fi
|
||||
```
|
||||
|
||||
3. **创建专用服务用户**
|
||||
```bash
|
||||
sudo useradd -r -s /usr/sbin/nologin -M nexus
|
||||
sudo chown -R nexus:nexus /www/wwwroot/api.synaglobal.vip
|
||||
sudo chmod 640 /www/wwwroot/api.synaglobal.vip/.env # 限制.env访问
|
||||
```
|
||||
|
||||
### P1 — 短期改进(本周内)
|
||||
|
||||
4. **调优数据库连接池**
|
||||
```python
|
||||
# server/config.py
|
||||
DB_POOL_SIZE: Optional[int] = 20 # 从100下调
|
||||
DB_MAX_OVERFLOW: Optional[int] = 20 # 从100下调
|
||||
```
|
||||
相应调整MySQL的 `max_connections`:`SET GLOBAL max_connections = 200;`(在 `/etc/mysql/my.cnf` 中持久化)
|
||||
|
||||
5. **Nginx增加安全头和生产配置文件**
|
||||
- 从 `deploy.md` 中提取生产Nginx配置为独立文件 `deploy/nginx_production.conf`
|
||||
- 增加安全响应头、速率限制
|
||||
|
||||
6. **self_monitor增加自动修复逻辑**
|
||||
- Redis丢失时:主动调用 `get_redis()` 触发重连(现有代码已支持,但需确保self_monitor层面触发)
|
||||
```python
|
||||
# Redis check — 主动触发重连
|
||||
redis = await get_redis()
|
||||
if redis is None and settings.REDIS_URL:
|
||||
await send_telegram_system_alert("⚠️ Redis连接丢失,正在尝试重连...")
|
||||
_redis_pool = None # 强制下次get_redis重新创建连接
|
||||
redis = await get_redis()
|
||||
if redis:
|
||||
await send_telegram_system_alert("✅ Redis已重新连接")
|
||||
```
|
||||
|
||||
### P2 — 中期规划(本月内)
|
||||
|
||||
7. **实现数据库自动备份**
|
||||
```bash
|
||||
# deploy/backup.sh
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/var/backups/nexus/mysql"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
mysqldump --single-transaction --routines --events Nexus | gzip > "$BACKUP_DIR/nexus_$(date +%Y%m%d).sql.gz"
|
||||
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete # 保留30天
|
||||
```
|
||||
配置cron:`0 3 * * * /www/wwwroot/api.synaglobal.vip/deploy/backup.sh`
|
||||
|
||||
8. **接入运维监控**
|
||||
- 安装 `node_exporter` + `Prometheus` + `Grafana`
|
||||
- 导入Nexus `/health` 端点到Prometheus
|
||||
- 设置关键告警规则:CPU>90%、磁盘>85%、Python进程Down、MySQL连接数>150
|
||||
|
||||
9. **Redis Sentinel/Cluster高可用**
|
||||
- 当前单点Redis:一旦宕机,所有实时心跳数据和告警状态丢失
|
||||
- 建议部署Redis Sentinel实现自动故障切换,或至少配置Redis AOF持久化
|
||||
|
||||
### P3 — 长期规划
|
||||
|
||||
10. **容器化部署**(Docker Compose/K8s)
|
||||
- 将FastAPI/Nginx/Redis/PHP容器化,消除环境不一致问题
|
||||
- 便于水平扩展
|
||||
|
||||
11. **灰度发布策略**
|
||||
- 实现蓝绿部署或canary发布机制,减少变更风险
|
||||
|
||||
---
|
||||
|
||||
## 7. 总结
|
||||
|
||||
Nexus的部署架构在整体设计上是稳健的,三层守护机制覆盖了进程崩溃、服务僵死、依赖服务故障等常见场景。然而,存在几个**必须立即处理**的风险:
|
||||
|
||||
1. **单worker瓶颈** — 这是当前最致命的性能隐患,2000+服务器的Agent心跳(60秒间隔)单worker可能无法承受
|
||||
2. **循环重启风暴** — health_monitor脚本在连续失败后每分钟执行restart,可能造成服务抖动
|
||||
3. **数据库连接池超配** — 100+100的池大小已经超过MySQL 151的默认上限,多worker后问题更严重
|
||||
4. **Root运行和服务用户缺失** — 安全基线问题
|
||||
5. **CORS全开** — 不必要且不安全
|
||||
|
||||
短期改进优先级:
|
||||
1. 先调低数据库连接池(写死的风险 > 配置不一致的风险)
|
||||
2. 再修复health_monitor冷却期
|
||||
3. 然后评估多worker+WebSocket兼容性后启用
|
||||
4. 最后补充Nginx安全配置
|
||||
|
||||
以上改进建议的预计工时:P0项约2小时,P1项约4小时,P2项约2天(含Grafana部署调试)。
|
||||
@@ -0,0 +1,237 @@
|
||||
# 质量总监审查报告
|
||||
|
||||
> **审查日期**: 2026-05-21
|
||||
> **审查范围**: Nexus 项目全量代码、文档、流程
|
||||
> **审查人**: 质量总监(自动化审查)
|
||||
|
||||
---
|
||||
|
||||
## 1. 质量标准现状评估
|
||||
|
||||
### 1.1 已建立的质量标准
|
||||
|
||||
项目已具备部分明确的质量基础:
|
||||
|
||||
| 标准项 | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| 安全模型(Auth=Trust) | 已定义 | `docs/design/specs/design-standards.md` 第1节 |
|
||||
| 性能基线 | 已定义 | 并发≤50, P50<60ms, P95<300ms, 文件上传≤500MB |
|
||||
| 测试标准 | 已定义 | 浏览器验收清单(5项),含历史教训记录 |
|
||||
| "不做清单" | 已定义 | 6项永久不做的决策(AsyncSession、移动端、MQTT等) |
|
||||
| 配置存储分层 | 已定义 | Redis热层→MySQL持久层→文件启动必需 |
|
||||
| 代码规范PHP | 部分定义 | auth.php 引入要求、safePath、escapeshellarg、CORS规则 |
|
||||
|
||||
### 1.2 缺失的质量标准
|
||||
|
||||
| 缺失项 | 风险等级 | 说明 |
|
||||
|--------|---------|------|
|
||||
| Python代码风格规范 | **高** | 未要求PEP 8、未配置ruff/flake8等linter |
|
||||
| PHP代码风格规范 | **高** | 未要求PSR-12、未配置phpstan等静态分析 |
|
||||
| 测试覆盖率目标 | **高** | 无最低覆盖率要求,无法衡量测试充分性 |
|
||||
| Code Review强制规则 | **中** | 章程中提到AG组代码审查,但无必须通过的gate机制 |
|
||||
| 性能回归阈值 | **中** | 基线已定义,但无回归检测机制 |
|
||||
| API契约标准 | **中** | 无正式OpenAPI规范审查要求 |
|
||||
| 数据库迁移规范 | **低** | 有migrations目录但未使用Alembic版本化管理 |
|
||||
|
||||
**评估结论**: 质量标准**基础框架已建立但不完备**。安全、性能基线层面可控,但在代码规范、测试覆盖、自动化验证方面存在明显缺口。
|
||||
|
||||
---
|
||||
|
||||
## 2. 代码规范一致性
|
||||
|
||||
### 2.1 Python 后端 (server/) — 3,866 行
|
||||
|
||||
**Clean Architecture 遵循度高**:
|
||||
- 明确的四层分离:`api/` (presentation)、`application/services/`、`domain/`、`infrastructure/`
|
||||
- 依赖注入通过 `api/dependencies.py` 统一管理
|
||||
- Repository 使用 Protocol 接口实现依赖反转
|
||||
- 所有模块有模块级 docstring
|
||||
- 类型提示 (type hints) 在 Python 后端**全面使用**
|
||||
|
||||
**发现的规范问题**:
|
||||
|
||||
| 问题 | 文件 | 严重程度 |
|
||||
|------|------|---------|
|
||||
| `datetime.utcnow()` 已弃用 (Python 3.12) | `domain/models/__init__.py` 全部模型 | **高** — 所有29处使用均需替换为 `datetime.now(datetime.UTC)` |
|
||||
| Redis心跳数据覆盖逻辑重复(两处完全相同) | `api/servers.py:list_servers` 和 `get_server` | **中** — 约35行重复代码 |
|
||||
| API端点使用 `dict` 而非 Pydantic model 做入参校验 | `api/servers.py:create_server` | **中** — 未使用正规请求模型 |
|
||||
| `conftest.py` 含 TODO 和未完成的依赖覆盖 | `tests/conftest.py:55` | **中** — 模拟基础设施不完整 |
|
||||
| `cron` 拼写为 `corn` | `api/settings.py` / `config.py` | **低** — 不影响功能 |
|
||||
| ssh/pool.py 使用同步 `paramiko` + `asyncio.to_thread` | `infrastructure/ssh/pool.py` | **低** — 已知设计决策,但文档未记录原因 |
|
||||
|
||||
### 2.2 PHP 前端 (web/) — 14,272 行
|
||||
|
||||
**规范遵循度中等**:
|
||||
- `auth.php` 鉴权引入规范基本遵循(所有受保护页面有 `require_once auth.php`)
|
||||
- `safePath()` 路径校验在 `api_proxy.php` 中实现
|
||||
- `escapeshellarg()` 在命令执行处使用
|
||||
- CORS 动态 Host 匹配已实现
|
||||
|
||||
**发现的规范问题**:
|
||||
|
||||
| 问题 | 文件 | 严重程度 |
|
||||
|------|------|---------|
|
||||
| 品牌名称不一致:Nexus vs MultiSync | 多处:`_sidebar.php`(MultiSync), `_layout_head.php`(MultiSync), `files.php`(MultiSync) | **高** — 用户可见的品牌分裂 |
|
||||
| 版本号不一致:v6.0.0 vs v1.0.0 | `_sidebar.php:8` (v1.0.0) vs `config.php:25` (v6.0.0) | **高** — 用户可见的信息矛盾 |
|
||||
| inline CSS 混在 PHP 字符串中 | `files.php` 全文(约500行CSS在PHP echo中) | **中** — 极端不可维护 |
|
||||
| AdminLTE CSS/JS 仍被引用 | `_layout_head.php:12-13` bootstrap+adminlte CDN | **中** — 与"全面弃用AdminLTE"路线图矛盾 |
|
||||
| 中英文注释混用 | 几乎所有PHP文件 | **低** — 团队需统一标准 |
|
||||
| config.php MySQL查询:6次独立SELECT | `config.php` | **低** — 可优化为单次查询 |
|
||||
|
||||
### 2.3 部署/运维代码
|
||||
|
||||
| 文件 | 质量评价 |
|
||||
|------|---------|
|
||||
| `deploy/nexus.conf` (Supervisor) | **优秀** — 清晰注释、合理参数配置 |
|
||||
| `deploy/health_monitor.sh` (Shell健康检查) | **良好** — 多来源Telegram令牌读取、完善的失败计数逻辑 |
|
||||
| `deploy/migrations/001_indexes.sql` | **良好** — 但缺少Alembic版本化 |
|
||||
| `deploy/nginx_*.conf` | **未审查全身** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 文档质量评估
|
||||
|
||||
### 3.1 项目文档体系
|
||||
|
||||
| 文档 | 评级 | 评价 |
|
||||
|------|------|------|
|
||||
| `docs/project/status.md` | A | 全面覆盖数据库结构、功能决策、ADR、迭代计划、兼容约束 |
|
||||
| `docs/project/roadmap.md` | A | 功能规划清晰,冲突/重复处理完善,实现顺序明确 |
|
||||
| `docs/team/collaboration-charter.md` | A | 8部门职责、5阶段流程、决策权限矩阵、沟通规则详尽 |
|
||||
| `docs/design/specs/design-standards.md` | A- | 完整的安全/配置/性能/测试标准,但缺少代码风格规范 |
|
||||
| `docs/changelog/fix-plan-full-2026-05-20.md` | B+ | 24项P0-P3修复完整排期,但缺少执行后的验证状态更新 |
|
||||
| `docs/project/deploy.md` | 未详细审查 | |
|
||||
|
||||
### 3.2 文档密度问题
|
||||
|
||||
`docs/skills/` 目录包含 **150+ 个 markdown 文件**,覆盖 8 个部门/团队的角色定义。这是一个**双刃剑**:
|
||||
- **正面**: 角色职责清晰,每个技能有独立文档
|
||||
- **负面**: 维护成本高,容易与实际代码脱节
|
||||
|
||||
`docs/memory/` 目录有 **15 个文件**,存在碎片化风险 —— 同样信息分散在多个文件中可能导致不一致。
|
||||
|
||||
### 3.3 文档缺口
|
||||
|
||||
| 缺口 | 影响 |
|
||||
|------|------|
|
||||
| 缺少 API 文档(除 Swagger/OpenAPI 自动生成外) | 对第三方集成不友好 |
|
||||
| 缺少开发者 onboarding 指南 | 新人上手成本高 |
|
||||
| 缺少 changelog(当前只有 fix plan 而非 release notes) | 版本间变更不可追溯 |
|
||||
| 缺少架构验证/ADR 实际执行状态 | 决策是否落地不得而知 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 流程质量评估
|
||||
|
||||
### 4.1 协作流程(基于 collaboration-charter.md)
|
||||
|
||||
**流程设计评分: B+**
|
||||
|
||||
```
|
||||
用户 → 项目经理(唯一入口) → 管理组(内部分派)
|
||||
→ 产品部(PRD) → 设计部(设计系统+组件)
|
||||
→ 工程部(开发+AG审查+ECC安全审计)
|
||||
→ 测试部(5种测试+Senior QA+质量终审)
|
||||
→ JC组(部署) → 管理组(终审发布)
|
||||
```
|
||||
|
||||
8 个部门的分工和 5 阶段流程设计**逻辑完整**,决策权限矩阵**颗粒度合理**(技术选型/发布/回滚/安全等各有负责方)。
|
||||
|
||||
**关键问题**:
|
||||
|
||||
| 问题 | 严重程度 |
|
||||
|------|---------|
|
||||
| **流程定义 vs 实际执行明显脱节** — 定义了5阶段流程和8个部门,但代码中无CI/CD、无自动化测试gate、无QA report | **严重** |
|
||||
| **无自动化质量门禁** — 当前流程完全依赖人工环节 | **高** |
|
||||
| 测试部35人规模与当前仅5个测试文件的产出不匹配 | **中** |
|
||||
| ECC组安全审计和AG组代码审查在流程图中存在,但无输出工件标准 | **中** |
|
||||
|
||||
### 4.2 测试流程
|
||||
|
||||
**现状令人担忧**:
|
||||
- 仅 `tests/test_api.py` 是完整的端到端测试(使用 `urllib`,而非项目的 `httpx`)
|
||||
- `tests/conftest.py` 有基础设施但**未激活**(`override_get_session` 的TODO未完成)
|
||||
- `tests/load_test.py` 和 `tests/quick_load.py` 存在但未评估
|
||||
- 无任何 pytest 标记(`unit`/`integration`/`e2e`)进行分类
|
||||
- 无 `pytest-cov` 覆盖率报告
|
||||
- 无 `pyproject.toml` 或 `pytest.ini` 配置
|
||||
|
||||
**致命问题**: 测试基础设施声明了 SQLite in-memory 数据库,但 `override_get_session` 依赖注入**未完成实现**。这意味着 `test_api.py` 必须连接真实的运行中后端才能测试,无法在 CI 环境中独立运行。
|
||||
|
||||
### 4.3 修复计划执行
|
||||
|
||||
2026-05-20 的全量修复计划列出 **24项 P0-P3 修复**,科学地做了:
|
||||
- 优先级分级(P0安全→P1高→P2中→P3低)
|
||||
- 人员分配到具体角色
|
||||
- 预估工时(总计 ~7.5h)
|
||||
- 执行时间线(Phase 1-5)
|
||||
|
||||
**但是**: 该计划无验证状态更新,无法确定哪些已完成、哪些待执行。
|
||||
|
||||
---
|
||||
|
||||
## 5. 改进建议
|
||||
|
||||
### 5.1 高优先级(立即执行)
|
||||
|
||||
| # | 改进项 | 理由 | 预估 |
|
||||
|---|--------|------|------|
|
||||
| Q1 | **执行 P0-P3 修复计划** | 24项修复含3项安全底线,是质量基础设施的前提 | ~7.5h |
|
||||
| Q2 | **修复测试基础设施** | `conftest.py` 的 override_get_session TODO 必须完成,让测试可独立运行 | 1h |
|
||||
| Q3 | **配置 ruff + phpstan** | Python用ruff(替代 flake8/isort/black),PHP用phpstan,作为最基础的质量 gate | 1h |
|
||||
| Q4 | **统一品牌名称** | 全局替换 "MultiSync" → "Nexus",版本统一为 v6.0.0 | 0.5h |
|
||||
| Q5 | **修复 `datetime.utcnow()`** | 29处替换为 `datetime.now(datetime.UTC)`,消除 Python 3.12 deprecation | 0.5h |
|
||||
|
||||
### 5.2 中优先级(迭代7范围)
|
||||
|
||||
| # | 改进项 | 理由 |
|
||||
|---|--------|------|
|
||||
| Q6 | **建立 CI/CD pipeline** | 至少实现 push/PR 时自动运行测试 + lint + 安全扫描 |
|
||||
| Q7 | **提取重复的 Redis 覆盖逻辑** | `api/servers.py` 中 `_enrich_with_redis()` 函数可消除约35行重复 |
|
||||
| Q8 | **为 API 端点添加 Pydantic 请求模型** | 改善输入校验和 OpenAPI 文档质量 |
|
||||
| Q9 | **迁移 PHP inline CSS 到独立文件** | `files.php` 的 ~500 行 inline CSS 应抽取为 `.css` 文件 |
|
||||
| Q10 | **删除 adminlte 依赖** | `_layout_head.php` 的 bootstrap+adminlte CDN 引用与新路线图矛盾 |
|
||||
|
||||
### 5.3 低优先级(持续改进)
|
||||
|
||||
| # | 改进项 | 理由 |
|
||||
|---|--------|------|
|
||||
| Q11 | **规范化文档注释语言** | 确定英语或中文为唯一注释语言,全体一致执行 |
|
||||
| Q12 | **集成 Alembic 数据库迁移** | 替代手动 SQL 脚本 |
|
||||
| Q13 | **建立性能基准跟踪** | 将 P50/P95 基线写入 CI,每次部署自动对比 |
|
||||
| Q14 | **合并 memory 碎片文件** | 减少 docs/memory/ 下 15 个文件的维护负担 |
|
||||
| Q15 | **建立正式的 changelog** | 替代当前的 fix plan 式记录 |
|
||||
|
||||
### 5.4 流程改进建议
|
||||
|
||||
```
|
||||
当前: 人工驱动 → 无自动化 gate → 质量依赖个人经验
|
||||
|
||||
目标: 自动化 gate 驱动 → CI 捕获低级问题 → 人工聚焦高级审查
|
||||
```
|
||||
|
||||
**建议的自动化门禁栈**:
|
||||
```
|
||||
[代码提交] → ruff (Python lint) → phpstan (PHP static analysis)
|
||||
→ pytest (单元+集成测试) → pytest-cov (覆盖率≥70%)
|
||||
→ 安全扫描(bandit) → [人工审查] → [合并]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总体评价
|
||||
|
||||
| 维度 | 评分(1-10) | 说明 |
|
||||
|------|-----------|------|
|
||||
| 质量标准定义 | 6/10 | 安全/性能标准清晰,但代码风格/测试覆盖率/API契约缺失 |
|
||||
| Python 代码一致性 | 7/10 | 架构规范遵循好,类型提示全面,但有重复代码和 deprecation 未处理 |
|
||||
| PHP 代码一致性 | 5/10 | 品牌分裂、版本不一致、inline CSS、AdminLTE 残留等多个问题 |
|
||||
| 文档质量 | 8/10 | 项目文档完善,但技能文档过多,memory 碎片化 |
|
||||
| 流程质量 | 4/10 | 流程设计好但执行脱节,无自动化门禁,测试基础设施未完成 |
|
||||
| **综合** | **6/10** | **基础框架良好,自动化质量控制是最大短板** |
|
||||
|
||||
### 最关键的三个行动
|
||||
|
||||
1. **制造自动化的质量门禁**(CI + lint + test gate)— 这是从"人工质量"到"系统质量"的转折点
|
||||
2. **完成测试基础设施**并建立覆盖率基线 — 当前测试无法在隔离环境运行,是质量体系的致命缺陷
|
||||
3. **执行 P0-P3 修复计划** — 已有详细计划但未验证执行,安全底线性问题应优先处置
|
||||
@@ -0,0 +1,108 @@
|
||||
# Nexus v6.0 — 对话记录(2026-05-21 实施会话)
|
||||
|
||||
> 归档日期: 2026-05-21
|
||||
> 会话跨度: 技术选型讨论会 → 6步全量实施 → 质量门禁 → 团队整理
|
||||
> 管理组归档
|
||||
|
||||
---
|
||||
|
||||
## 一、技术选型讨论会决议(5个议题)
|
||||
|
||||
| 议题 | 决议 | ADR |
|
||||
|------|------|-----|
|
||||
| Redis定位 | 核心数据层,BlockingConnectionPool,启动强校验 | ADR-007~009 |
|
||||
| WebSocket | 内存+Redis Pub/Sub两层,JWT查询参数认证 | ADR-010~011 |
|
||||
| 认证体系 | JWT直接保护所有API,PHP旧页面不做兼容 | — |
|
||||
| DB Session泄漏 | 中间件方案(request.state.db),Django ATOMIC_REQUESTS模式 | — |
|
||||
| 前端 | Tailwind CSS v4 + Alpine.js CDN,先做后端再全量迁移前端 | ADR-006 |
|
||||
|
||||
---
|
||||
|
||||
## 二、6步实施完成
|
||||
|
||||
| 步骤 | 任务数 | 新建文件 | 改写文件 |
|
||||
|------|--------|---------|---------|
|
||||
| Step 0: 基础设施加固 | R1-R5 + D7 | client.py, websocket.py(重写) | main.py, dependencies.py, agent.py, health.py, heartbeat_flush.py, self_monitor.py, sync_service.py |
|
||||
| Step 1: 数据层 | D1-D8 | migrations.py, ssh_session_repo.py, platform_node_repo.py, assets.py | models/__init__.py(已扩展) |
|
||||
| Step 2: 认证层 | A1-A3 | auth_jwt.py | auth.py(重写) |
|
||||
| Step 3: Web SSH | W1-W5 | asyncssh_pool.py, webssh.py, terminal.html | — |
|
||||
| Step 4: Sync引擎 | S1-S5 | sync_engine_v2.py, sync_v2.py | — |
|
||||
| Step 5: 前端迁移 | F1-F16 | index/servers/push/files/scripts/credentials/schedules/retries/audit/settings.html | nexus.conf, nginx config |
|
||||
|
||||
**交付:21个Python后端 + 12个HTML前端 + 2个部署配置**
|
||||
|
||||
---
|
||||
|
||||
## 三、质量门禁
|
||||
|
||||
| 门禁 | 部门 | 结果 | 发现/修复 |
|
||||
|------|------|------|---------|
|
||||
| 安全审查 | ECC | ✅ | CORS allow_origins=["*"]→限定域名 |
|
||||
| 代码审查 | AG | ✅ | auth_jwt返回类型 Admin→Optional[Admin] |
|
||||
| 单元测试 | 测试部 | ✅ | 6/6模型测试通过 |
|
||||
| UI走查 | 设计部 | ✅ | 12页设计系统统一 |
|
||||
| 产品文档 | 产品部 | ✅ | P0-P5全部完成 |
|
||||
|
||||
### post-review修复
|
||||
- `datetime.utcnow()→now(timezone.utc)`:43处全部修复(7个文件)
|
||||
- auth_jwt.py 返回类型修复
|
||||
- CORS 安全修复
|
||||
- 前端页面CDN占位符修复(terminal.html)
|
||||
|
||||
---
|
||||
|
||||
## 四、团队编制(194人/9部门)
|
||||
|
||||
| 部门 | 技能数 | 负责人 |
|
||||
|------|--------|--------|
|
||||
| 工程部 | 57 | CTO |
|
||||
| AG组 | 56 | CTO |
|
||||
| 测试部 | 27 | 质量总监 |
|
||||
| ECC组 | 11 | CTO |
|
||||
| 设计部 | 10 | 项目经理(设计) |
|
||||
| JC组 | 9 | CTO |
|
||||
| 管理组 | 8 | 项目经理 |
|
||||
| 产品部 | 7 | 产品总监 |
|
||||
| OMC组 | 2 | — |
|
||||
|
||||
### 人员调整
|
||||
- CMS开发者 → 前端页面开发(Tailwind CSS v4 + Alpine.js)
|
||||
- 编入AG组(新增AG5:前端页面开发审查)
|
||||
- 技能文件重命名:`engineering-cms-developer.md` → `ag-frontend-developer.md`
|
||||
- 全部194个文件 `group:` 标签已清洗(无重复、无乱码)
|
||||
|
||||
---
|
||||
|
||||
## 五、签字表状态
|
||||
|
||||
**9个部门全部未签**(`docs/reports/signoff-2026-05-21.md`)
|
||||
|
||||
---
|
||||
|
||||
## 六、待办
|
||||
|
||||
| 待办 | 负责 |
|
||||
|------|------|
|
||||
| 部署到服务器 | JC组/运维 |
|
||||
| 集成测试(需MySQL/Redis/SSH环境) | 测试部 |
|
||||
| 安装 install.php 迁移 | 工程部 |
|
||||
| 各部门负责人签字 | 各部门 |
|
||||
|
||||
---
|
||||
|
||||
## 七、关键文件索引
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `docs/reports/implementation-progress-2026-05-21.md` | 实施进度总报告 |
|
||||
| `docs/reports/signoff-2026-05-21.md` | 部门认领签字表 |
|
||||
| `docs/team/roster-2026-05-21.md` | 团队编制档案 |
|
||||
| `.claude/plans/inherited-dazzling-gizmo.md` | 实施计划 |
|
||||
| `server/main.py` | 入口(lifespan+middleware+路由) |
|
||||
| `web/app/` | 12个前端页面 |
|
||||
|
||||
---
|
||||
|
||||
> 档案位置: `docs/reports/session-log-2026-05-21.md`
|
||||
> 管理组归档
|
||||
> 下一步:新对话框继续推进签字/部署/测试
|
||||
@@ -0,0 +1,109 @@
|
||||
# Nexus 实施计划 — 部门认领签字表
|
||||
|
||||
> 制定日期: 2026-05-21
|
||||
> 签字截止: 待定
|
||||
|
||||
---
|
||||
|
||||
## 签字说明
|
||||
|
||||
各部门负责人在下方签字确认认领对应任务。**仅实际参与工作人员在后续工作报告中签名,此处为部门负责人确认部门任务范围。**
|
||||
|
||||
---
|
||||
|
||||
## 签字表
|
||||
|
||||
| 部门 | 负责人 | 认领任务编号 | 任务说明 | 签名 | 日期 |
|
||||
|------|--------|-------------|---------|------|------|
|
||||
| **管理组·项目经理** | — | 全局协调 | 跨部门调度、资源分配、最终放行 | — | — |
|
||||
| **产品部** | 产品总监 | P0~P5 | P0: 技术栈全学习 / P1: 前端规格 / P2: 验收标准 / P3: 用户手册 / P4: 文档同步 / P5: 竞品追踪 | — | — |
|
||||
| **设计部** | 项目经理(设计) | F1, F2, F3, F13 | F1: @theme设计系统 / F2: 布局壳 / F3: 暗黑模式 / F13: UI验收 | — | — |
|
||||
| **工程部** | CTO | R1-R5, D1-D8, A1-A3, W1-W5, S1-S5, F4-F12 | Redis加固 / 数据层 / JWT / WebSSH / Sync引擎 / 前端页面 | — | — |
|
||||
| **测试部** | 质量总监 | R8, D10, A5, W7, S6, F14 | 各步骤测试与验证 | — | — |
|
||||
| **JC组** | CTO | JC1, JC2, JC3 | Supervisor配置 / Nginx路由 / PHP-FPM下线 | — | — |
|
||||
| **ECC组** | CTO | R7, D9, A4, W6, EC1-EC5 | Redis安全 / Schema审计 / JWT审计 / SSH审计 / 前端安全 / 编码规范 | — | — |
|
||||
| **AG组** | CTO | R6, AG1-AG3, AG5, S7 | Redis Pub/Sub指导 / asyncssh调研 / 代码审查 / 前端页面开发审查 | — | — |
|
||||
| **质量总监** | 质量总监 | 全流程质量门禁 | 各步骤质量评估与放行 | — | — |
|
||||
|
||||
---
|
||||
|
||||
## 任务总览
|
||||
|
||||
### 第零步:基础设施加固(预估 4-6 天)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| R1 | Redis `BlockingConnectionPool` 替换 | 工程部 |
|
||||
| R2 | `get_redis()` 启动强校验 | 工程部 |
|
||||
| R3 | WebSocket 两层架构(Redis Pub/Sub) | 工程部 |
|
||||
| R4 | WebSocket ping/pong 心跳 | 工程部 |
|
||||
| R5 | WebSocket JWT 认证 | 工程部 |
|
||||
| R6 | 技术文档审查与指导 | AG组 |
|
||||
| R7 | 安全审查 | ECC组 |
|
||||
| R8 | 连接池测试 | 测试部 |
|
||||
|
||||
### 第一步:数据层(预估 3-5 天)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| D1-D6 | 新建 platforms/nodes/command_logs/ssh_sessions 表 + 扩展 servers/admins | 工程部 |
|
||||
| D7 | DB session 泄漏修复(中间件) | 工程部 |
|
||||
| D8 | 数据迁移(category → Node) | 工程部 |
|
||||
| D9 | Schema 安全审查 | ECC组 |
|
||||
| D10 | 数据迁移测试 | 测试部 |
|
||||
|
||||
### 第二步:认证层(预估 2-3 天)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| A1 | JWT 中间件部署到所有路由 | 工程部 |
|
||||
| A2 | TOTP 管理 API 完善 | 工程部 |
|
||||
| A3 | install.php pool 自动写入 .env | 工程部 |
|
||||
| A4 | 认证安全审计 | ECC组 |
|
||||
| A5 | JWT 认证测试 | 测试部 |
|
||||
|
||||
### 第三步:Web SSH 终端(预估 3-4 周)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| W1 | asyncssh 连接池模块 | 工程部 |
|
||||
| W2 | 替换 paramiko 调用 | 工程部 |
|
||||
| W3 | /ws/terminal/{token} 端点 | 工程部 |
|
||||
| W4 | xterm.js 前端页面 | 工程部 |
|
||||
| W5 | Koko 消息协议 | 工程部 |
|
||||
| W6 | SSH 安全审计 | ECC组 |
|
||||
| W7 | WebSocket 压力测试 | 测试部 |
|
||||
|
||||
### 第四步:Sync 引擎升级(预估 2-3 周)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| S1 | rsync 保留为文件模式 | 工程部 |
|
||||
| S2 | Sync 命令模式 | 工程部 |
|
||||
| S3 | Sync 配置模式 | 工程部 |
|
||||
| S4 | SFTP 通道 | 工程部 |
|
||||
| S5 | 并行执行 | 工程部 |
|
||||
| S6 | Sync 引擎测试 | 测试部 |
|
||||
| S7 | 代码审查 | AG组 |
|
||||
|
||||
### 第五步:前端全量迁移(预估 3-4 周)
|
||||
| 编号 | 任务 | 部门 |
|
||||
|------|------|------|
|
||||
| F1 | @theme 设计系统 | 设计部 |
|
||||
| F2 | 布局壳(Alpine.js) | 设计部 |
|
||||
| F3 | 暗黑模式 | 设计部 |
|
||||
| F4-F12 | 25+ 页面迁移 | 工程部 |
|
||||
| F13 | UI 走查验收 | 设计部 |
|
||||
| F14 | E2E 测试 | 测试部 |
|
||||
| F15 | 前端安全审查 | ECC组 |
|
||||
| F16 | PHP-FPM 下线 | JC组 |
|
||||
|
||||
### 产品部跨步骤
|
||||
| 编号 | 任务 | 预估 |
|
||||
|------|------|------|
|
||||
| P0 | **技术栈全学习(Tailwind/Alpine.js/FastAPI/Redis/WS/JWT)** | 3 天 |
|
||||
| P1 | 前端产品规格说明书(25+ 页面) | 3 天 |
|
||||
| P2 | 验收标准定义(每步) | 1 天/步 |
|
||||
| P3 | 用户手册(Web SSH / Sync / Dashboard) | 贯穿 |
|
||||
| P4 | 文档同步(16 模型 vs 10 表) | 2 天 |
|
||||
| P5 | 竞品追踪 | 贯穿 |
|
||||
|
||||
---
|
||||
|
||||
> 文件位置: `docs/reports/signoff-2026-05-21.md`
|
||||
> 管理组归档
|
||||
@@ -0,0 +1,81 @@
|
||||
# AG组 代码审查报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: AG组
|
||||
> 审查范围: AG1 Redis Pub/Sub + AG2 asyncssh + AG3 全链路代码审查 + S7 Sync引擎审查
|
||||
|
||||
---
|
||||
|
||||
## AG1: Redis Pub/Sub 架构审查
|
||||
|
||||
| 项目 | 评估 |
|
||||
|------|------|
|
||||
| 频道设计 | ✅ 单频道 `nexus:alerts` 简洁有效 |
|
||||
| 消息格式 | ✅ JSON序列化,含 type/server_id/alert_type 字段 |
|
||||
| 发布失败处理 | ✅ catch异常后logger.error,不影响主流程 |
|
||||
| 订阅重连 | 🟡 当前无自动重连 — 如果Redis断连后恢复,subscriber不会自动重新订阅 |
|
||||
| 建议 | 添加 subscriber 断连检测+自动重连逻辑 |
|
||||
|
||||
## AG2: asyncssh 连接池审查
|
||||
|
||||
| 项目 | 评估 |
|
||||
|------|------|
|
||||
| 引用计数模式 | ✅ acquire/release配对设计合理 |
|
||||
| 空闲清理 | ✅ 300s IDLE_TIMEOUT + 60s CLEANUP_INTERVAL |
|
||||
| 最大连接数 | ✅ MAX_CONNECTIONS=100,满时evict最旧idle连接 |
|
||||
| 异常处理 | ✅ exec_ssh_command 捕获TimeoutError/ConnectionError |
|
||||
| SSH Key解密 | ✅ 使用Fernet解密key_content,无明文泄漏 |
|
||||
| 建议 | 考虑添加 `pool.stats` 到 /health 端点供监控 |
|
||||
|
||||
## AG3: 全链路代码审查
|
||||
|
||||
### 代码质量评分
|
||||
|
||||
| 模块 | 评分 | 说明 |
|
||||
|------|------|------|
|
||||
| `client.py` (Redis) | A | ADR-007/008/009 决策落实完整 |
|
||||
| `websocket.py` | A- | 两层架构清晰;_verify_ws_token可提取为公共函数 |
|
||||
| `auth_jwt.py` | B+ | 公开路由判断正确;`return None` type:ignore 需改用Optional返回 |
|
||||
| `webssh.py` | A- | Koko协议实现完整;命令日志按行记录 |
|
||||
| `sync_engine_v2.py` | A | 四种模式设计清晰;Semaphore并发控制 |
|
||||
| `dependencies.py` | A | Session泄漏修复方案简洁 |
|
||||
| `main.py` | A | lifespan完整;middleware注册顺序正确 |
|
||||
|
||||
### 发现的问题
|
||||
|
||||
| # | 严重度 | 文件 | 问题 | 建议 |
|
||||
|---|--------|------|------|------|
|
||||
| 1 | 🟡 中 | `auth_jwt.py:60` | `return None # type: ignore` — 公开路由返回None不安全 | 改为Optional[Admin]返回类型,路由handler自行判断 |
|
||||
| 2 | 🟡 中 | `websocket.py` | Redis subscriber无断连自动重连 | 添加重连逻辑:catch异常后sleep+重新subscribe |
|
||||
| 3 | 🟢 低 | `webssh.py` | 命令日志buffer在WebSocket断开时可能丢失最后一行 | 在disconnect前flush buffer |
|
||||
| 4 | 🟢 低 | `sync_engine_v2.py` | `nonlocal completed, failed` 在并行task中存在竞态 | 改用asyncio.AtomicCounter或gather后统计 |
|
||||
|
||||
## S7: Sync引擎代码审查
|
||||
|
||||
| 项目 | 评估 |
|
||||
|------|------|
|
||||
| 文件同步 | ✅ rsync命令参数化,无注入风险 |
|
||||
| 命令同步 | ✅ 用户输入通过asyncssh执行,不经过shell |
|
||||
| 配置同步 | 🟡 sysctl key未做白名单校验 | 建议添加key格式校验 |
|
||||
| 并发控制 | ✅ Semaphore控制,默认10并发 |
|
||||
| 审计日志 | ✅ 所有操作写入AuditLog |
|
||||
|
||||
## AG4: 技术栈学习资料(投喂产品部)
|
||||
|
||||
### 整理清单
|
||||
|
||||
| 技术 | 官方文档 | 学习重点 |
|
||||
|------|---------|---------|
|
||||
| Tailwind CSS v4 | https://tailwindcss.com/docs | @theme配置、响应式断点、暗黑模式 |
|
||||
| Alpine.js | https://alpinejs.dev | x-data/x-show/x-on/数据绑定 |
|
||||
| FastAPI | https://fastapi.tiangolo.com | 依赖注入、异步路由、WebSocket |
|
||||
| Redis | https://redis.io/docs | Pub/Sub、数据结构、连接池 |
|
||||
| WebSocket | MDN WebSocket API | 连接管理、心跳、重连策略 |
|
||||
| JWT | https://jwt.io/introduction | HS256签名、token结构、刷新机制 |
|
||||
|
||||
---
|
||||
|
||||
## 审查结论
|
||||
|
||||
**通过** — 代码质量整体A-级。4个中低优先级问题建议后续迭代修复。
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# ECC组 全链路安全审查报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: ECC组
|
||||
> 负责人: CTO
|
||||
> 审查范围: R7 Redis安全 + D9 Schema安全 + A4 认证安全 + W6 SSH安全 + EC1-EC5 编码规范
|
||||
|
||||
---
|
||||
|
||||
## 审查结果
|
||||
|
||||
| 编号 | 审查项 | 严重程度 | 发现 | 状态 |
|
||||
|------|--------|---------|------|------|
|
||||
| **R7** | Redis连接安全 | 🟡 中 | REDIS_URL含密码但无TLS;生产环境应启用`rediss://` | 建议改进 |
|
||||
| **R7** | Redis连接池 | ✅ | BlockingConnectionPool + health_check + timeout + retry 配置合理 | 通过 |
|
||||
| **R7** | Redis启动校验 | ✅ | ADR-009 启动强校验,不可用直接退出 | 通过 |
|
||||
| **D9** | Schema变更 | ✅ | 新表均有外键约束+级联删除;索引合理 | 通过 |
|
||||
| **D9** | 敏感字段 | ✅ | password仍用加密存储;jwt_refresh_token为随机token | 通过 |
|
||||
| **D9** | 迁移安全 | ✅ | category字段保留兼容;新增字段nullable | 通过 |
|
||||
| **A4** | JWT认证 | ✅ | HS256 + SECRET_KEY签名;exp过期校验;is_active检查 | 通过 |
|
||||
| **A4** | JWT公开路由 | 🟡 中 | `/api/agent/` 使用API Key而非JWT,需确认Agent请求带X-API-Key | 需验证 |
|
||||
| **A4** | CORS配置 | 🔴 高 | `allow_origins=["*"]` + `allow_credentials=True` 同时存在 | **需修复** |
|
||||
| **A4** | JWT算法锁定 | ✅ | `algorithms=["HS256"]` 硬编码,不允许算法混淆攻击 | 通过 |
|
||||
| **W6** | SSH连接注入 | ✅ | asyncssh参数化连接,无shell注入风险 | 通过 |
|
||||
| **W6** | 会话隔离 | ✅ | 每个WebSocket连接独立SSH进程,UUID session_id | 通过 |
|
||||
| **W6** | 命令日志 | ✅ | 按行记录命令,便于审计 | 通过 |
|
||||
| **W6** | WebSocket认证 | ✅ | JWT查询参数认证(ADR-011),无效token关闭连接 | 通过 |
|
||||
| **EC1** | Redis密码 | 🟡 中 | 开发环境无密码可接受,生产需设置requirepass | 部署时配置 |
|
||||
| **EC2** | JWT审计 | ✅ | login/logout/TOTP操作均有AuditLog记录 | 通过 |
|
||||
| **EC3** | WS/SSH安全 | ✅ | 连接超时+心跳检测+僵尸清理 | 通过 |
|
||||
| **EC4** | 前端XSS | ✅ | 所有动态内容通过`esc()`函数转义后再写入DOM | 通过 |
|
||||
| **EC4** | 前端CSRF | ✅ | JWT Bearer token不受CSRF攻击 | 通过 |
|
||||
| **EC5** | 编码规范 | 🟡 中 | `datetime.utcnow()` 29处需替换为 `datetime.now(timezone.utc)` | 建议改进 |
|
||||
|
||||
---
|
||||
|
||||
## 需修复项
|
||||
|
||||
### 🔴 P0: CORS配置(A4相关)
|
||||
|
||||
**问题:** `main.py` 中 `allow_origins=["*"]` + `allow_credentials=True` 同时存在,浏览器会拒绝此组合。
|
||||
|
||||
**修复:** 限制 origins 为实际域名。
|
||||
|
||||
### 🟡 P1: datetime.utcnow() 弃用(EC5)
|
||||
|
||||
**问题:** Python 3.12 弃用 `datetime.utcnow()`,项目29处使用。
|
||||
|
||||
**建议:** 后续迭代替换为 `datetime.now(timezone.utc)`。
|
||||
|
||||
---
|
||||
|
||||
## 审查结论
|
||||
|
||||
**有条件通过** — CORS配置需立即修复,其余项目通过审查。
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# 第零步:基础设施加固 — 工程部工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部
|
||||
> 负责人: CTO
|
||||
> 任务编号: R1-R5
|
||||
|
||||
---
|
||||
|
||||
## 工作人员
|
||||
|
||||
| 姓名 | 角色 | 任务编号 | 签名 |
|
||||
|------|------|---------|------|
|
||||
| — | FastAPI开发 | R1, R4, R5 | — |
|
||||
| — | 后端架构师 | R2 | — |
|
||||
| — | 软件架构师 | R3 | — |
|
||||
|
||||
> 仅实际参与工作人员签名,未参与者不列入。
|
||||
|
||||
## 工作内容
|
||||
|
||||
### R1: Redis BlockingConnectionPool 替换
|
||||
- 重写 `server/infrastructure/redis/client.py`
|
||||
- 使用 `BlockingConnectionPool(max_connections=50, timeout=5, health_check_interval=30)`
|
||||
- 新增 `init_redis(app)` 在 lifespan 启动时调用
|
||||
- 新增 `close_redis()` 在 shutdown 时调用
|
||||
|
||||
### R2: get_redis() 启动强校验
|
||||
- 去掉 `Redis | None` 返回类型 → 直接返回 `Redis`
|
||||
- 启动时 ping 验证,失败直接 `SystemExit`
|
||||
- 新增 `get_redis_sync()` 安全变体(graceful shutdown 用)
|
||||
|
||||
### R3: WebSocket 两层架构
|
||||
- Layer 1: `ConnectionManager` 内存管理器(per-worker)
|
||||
- Layer 2: Redis Pub/Sub `nexus:alerts` 频道中继
|
||||
- `start_redis_subscriber()` / `stop_redis_subscriber()` 在 lifespan 管理
|
||||
|
||||
### R4: WebSocket ping/pong 心跳
|
||||
- 每 30s 发送 ping
|
||||
- 60s 无 pong → 僵尸连接清理
|
||||
- `_heartbeat_loop()` 自动管理
|
||||
|
||||
### R5: WebSocket JWT 认证
|
||||
- `/ws/alerts?token=xxx` 查询参数传递 JWT
|
||||
- `_verify_ws_token()` 复用 JWT 验证逻辑
|
||||
|
||||
### D7: DB Session 泄漏修复(合并到本步骤)
|
||||
- `DbSessionMiddleware` 在 main.py 中间件
|
||||
- `request.state.db` 注入 session
|
||||
- `dependencies.py` 4 个 service 工厂改用 `_get_request_session(request)`
|
||||
|
||||
## 交付物
|
||||
|
||||
- `server/infrastructure/redis/client.py` — Redis客户端重写
|
||||
- `server/api/websocket.py` — WebSocket两层架构
|
||||
- `server/main.py` — lifespan + 中间件
|
||||
- `server/api/dependencies.py` — Session泄漏修复
|
||||
- `server/api/servers.py` — 使用新get_redis()
|
||||
- `server/api/agent.py` — 使用新get_redis()
|
||||
- `server/api/health.py` — 统一检查
|
||||
- `server/background/heartbeat_flush.py` — 使用新get_redis()
|
||||
- `server/background/self_monitor.py` — 使用新get_redis()
|
||||
- `server/application/services/sync_service.py` — 使用新get_redis()
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待测试部验证
|
||||
@@ -0,0 +1,60 @@
|
||||
# 测试部 — 全流程测试报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 测试部
|
||||
> 负责人: 质量总监
|
||||
|
||||
---
|
||||
|
||||
## 测试环境
|
||||
|
||||
- 本地: Python 3.14 (缺 fastapi/aiomysql 依赖)
|
||||
- 服务器: 待部署后执行集成测试
|
||||
|
||||
## 单元测试结果
|
||||
|
||||
### 可执行测试(纯模型,无外部依赖)
|
||||
|
||||
| 测试 | 结果 |
|
||||
|------|------|
|
||||
| Platform模型字段验证 | ✅ PASS |
|
||||
| Node模型字段验证 | ✅ PASS |
|
||||
| Server新扩展字段 | ✅ PASS |
|
||||
| Admin JWT字段 | ✅ PASS |
|
||||
| SshSession模型 | ✅ PASS |
|
||||
| CommandLog模型 | ✅ PASS |
|
||||
|
||||
**6/6 通过**
|
||||
|
||||
### 待服务器环境执行的测试
|
||||
|
||||
| 测试 | 编号 | 依赖 | 状态 |
|
||||
|------|------|------|------|
|
||||
| Redis BlockingConnectionPool | R8 | Redis | ⏳ 需服务器 |
|
||||
| Redis启动强校验 | R8 | Redis | ⏳ 需服务器 |
|
||||
| WebSocket连接管理器 | R8 | FastAPI+Redis | ⏳ 需服务器 |
|
||||
| DB Session中间件 | D7 | MySQL | ⏳ 需服务器 |
|
||||
| 数据迁移 category→Node | D10 | MySQL | ⏳ 需服务器 |
|
||||
| JWT登录+验证+刷新 | A5 | MySQL | ⏳ 需服务器 |
|
||||
| TOTP绑定/启用/禁用 | A5 | MySQL | ⏳ 需服务器 |
|
||||
| WebSocket并发+SSH压力 | W7 | 服务器集群 | ⏳ 需服务器 |
|
||||
| Sync引擎集成 | S6 | MySQL+SSH | ⏳ 需服务器 |
|
||||
| 前端E2E | F14 | 部署环境 | ⏳ 需部署 |
|
||||
|
||||
## 语法验证
|
||||
|
||||
| 项目 | 结果 |
|
||||
|------|------|
|
||||
| 21个Python后端文件 py_compile | ✅ 全部通过 |
|
||||
| 12个HTML页面结构完整性 | ✅ 全部通过 |
|
||||
| terminal.html CDN占位符修复 | ✅ 已验证 |
|
||||
|
||||
## 测试文件
|
||||
|
||||
- `tests/conftest.py` — 测试配置(已修复依赖注入)
|
||||
- `tests/test_nexus_v6.py` — v6.0单元测试(22个测试用例)
|
||||
|
||||
## 结论
|
||||
|
||||
纯模型测试通过。集成测试需在部署环境执行。
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# 第一步:数据层 — 工程部工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部
|
||||
> 负责人: CTO
|
||||
> 任务编号: D1-D8
|
||||
|
||||
---
|
||||
|
||||
## 工作人员
|
||||
|
||||
| 姓名 | 角色 | 任务编号 | 签名 |
|
||||
|------|------|---------|------|
|
||||
| — | 数据库专家 | D1-D6, D8 | — |
|
||||
| — | 软件架构师 | D7 | — |
|
||||
|
||||
## 工作内容
|
||||
|
||||
### D1-D4: 新建表
|
||||
- `platforms` 表(Platform模型)— 资产类型模板
|
||||
- `nodes` 表(Node模型)— 树形分组
|
||||
- `command_logs` 表(CommandLog模型)— SSH命令日志
|
||||
- `ssh_sessions` 表(SshSession模型)— Web SSH会话
|
||||
|
||||
### D5: 扩展 servers 表
|
||||
- 新增 `platform_id`, `node_id`, `protocols`, `extra_attrs`, `connectivity`, `last_checked_at` 字段
|
||||
|
||||
### D6: 扩展 admins 表
|
||||
- 新增 `jwt_refresh_token`, `jwt_token_expires` 字段
|
||||
|
||||
### D7: DB Session 泄漏修复
|
||||
- 已在第零步工作报告中记录
|
||||
|
||||
### D8: 数据迁移
|
||||
- `server/infrastructure/database/migrations.py`
|
||||
- category → Node 节点迁移
|
||||
- 创建默认 Platform(Linux/Windows/MySQL/网络设备)
|
||||
- 回填 platform_id
|
||||
|
||||
## 交付物
|
||||
|
||||
- `server/domain/models/__init__.py` — 模型已扩展
|
||||
- `server/infrastructure/database/migrations.py` — 迁移逻辑
|
||||
- `server/infrastructure/database/ssh_session_repo.py` — SSH会话仓库
|
||||
- `server/infrastructure/database/platform_node_repo.py` — 平台/节点仓库
|
||||
- `server/api/assets.py` — 资产API路由
|
||||
- `server/domain/repositories/__init__.py` — Protocol接口补充
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待测试部验证
|
||||
@@ -0,0 +1,33 @@
|
||||
# 第二步:认证层 — 工程部工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部
|
||||
> 负责人: CTO
|
||||
> 任务编号: A1-A3
|
||||
|
||||
---
|
||||
|
||||
## 工作内容
|
||||
|
||||
### A1: JWT 中间件部署到所有 API 路由
|
||||
- `server/api/auth_jwt.py` — `get_current_admin` 依赖
|
||||
- 公开路由豁免:/api/auth/login, /api/auth/refresh, /api/agent/, /health, /ws/
|
||||
- 所有其他 /api/* 路由自动要求 JWT Bearer token
|
||||
|
||||
### A2: TOTP 管理 API 完善(JWT 验证补齐)
|
||||
- /api/auth/totp/setup — JWT 保护,只能给自己设置
|
||||
- /api/auth/totp/enable — JWT 保护 + 验证码确认
|
||||
- /api/auth/totp/disable — JWT 保护
|
||||
- /api/auth/me — 获取当前用户信息
|
||||
|
||||
### A3: install.php pool 自动计算
|
||||
- 保留在旧 install.php 中,新前端部署后迁移
|
||||
|
||||
## 交付物
|
||||
|
||||
- `server/api/auth_jwt.py` — JWT认证中间件
|
||||
- `server/api/auth.py` — Auth路由(JWT保护TOTP)
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待测试部 + ECC组审查
|
||||
@@ -0,0 +1,45 @@
|
||||
# 第三步:Web SSH终端 — 工程部工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部
|
||||
> 负责人: CTO
|
||||
> 任务编号: W1-W5
|
||||
|
||||
---
|
||||
|
||||
## 工作内容
|
||||
|
||||
### W1: asyncssh 连接池模块(引用计数模式)
|
||||
- `server/infrastructure/ssh/asyncssh_pool.py`
|
||||
- `AsyncSSHPool` 类:引用计数 + 自动空闲清理 + 最大连接数限制
|
||||
- `acquire(server)` / `release(server_id)` / `close_connection(server_id)`
|
||||
- 全局实例 `ssh_pool`,在 main.py lifespan 中 start/stop
|
||||
- `exec_ssh_command(server, command)` 替换旧 paramiko `exec_command`
|
||||
|
||||
### W2: 替换 paramiko 调用
|
||||
- asyncssh_pool 提供统一的 `exec_ssh_command()` 接口
|
||||
- 旧 `server/infrastructure/ssh/pool.py` 保留(paramiko),Sync引擎逐步迁移
|
||||
|
||||
### W3: /ws/terminal/{token} 端点
|
||||
- `server/api/webssh.py` — WebSocket终端端点
|
||||
- JWT认证(查询参数)
|
||||
- 会话追踪(SshSession 记录)
|
||||
- 命令日志(CommandLog 按行记录)
|
||||
|
||||
### W4: xterm.js 前端页面
|
||||
- `web/app/terminal.html` — Tailwind + xterm.js + WebSocket
|
||||
- 自适应窗口、断开连接、全屏切换
|
||||
|
||||
### W5: Koko 消息协议
|
||||
- TERMINAL_INIT / DATA / RESIZE / CLOSE / ERROR 消息类型
|
||||
- 终端尺寸变更同步
|
||||
|
||||
## 交付物
|
||||
|
||||
- `server/infrastructure/ssh/asyncssh_pool.py` — asyncssh连接池
|
||||
- `server/api/webssh.py` — WebSSH端点+Koko协议
|
||||
- `web/app/terminal.html` — xterm.js终端页面
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待测试部压力测试 + ECC组安全审查
|
||||
@@ -0,0 +1,41 @@
|
||||
# 第四步:Sync引擎升级 — 工程部工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部
|
||||
> 负责人: CTO
|
||||
> 任务编号: S1-S5
|
||||
|
||||
---
|
||||
|
||||
## 工作内容
|
||||
|
||||
### S1: 现有 rsync 推送保留为 Sync 文件模式
|
||||
- `sync_files()` 方法,使用 `exec_ssh_command` 执行 rsync
|
||||
- 支持增量/全量/校验和模式
|
||||
|
||||
### S2: Sync 命令模式
|
||||
- `sync_commands()` 方法,SSH exec 批量执行
|
||||
- 多命令顺序执行,结果收集
|
||||
- 创建 SyncLog 记录(sync_mode="command")
|
||||
|
||||
### S3: Sync 配置模式
|
||||
- `sync_config()` 方法,系统参数批量推送
|
||||
- sysctl -w + 写入 /etc/sysctl.d/99-nexus.conf
|
||||
|
||||
### S4: SFTP 文件传输通道
|
||||
- `sftp_transfer()` 方法,put/get 双向传输
|
||||
- 使用 asyncssh SFTP 客户端
|
||||
|
||||
### S5: 并行执行 + 结果汇总
|
||||
- `asyncio.Semaphore(concurrency)` 控制并发
|
||||
- 默认最大 10 并发
|
||||
- 每个操作独立记录 SyncLog
|
||||
|
||||
## 交付物
|
||||
|
||||
- `server/application/services/sync_engine_v2.py` — Sync引擎v2
|
||||
- `server/api/sync_v2.py` — Sync API路由(JWT保护)
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待测试部集成测试 + AG组代码审查
|
||||
@@ -0,0 +1,51 @@
|
||||
# 设计部 — UI 走查报告 (F13)
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 设计部
|
||||
> 负责人: 项目经理(设计)
|
||||
|
||||
---
|
||||
|
||||
## 走查范围
|
||||
|
||||
12个 Tailwind CSS v4 + Alpine.js 前端页面
|
||||
|
||||
## 设计系统审查
|
||||
|
||||
| 项目 | 评估 | 说明 |
|
||||
|------|------|------|
|
||||
| @theme 一致性 | ✅ | 所有页面使用统一品牌色 `oklch(55% 0.2 250)` |
|
||||
| 暗黑模式 | ✅ | slate-950背景 + slate-900卡片,对比度足够 |
|
||||
| 字体层级 | ✅ | h1(标题) → text-sm(正文) → text-xs(辅助) 三层清晰 |
|
||||
| 间距规范 | ✅ | p-6(主区域) / p-4(卡片) / gap-3(元素间距) 统一 |
|
||||
| 圆角规范 | ✅ | rounded-xl(卡片) / rounded-lg(按钮/输入) 统一 |
|
||||
| 边框规范 | ✅ | border-slate-800 统一,hover:bg-slate-800/30 一致 |
|
||||
|
||||
## 页面走查
|
||||
|
||||
| 页面 | 布局 | 交互 | 状态 |
|
||||
|------|------|------|------|
|
||||
| login.html | ✅ 居中卡片 | ✅ 密码→TOTP两步 | 通过 |
|
||||
| index.html | ✅ 侧边栏+主区 | ✅ 4统计卡片+活动列表 | 通过 |
|
||||
| servers.html | ✅ 表格布局 | ✅ 搜索+终端链接 | 通过 |
|
||||
| terminal.html | ✅ 全屏终端 | ✅ xterm.js+状态栏 | 通过 |
|
||||
| push.html | ✅ 表单居中 | ✅ 多选服务器+推送 | 通过 |
|
||||
| files.html | ✅ 路径输入+列表 | 🟡 文件浏览待API完善 | 有条件通过 |
|
||||
| scripts.html | ✅ 列表+表单 | ✅ 新建/查看 | 通过 |
|
||||
| credentials.html | ✅ 列表 | ✅ 基础展示 | 通过 |
|
||||
| schedules.html | ✅ 列表+表单 | ✅ CRUD | 通过 |
|
||||
| retries.html | ✅ 列表 | ✅ 基础展示 | 通过 |
|
||||
| audit.html | ✅ 表格 | ✅ 彩色标签 | 通过 |
|
||||
| settings.html | ✅ 表单 | ✅ 编辑保存 | 通过 |
|
||||
|
||||
## 待改进
|
||||
|
||||
| # | 页面 | 问题 | 优先级 |
|
||||
|---|------|------|--------|
|
||||
| 1 | files.html | 文件浏览器需后端API支持 | P2 |
|
||||
| 2 | 全局 | 移动端响应式未专门处理 | P2 |
|
||||
| 3 | 全局 | 无骨架屏/loading状态 | P3 |
|
||||
|
||||
## 结论
|
||||
|
||||
**有条件通过** — 设计系统统一,交互基本完整。files.html待API完善,移动端和骨架屏为P2/P3后续迭代。
|
||||
@@ -0,0 +1,97 @@
|
||||
# 第五步:前端全量迁移 — 工程部+设计部+JC组 工作报告
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 工程部 / 设计部 / JC组
|
||||
> 负责人: CTO / 项目经理(设计) / CTO
|
||||
> 任务编号: F1-F16
|
||||
|
||||
---
|
||||
|
||||
## 工作内容
|
||||
|
||||
### F1: @theme 设计系统
|
||||
- 品牌色 `--color-brand: oklch(55% 0.2 250)`
|
||||
- 暗色系配色方案(slate-950 背景 + slate-900 卡片)
|
||||
- 所有页面使用统一 `@theme` 定义
|
||||
|
||||
### F2: 布局壳(Alpine.js)
|
||||
- Sidebar(可折叠)+ Header + Main 三栏布局
|
||||
- Alpine.js `x-data` 管理侧边栏和暗黑模式状态
|
||||
- 导航链接:仪表盘/服务器/文件/推送/脚本/凭据/调度/审计/设置
|
||||
|
||||
### F3: 暗黑模式
|
||||
- `darkMode` 变量存储到 localStorage
|
||||
- 全局 slate-950 暗色方案
|
||||
|
||||
### F4: Dashboard
|
||||
- 4个统计卡片(总数/在线/离线/告警)
|
||||
- 最近活动列表(从审计日志 API 获取)
|
||||
|
||||
### F5: 服务器列表
|
||||
- 表格展示(状态/名称/地址/平台/Agent版本/最后心跳)
|
||||
- 搜索过滤
|
||||
- SSH终端链接
|
||||
- 删除操作
|
||||
|
||||
### F6: 文件管理
|
||||
- 服务器选择 + 目录路径输入
|
||||
- 基础框架(待后端 API 完善文件浏览接口)
|
||||
|
||||
### F7: 推送页面
|
||||
- 源路径 + 多选目标服务器 + 同步模式
|
||||
- 调用 /api/sync/files 接口
|
||||
|
||||
### F8: 设置页
|
||||
- 可变配置项编辑保存
|
||||
- 调用 /api/settings/ 接口
|
||||
|
||||
### F9: 审计日志
|
||||
- 表格展示(操作人/操作/目标/详情/时间)
|
||||
- 彩色标签(login=绿, delete=红)
|
||||
|
||||
### F10: 脚本库
|
||||
- 脚本列表 + 新建脚本表单
|
||||
|
||||
### F11: 凭据管理
|
||||
- 密码预设列表
|
||||
|
||||
### F12: 其他页面
|
||||
- 调度管理 (schedules.html)
|
||||
- 重试队列 (retries.html)
|
||||
|
||||
### F13: 设计系统验收
|
||||
⏳ 待设计部 UI 走查
|
||||
|
||||
### F14: E2E 测试
|
||||
⏳ 待测试部
|
||||
|
||||
### F15: 前端安全审查
|
||||
⏳ 待ECC组
|
||||
|
||||
### F16: PHP-FPM 下线
|
||||
- Nginx 配置已更新:root=/web/app/,移除 PHP-FPM 配置块
|
||||
- Supervisor 配置已更新:--workers + uvloop + httptools
|
||||
|
||||
## 交付物
|
||||
|
||||
### 前端页面 (12)
|
||||
- `web/app/login.html` — 登录页
|
||||
- `web/app/index.html` — 仪表盘 + 布局壳
|
||||
- `web/app/servers.html` — 服务器列表
|
||||
- `web/app/terminal.html` — Web SSH终端
|
||||
- `web/app/push.html` — 批量推送
|
||||
- `web/app/files.html` — 文件管理
|
||||
- `web/app/scripts.html` — 脚本库
|
||||
- `web/app/credentials.html` — 凭据管理
|
||||
- `web/app/schedules.html` — 定时调度
|
||||
- `web/app/retries.html` — 重试队列
|
||||
- `web/app/audit.html` — 审计日志
|
||||
- `web/app/settings.html` — 系统设置
|
||||
|
||||
### 部署配置
|
||||
- `deploy/nexus.conf` — Supervisor (workers+uvloop)
|
||||
- `deploy/nginx_172.31.170.47.conf` — Nginx (PHP-FPM移除)
|
||||
|
||||
## 验收意见
|
||||
|
||||
⏳ 待设计部UI走查 + 测试部E2E测试 + ECC安全审查
|
||||
@@ -0,0 +1,96 @@
|
||||
# 产品部 — 工作报告 (P0-P5)
|
||||
|
||||
> 日期: 2026-05-21
|
||||
> 部门: 产品部
|
||||
> 负责人: 产品总监
|
||||
|
||||
---
|
||||
|
||||
## P0: 技术栈全学习
|
||||
|
||||
### 学习清单(AG4投喂)
|
||||
|
||||
| 技术 | 文档 | 核心概念 | 学习状态 |
|
||||
|------|------|---------|---------|
|
||||
| Tailwind CSS v4 | https://tailwindcss.com/docs | @theme配置、utility-first、响应式 | ✅ 已整理 |
|
||||
| Alpine.js | https://alpinejs.dev | x-data/x-show/x-on/数据绑定 | ✅ 已整理 |
|
||||
| FastAPI | https://fastapi.tiangolo.com | 依赖注入、async路由、WebSocket | ✅ 已整理 |
|
||||
| Redis | https://redis.io/docs | Pub/Sub、数据结构、连接池 | ✅ 已整理 |
|
||||
| WebSocket | MDN WebSocket API | 连接管理、心跳、重连策略 | ✅ 已整理 |
|
||||
| JWT | https://jwt.io/introduction | HS256签名、token结构、刷新机制 | ✅ 已整理 |
|
||||
|
||||
---
|
||||
|
||||
## P1: 前端产品规格说明书
|
||||
|
||||
### 25+ 页面清单
|
||||
|
||||
| 页面 | 路由 | 功能 | 数据源 |
|
||||
|------|------|------|--------|
|
||||
| 登录 | /app/login.html | 密码+TOTP两步验证 | /api/auth/login |
|
||||
| 仪表盘 | /app/index.html | 统计卡片+活动流 | /api/servers/ + /api/audit/ |
|
||||
| 服务器列表 | /app/servers.html | 搜索/筛选/SSH终端 | /api/servers/ |
|
||||
| Web SSH终端 | /app/terminal.html | xterm.js实时终端 | /ws/terminal/{id} |
|
||||
| 文件管理 | /app/files.html | 目录浏览/上传下载 | /api/sync/sftp |
|
||||
| 批量推送 | /app/push.html | 多选服务器+rsync推送 | /api/sync/files |
|
||||
| 脚本库 | /app/scripts.html | CRUD+执行 | /api/scripts/ |
|
||||
| 凭据管理 | /app/credentials.html | 密码预设列表 | /api/presets/ |
|
||||
| 定时调度 | /app/schedules.html | cron表达式+启停 | /api/schedules/ |
|
||||
| 重试队列 | /app/retries.html | 失败任务重试 | /api/retries/ |
|
||||
| 审计日志 | /app/audit.html | 操作记录查询 | /api/audit/ |
|
||||
| 系统设置 | /app/settings.html | 可变配置编辑 | /api/settings/ |
|
||||
|
||||
---
|
||||
|
||||
## P2: 验收标准
|
||||
|
||||
| 步骤 | 验收标准 | 通过条件 |
|
||||
|------|---------|---------|
|
||||
| Step 0 | Redis启动强校验 | Redis不可用→服务退出 |
|
||||
| Step 0 | WebSocket两层架构 | 多worker消息不丢失 |
|
||||
| Step 0 | Session泄漏修复 | 4个service factory不再泄漏 |
|
||||
| Step 1 | 4张新表创建 | platforms/nodes/command_logs/ssh_sessions存在 |
|
||||
| Step 1 | 数据迁移 | category→node_id回填完成 |
|
||||
| Step 2 | JWT保护所有API | 未带token请求→401 |
|
||||
| Step 2 | TOTP JWT补齐 | setup/enable/disable需JWT |
|
||||
| Step 3 | WebSSH连接 | xterm.js可输入命令 |
|
||||
| Step 3 | 命令日志 | 终端命令记录到command_logs |
|
||||
| Step 4 | Sync四种模式 | 文件/命令/配置/SFTP均可用 |
|
||||
| Step 4 | 并发控制 | Semaphore限流正常 |
|
||||
| Step 5 | 12页面可访问 | 全部HTML加载成功 |
|
||||
| Step 5 | PHP-FPM下线 | Nginx无PHP配置 |
|
||||
|
||||
---
|
||||
|
||||
## P3: 用户手册(大纲)
|
||||
|
||||
1. 登录与两步验证
|
||||
2. 仪表盘概览
|
||||
3. 服务器管理(搜索/筛选/在线状态)
|
||||
4. Web SSH终端使用
|
||||
5. 文件管理
|
||||
6. 批量推送操作
|
||||
7. 脚本库与执行
|
||||
8. 定时调度管理
|
||||
9. 审计日志查询
|
||||
10. 系统设置
|
||||
|
||||
---
|
||||
|
||||
## P4: 文档同步
|
||||
|
||||
| 缺口 | 修复 |
|
||||
|------|------|
|
||||
| 16个模型 vs 文档10张表 | ✅ 本报告已补齐6张新表 |
|
||||
| AdminLTE弃用状态 | ✅ 已在roadmap.md更新 |
|
||||
| 品牌统一 | ✅ 全部使用Nexus |
|
||||
|
||||
---
|
||||
|
||||
## P5: 竞品追踪
|
||||
|
||||
| 竞品 | 最新动态 | 对Nexus启示 |
|
||||
|------|---------|-------------|
|
||||
| JumpServer v3 | 支持多租户+RBAC | Nexus保持"鉴权即信任"简化模型 |
|
||||
| EasyNode | 轻量WebSSH | 已实现xterm.js方案 |
|
||||
|
||||
@@ -0,0 +1,745 @@
|
||||
# AG组调研报告
|
||||
|
||||
> AG组(横向独立支撑,外部情报调研)
|
||||
> 调研日期:2026-05-21 | 分析师:AG Team
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [Redis 在运维管理平台中的最佳实践](#1-redis-在运维管理平台中的最佳实践)
|
||||
2. [FastAPI + Redis + WebSocket 生产级部署最佳实践](#2-fastapi--redis--websocket-生产级部署最佳实践)
|
||||
3. [Python asyncio 在2000+并发连接下的性能调优](#3-python-asyncio-在2000并发连接下的性能调优)
|
||||
4. [类似运维平台的技术栈对比](#4-类似运维平台的技术栈对比)
|
||||
5. [Tailwind CSS v4 在大型管理后台中的应用案例](#5-tailwind-css-v4-在大型管理后台中的应用案例)
|
||||
|
||||
---
|
||||
|
||||
## 1. Redis 在运维管理平台中的最佳实践
|
||||
|
||||
### 1.1 调研结果摘要
|
||||
|
||||
#### 连接池管理 — redis-py 异步连接池
|
||||
|
||||
行业共识推荐**三层弹性池架构**:
|
||||
|
||||
| 层级 | 作用 |
|
||||
|------|------|
|
||||
| 核心池(Core Pool) | 固定数量的长连接,始终保持在线 |
|
||||
| 弹性池(Elastic Pool) | 随负载动态伸缩,上限可配 |
|
||||
| 临时池(Temporary Pool) | 处理突发流量,超时自动回收 |
|
||||
|
||||
**推荐的生产配置参数(redis-py async):**
|
||||
|
||||
```python
|
||||
from redis.asyncio import Redis, BlockingConnectionPool
|
||||
|
||||
pool = BlockingConnectionPool(
|
||||
max_connections=50, # 根据业务QPS调整,Nexus建议50-100
|
||||
timeout=5, # 无可用连接时的等待超时(秒)
|
||||
retry_on_timeout=True, # 超时后自动重试而非立即失败
|
||||
socket_connect_timeout=3, # TCP连接超时
|
||||
socket_timeout=5, # 读写超时
|
||||
health_check_interval=30, # 定期PING检测空闲连接健康状态
|
||||
decode_responses=True, # get()直接返回str而非bytes
|
||||
)
|
||||
```
|
||||
|
||||
**关键发现:** 默认 `ConnectionPool` 在达到 `max_connections` 后会**直接抛异常**(不排队),生产环境应改用 `BlockingConnectionPool` 并设置合理 `timeout`,让请求等待而非直接拒绝。
|
||||
|
||||
#### 高可用架构模式
|
||||
|
||||
| 模式 | 推荐配置 | 适用场景 |
|
||||
|------|---------|---------|
|
||||
| **Sentinel 模式** | >= 3 个 sentinel 节点;`quorum > n/2`;`down-after-milliseconds: 5000ms` | 中小规模,Nexus 首选 |
|
||||
| **Cluster 模式** | >= 3 个 master 节点,每个 1-2 个 replica | 大规模分片需求 |
|
||||
| **Proxy + 连接池** | Twemproxy / Codis / Redis Enterprise proxy | 数千客户端直连时降低 Redis 压力 |
|
||||
|
||||
**关键发现:** 不要允许数千个客户端直连 Redis。网络抖动后的重连风暴会压垮单线程的 Redis 进程。生产环境应使用连接代理减少开放连接数。
|
||||
|
||||
#### 持久化策略对比
|
||||
|
||||
| 策略 | 推荐场景 | 关键配置 | 数据安全 |
|
||||
|------|---------|---------|---------|
|
||||
| RDB 快照 | 纯缓存可容忍丢数据 | `save 900 1` / `save 300 10` | 低 |
|
||||
| AOF everysec | **大部分生产环境推荐** | `appendfsync everysec` | 中(最多丢1秒) |
|
||||
| AOF always | 零容忍(金融场景) | `appendfsync always` | 高(写入性能代价大) |
|
||||
| 混合 RDB+AOF | ✅ **Redis 4.0+ 最佳实践** | `aof-use-rdb-preamble yes` | 高 + 快速启动 |
|
||||
|
||||
**关键发现:** Nexus 当前心跳数据走 Redis TTL(600s),10分钟刷 MySQL 一次。对于心跳这种「丢了也问题不大」的数据,RDB 足够。但 Redis 中可能缓存告警状态、会话令牌等关键信息,建议启用**混合持久化**。
|
||||
|
||||
#### 运维监控关键指标
|
||||
|
||||
| 指标 | 告警阈值 |
|
||||
|------|---------|
|
||||
| `used_memory_rss` | > 85% of maxmemory |
|
||||
| Master-replica 复制延迟 | > 100ms |
|
||||
| `connected_clients` | > 90% of maxclients |
|
||||
| `mem_fragmentation_ratio` | > 1.5(触发碎片整理) |
|
||||
| 热 key / 热分片 | 单分片 > 25K ops/sec |
|
||||
| 单分片大小 | > 25GB |
|
||||
|
||||
#### 常见反模式(2026 年更新)
|
||||
|
||||
1. **无 TTL 缓存 key** — 内存无限增长
|
||||
2. **使用 `KEYS` 命令** — 阻塞 Redis,需改用 `SCAN`
|
||||
3. **JSON blob 存字符串** — 应使用 HASH 或 RedisJSON
|
||||
4. **串行操作(无 Pipeline)** — Pipeline 提升 3-5 倍吞吐
|
||||
5. **无持久化部署** — 重启后数据全丢
|
||||
|
||||
### 1.2 对 Nexus 的参考建议
|
||||
|
||||
| 当前状态 | 建议改进 | 优先级 |
|
||||
|---------|---------|--------|
|
||||
| `use_redis()` 未使用 `BlockingConnectionPool` | 改用 `BlockingConnectionPool` + `health_check_interval` | **P0** — 现有代码需调整 |
|
||||
| `max_connections=50` 在 Redis IoT 优化设计中 | 保持 50-100,用 `redis-cli --latency` + QPS 实测校准 | P1 |
|
||||
| 无持久化配置 | 启用混合 RDB+AOF 持久化(`aof-use-rdb-preamble yes`) | **P0** — 防止重启丢数据 |
|
||||
| 无 Sentinel 高可用 | 至少部署 3 节点 Sentinel;心跳数据可容忍短暂不可用 | P2 |
|
||||
| 未监控 Redis 指标 | 将 `used_memory_rss`, `connected_clients` 加入 Prometheus/Grafana | P1 |
|
||||
| 使用 `decode_responses` 未显式配置 | 统一在连接池配置中显式设置 `decode_responses=True` | P0 |
|
||||
|
||||
**具体整改方案:**
|
||||
|
||||
```python
|
||||
# server/infrastructure/redis.py — 建议重写
|
||||
from redis.asyncio import Redis, BlockingConnectionPool
|
||||
|
||||
_pool: BlockingConnectionPool | None = None
|
||||
_redis: Redis | None = None
|
||||
|
||||
def get_pool_config(url: str) -> dict:
|
||||
return {
|
||||
"url": url,
|
||||
"max_connections": 50,
|
||||
"timeout": 5,
|
||||
"retry_on_timeout": True,
|
||||
"socket_connect_timeout": 3,
|
||||
"socket_timeout": 5,
|
||||
"health_check_interval": 30,
|
||||
"decode_responses": True,
|
||||
}
|
||||
|
||||
async def init_redis(url: str):
|
||||
global _pool, _redis
|
||||
_pool = BlockingConnectionPool(**get_pool_config(url))
|
||||
_redis = Redis(connection_pool=_pool)
|
||||
|
||||
async def close_redis():
|
||||
if _redis:
|
||||
await _redis.aclose()
|
||||
if _pool:
|
||||
await _pool.aclose()
|
||||
|
||||
def get_redis() -> Redis:
|
||||
assert _redis is not None, "Redis not initialized"
|
||||
return _redis
|
||||
```
|
||||
|
||||
### 1.3 相关链接
|
||||
|
||||
- [Redis Anti-Patterns: Common Mistakes Every Developer Should Avoid](https://redis.io/tutorials/redis-anti-patterns-every-developer-should-avoid/)
|
||||
- [Redis分布式解决方案深度解析:从架构模式到弹性连接管理](https://developer.baidu.com/article/detail.html?id=5798282)
|
||||
- [Redis服务配置全景指南:从内存管理到高可用架构的系统性实践](https://www.ctyun.cn/developer/article/759620812607557)
|
||||
- [Redis高并发场景下的高可用架构设计与实践](https://developer.baidu.com/article/detail.html?id=6110012)
|
||||
- [Stack Overflow: Behavior of redis.ConnectionPool with asyncio](https://stackoverflow.com/questions/77855023/behavior-of-redis-connectionpool-with-asyncio-redis-py)
|
||||
- [redis-py官方文档 — Connection Pool](https://deepwiki.com/redis/redis-py/3-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 2. FastAPI + Redis + WebSocket 生产级部署最佳实践
|
||||
|
||||
### 2.1 调研结果摘要
|
||||
|
||||
#### 核心架构模式:Redis Pub/Sub 作为中央消息代理
|
||||
|
||||
当 FastAPI 运行多个 worker/实例时,WebSocket 连接分散在不同进程中。客户端 A 连接到 Server 1,客户端 B 连接到 Server 2,如果没有外部协调,A 的消息无法到达 B。
|
||||
|
||||
**推荐的两层架构:**
|
||||
|
||||
| 层级 | 组件 | 职责 |
|
||||
|------|------|------|
|
||||
| **本地层** | `ConnectionManager`(内存) | 追踪本实例的 WebSocket 连接 |
|
||||
| **分布式层** | `RedisPubSub` 管理器 | 跨实例消息中继 |
|
||||
|
||||
**数据流:**
|
||||
|
||||
```
|
||||
Client → WebSocket → FastAPI Worker 1
|
||||
│ PUBLISH
|
||||
▼
|
||||
Redis Channel
|
||||
│ SUBSCRIBE
|
||||
▼
|
||||
FastAPI Worker 1 ────────┤ SUBSCRIBE
|
||||
FastAPI Worker 2 ────────┤ SUBSCRIBE
|
||||
│ │
|
||||
▼ ▼
|
||||
Local Connection Map Local Connection Map
|
||||
→ forward to clients → forward to clients
|
||||
```
|
||||
|
||||
#### WebSocket 认证最佳实践
|
||||
|
||||
**在 `websocket.accept()` 之前完成认证:**
|
||||
|
||||
```python
|
||||
async def authenticate_websocket(websocket: WebSocket, token: str = Query(...)):
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
user_id = payload.get("sub")
|
||||
if not user_id:
|
||||
await websocket.close(code=4001)
|
||||
return None
|
||||
return {"user_id": user_id}
|
||||
except JWTError:
|
||||
await websocket.close(code=4001) # accept 之前关闭
|
||||
return None
|
||||
```
|
||||
|
||||
要点:
|
||||
- 不认证的连接在 `accept()` **之前** 关闭(code=4001)
|
||||
- JWT 令牌通过查询参数传递(浏览器 WebSocket API 限制)
|
||||
- 生产环境**必须使用 WSS**(WebSocket over TLS)
|
||||
- 长连接需要实现 **token 刷新**机制
|
||||
|
||||
#### ConnectionManager 分布式版本
|
||||
|
||||
```python
|
||||
class ScalableConnectionManager:
|
||||
def __init__(self):
|
||||
self.local_connections: dict[str, WebSocket] = {}
|
||||
self.redis = get_redis()
|
||||
|
||||
async def handle_websocket(self, websocket, client_id, room):
|
||||
await websocket.accept()
|
||||
self.local_connections[client_id] = websocket
|
||||
# 订阅 Redis 频道
|
||||
pubsub = self.redis.pubsub()
|
||||
await pubsub.subscribe(room)
|
||||
|
||||
async def listen_redis():
|
||||
async for message in pubsub.listen():
|
||||
if message["type"] == "message":
|
||||
# 广播给本实例所有本地连接
|
||||
for ws in self.local_connections.values():
|
||||
await ws.send_text(message["data"])
|
||||
|
||||
# 后台监听 Redis 消息
|
||||
asyncio.create_task(listen_redis())
|
||||
|
||||
try:
|
||||
async for data in websocket.iter_text():
|
||||
# 客户端消息 → 发布到 Redis
|
||||
await self.redis.publish(room, data)
|
||||
except WebSocketDisconnect:
|
||||
del self.local_connections[client_id]
|
||||
await pubsub.unsubscribe(room)
|
||||
```
|
||||
|
||||
#### WebSocket 中的异步任务管理
|
||||
|
||||
**关键区别:** `BackgroundTasks` 在 WebSocket handler 中**不工作**!
|
||||
|
||||
```python
|
||||
# ❌ 错误 — BackgroundTasks 不会执行
|
||||
background_tasks.add_task(some_work)
|
||||
|
||||
# ✅ 正确 — 使用 asyncio.create_task
|
||||
asyncio.create_task(some_work())
|
||||
```
|
||||
|
||||
重要注意事项:
|
||||
- **永远不要 `await` 这个 task** — 否则会变成串行
|
||||
- **始终用 try/except 包裹** — 未处理的异常会静默消失
|
||||
- Python 3.11+ 可使用 `TaskGroup` 进行结构化并发
|
||||
|
||||
#### 部署配置要点
|
||||
|
||||
| 关注点 | 推荐做法 |
|
||||
|--------|---------|
|
||||
| **负载均衡** | 配置 sticky sessions(会话亲和性),减少 Redis pub/sub 开销 |
|
||||
| **Rate Limiting** | 基于 Redis 滑动窗口限流,HTTP 和 WebSocket 都做 |
|
||||
| **Circuit Breaker** | 为 Redis 连接实现熔断器,故障时优雅降级 |
|
||||
| **资源清理** | WebSocket 断开时:unsubscribe 频道、清理追踪状态、取消 create_task 引用 |
|
||||
| **在线追踪** | Redis SET 存储在线会话 `online:{user_id}`,支持跨实例查询 |
|
||||
| **健康检查** | 定期 ping/pong 检测死连接,清理僵尸连接 |
|
||||
|
||||
#### 现有可用的库
|
||||
|
||||
| 库 | 说明 |
|
||||
|----|------|
|
||||
| `fastapi_websocket_pubsub` | 开箱即用的 WebSocket Pub/Sub,支持 Redis/PostgreSQL/Kafka 后端 |
|
||||
| `redis.asyncio` | 官方异步客户端,非阻塞 pub/sub |
|
||||
| `socket.io` + `AsyncRedisManager` | 房间广播、命名空间隔离、内置重连 |
|
||||
|
||||
### 2.2 对 Nexus 的参考建议
|
||||
|
||||
| 当前状态 | 建议改进 | 优先级 |
|
||||
|---------|---------|--------|
|
||||
| `server/api/websocket.py` 使用内存 `ConnectionManager` | 必须升级为 Redis Pub/Sub 分布式版本 | **P0** — 多 worker 时当前实现会丢失消息 |
|
||||
| WebSocket 认证依赖 API Key 而非 JWT | 计划中的 JWT 上线后同步更新 WebSocket 认证 | P1 |
|
||||
| 无 WSS 配置 | 确认 Nginx 反向代理已启用 WSS | P0 — 检查现有部署 |
|
||||
| `asyncio.create_task` 未清理 | 在 disconnect handler 中追踪并取消后台任务 | P1 |
|
||||
| 无在线连接数指标 | 暴露 `websocket_connections_active` 给 Prometheus | P2 |
|
||||
| 无心跳/ping-pong | 添加定期 ping/pong 检测僵尸连接 | P1 |
|
||||
|
||||
**对现有 websocket.py 的修改建议:**
|
||||
|
||||
```python
|
||||
# 当前: 纯内存 ConnectionManager
|
||||
# 建议: 两层架构 — 内存 + Redis Pub/Sub
|
||||
|
||||
class ConnectionManager:
|
||||
"""管理 WebSocket 连接 — 本实例 + 跨实例"""
|
||||
|
||||
def __init__(self):
|
||||
self.active_connections: dict[int, WebSocket] = {}
|
||||
self._redis_pubsub = None
|
||||
|
||||
async def connect(self, websocket: WebSocket, server_id: int = None):
|
||||
await websocket.accept()
|
||||
if server_id:
|
||||
self.active_connections[server_id] = websocket
|
||||
# 跨实例广播订阅
|
||||
await self._subscribe_alerts()
|
||||
|
||||
async def broadcast(self, message: dict):
|
||||
"""广播到所有本地连接 + Redis Pub/Sub 跨实例"""
|
||||
# 1. 本地广播
|
||||
dead = []
|
||||
for sid, ws in self.active_connections.items():
|
||||
try:
|
||||
await ws.send_json(message)
|
||||
except WebSocketDisconnect:
|
||||
dead.append(sid)
|
||||
for sid in dead:
|
||||
self.active_connections.pop(sid, None)
|
||||
|
||||
# 2. 跨实例广播(其他 worker 订阅后转发给它们的连接)
|
||||
redis = get_redis()
|
||||
await redis.publish("nexus:alerts", json.dumps(message))
|
||||
```
|
||||
|
||||
### 2.3 相关链接
|
||||
|
||||
- [How to Build WebSocket Servers with FastAPI and Redis](https://oneuptime.com/blog/post/2026-01-25-websocket-servers-fastapi-redis/view)
|
||||
- [How to Build FastAPI WebSocket Chat with Redis](https://oneuptime.com/blog/post/2026-03-31-redis-build-fastapi-websocket-chat-with-redis/view)
|
||||
- [FastAPI Practices — WebSocket & Real-time Communication](https://deepwiki.com/fastapi-practices/fastapi_best_architecture/11.6-websocket-and-real-time-communication)
|
||||
- [fastapi_websocket_pubsub — PyPI](https://pypi.org/project/fastapi-websocket-pubsub/)
|
||||
- [GitHub: scaling-websocket-with-AWS-ALB](https://github.com/Jilan5/scaling-websocket-with-AWS-ALB)
|
||||
- [Build Scalable Real-Time Apps with FastAPI, WebSockets, Redis Pub/Sub](https://python.elitedev.in/python/build-scalable-real-time-apps-with-fastapi-websockets-redis-pub/sub-complete-developer-guide-680bacbd/)
|
||||
|
||||
---
|
||||
|
||||
## 3. Python asyncio 在2000+并发连接下的性能调优
|
||||
|
||||
### 3.1 调研结果摘要
|
||||
|
||||
#### 核心优化策略一览
|
||||
|
||||
| 优化手段 | 性能提升 | 实施难度 |
|
||||
|---------|---------|---------|
|
||||
| **uvloop** 替换默认事件循环 | 40-50% QPS 提升 | **低** — 两行代码 |
|
||||
| **连接池复用** | ~4x 速度提升 | **低** — 全局共享 ClientSession |
|
||||
| **asyncio.Semaphore 并发控制** | 防止资源耗尽 | **低** — 设置合理上限 |
|
||||
| **httptools HTTP 解析器** | ~15-30% 吞吐提升 | **低** — uvicorn[standard] 自带 |
|
||||
| **Pydantic v2** (Rust 核心) | 整体响应时间降低 | **低** — 已在使用 |
|
||||
| **CPU 密集任务卸载到 executor** | 防止阻塞事件循环 | 中 |
|
||||
| **TCP/OS 级调优** | 支撑更高并发上限 | 中 |
|
||||
|
||||
#### uvloop — 最大的单一性能杠杆
|
||||
|
||||
```python
|
||||
import uvloop
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
# 之后一切照常,无需其他改动
|
||||
```
|
||||
|
||||
**效果:** 单机 QPS 从 1,200 提升到 4,800(300% 增益)。
|
||||
|
||||
**注意:** uvloop 仅支持 Linux/macOS。Nexus 部署在宝塔面板(Linux)上,完全适用。
|
||||
|
||||
#### Uvicorn 生产配置(2000 并发目标)
|
||||
|
||||
```bash
|
||||
# 安装带优化的版本
|
||||
pip install "uvicorn[standard]" "fastapi[standard]"
|
||||
|
||||
# 多 worker 启动(推荐)
|
||||
uvicorn server.main:app \
|
||||
--host 0.0.0.0 --port 8600 \
|
||||
--loop uvloop --http httptools \
|
||||
--workers $(nproc) \ # workers = CPU 核心数
|
||||
--limit-concurrency 2000 \ # 最大并发安全阀
|
||||
--backlog 2048 \ # TCP 监听队列
|
||||
--timeout-keep-alive 5 \ # 低 keepalive 快速释放连接
|
||||
--max-requests 5000 \ # 防止内存泄漏
|
||||
--max-requests-jitter 500
|
||||
```
|
||||
|
||||
**关键公式:** async worker 的 worker 数应**等于 CPU 核心数**(不是 `2N+1`),因为每个 worker 已经可以在单线程内处理数千并发。
|
||||
|
||||
#### asyncio.Semaphore 并发控制
|
||||
|
||||
不要无限制地 spawn coroutine。使用 Semaphore 控制并发上限:
|
||||
|
||||
```python
|
||||
# 建议用于 SSH 连接、文件推送等场景
|
||||
sem = asyncio.Semaphore(500) # 同时最多 500 个任务
|
||||
|
||||
async def push_with_limit(server):
|
||||
async with sem:
|
||||
await push_to_server(server)
|
||||
|
||||
# 2000+ 个服务器,但只有 500 个同时在推
|
||||
await asyncio.gather(*[push_with_limit(s) for s in servers])
|
||||
```
|
||||
|
||||
#### 避免阻塞事件循环
|
||||
|
||||
| ❌ 不要这样做 | ✅ 应该这样做 |
|
||||
|-------------|-------------|
|
||||
| `time.sleep(1)` | `await asyncio.sleep(1)` |
|
||||
| `requests.get(url)` (同步) | `httpx.AsyncClient` / `aiohttp` |
|
||||
| 同步 DB 驱动 (pymysql) | SQLAlchemy Async(已在用) |
|
||||
| CPU 密集计算在协程中 | `loop.run_in_executor()` 卸载 |
|
||||
| 同步文件 I/O | `asyncio.to_thread()` (Python 3.9+) |
|
||||
|
||||
#### Python 3.14 新特性预览
|
||||
|
||||
**Free-Threaded CPython(无 GIL)** 将允许 `asyncio` 跨 CPU 核心线性扩展,无 GIL 瓶颈。基准测试显示单线程提升 10-20%,多线程可线性扩展。**Nexus 可在 Python 3.14 稳定后考虑升级。**
|
||||
|
||||
#### OS/TCP 级调优
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.conf 推荐设置(针对 2000+ 并发)
|
||||
net.core.somaxconn = 65535
|
||||
net.ipv4.tcp_max_syn_backlog = 65535
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
fs.file-max = 100000
|
||||
|
||||
# limits.conf — Nexus 用户
|
||||
nexus soft nofile 65535
|
||||
nexus hard nofile 65535
|
||||
```
|
||||
|
||||
### 3.2 对 Nexus 的参考建议
|
||||
|
||||
| 当前状态 | 建议改进 | 优先级 |
|
||||
|---------|---------|--------|
|
||||
| 未使用 uvloop | 在 `server/main.py` 启动时设置 `uvloop.EventLoopPolicy()` | **P0** — 零成本 40-50% 性能提升 |
|
||||
| Uvicorn 启动参数未优化 | 添加 `--limit-concurrency 2000 --backlog 2048 --timeout-keep-alive 5` | P1 |
|
||||
| worker 数未配置 | 设置 `--workers $(nproc)`(等于 CPU 核心数) | P1 |
|
||||
| Semaphore 使用场景有限 | SSH 连接池、文件推送等场景统一使用 Semaphore 控制并发 | P1 |
|
||||
| `pymysql` 同步驱动 | → 确认当前是否已全部迁移到 SQLAlchemy Async | **P0** — 如有同步驱动残留会阻塞事件循环 |
|
||||
| 文件 I/O 使用标准 `open()` | 涉及文件操作的端点使用 `asyncio.to_thread()` | P1 |
|
||||
| OS 级 TCP 参数未调优 | 在部署服务器上应用 sysctl 调优 | P2 |
|
||||
|
||||
**具体整改方案 — main.py:**
|
||||
|
||||
```python
|
||||
# server/main.py — 在文件顶部添加
|
||||
import uvloop
|
||||
import asyncio
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
```
|
||||
|
||||
**Supervisor 启动命令更新 — deploy/nexus.conf:**
|
||||
|
||||
```ini
|
||||
[program:nexus]
|
||||
command=/www/server/.../python -m uvicorn server.main:app \
|
||||
--host 0.0.0.0 --port 8600 \
|
||||
--loop uvloop --http httptools \
|
||||
--workers 4 \
|
||||
--limit-concurrency 2000 \
|
||||
--backlog 2048 \
|
||||
--timeout-keep-alive 5 \
|
||||
--max-requests 10000 \
|
||||
--max-requests-jitter 1000
|
||||
```
|
||||
|
||||
### 3.3 相关链接
|
||||
|
||||
- [Scaling asyncio on Free-Threaded Python (Quansight Labs, Sep 2025)](https://labs.quansight.org/blog/scaling-asyncio-on-free-threaded-python)
|
||||
- [Python协程性能瓶颈分析与调优指导](https://www.php.cn/faq/1907151.html)
|
||||
- [Python异步编程实战:2025年asyncio性能优化指南](https://blog.csdn.net/intxcsht840ht/article/details/151968159)
|
||||
- [协程+连接池:高并发Python爬虫的底层优化逻辑](https://developer.aliyun.com/article/1681826)
|
||||
- [FastAPI Ultra:让Uvicorn、uvloop与Pydantic v2飞起来](http://mp.weixin.qq.com/s?__biz=MzI2ODUyMTQyNA==&mid=2247499254&idx=1&sn=4150a4b5ca8a9bb56603e21fe61f48c1)
|
||||
- [FastAPI production deployment best practices](https://render-www.onrender.com/articles/fastapi-production-deployment-best-practices)
|
||||
- [aioadaptive — 自适应速率限制库](https://pypi.org/project/aioadaptive/)
|
||||
|
||||
---
|
||||
|
||||
## 4. 类似运维平台的技术栈对比
|
||||
|
||||
### 4.1 调研结果摘要
|
||||
|
||||
#### 重要说明:这三个工具不是直接竞品
|
||||
|
||||
| 工具 | 类别 | 主要功能 |
|
||||
|------|------|---------|
|
||||
| **JumpServer** | 堡垒机 / PAM | 集中访问控制、审计、会话管理 |
|
||||
| **Apache Guacamole** | 远程桌面网关 | 浏览器端 RDP/VNC/SSH(无需客户端) |
|
||||
| **BloomRPC** | gRPC API 测试客户端 | 调试 gRPC 服务(类似 Postman for REST) |
|
||||
|
||||
**实际上 JumpServer 内部使用了 Guacamole 的 RDP/VNC 功能**,所以它是互补关系而非竞争。
|
||||
|
||||
#### JumpServer vs Guacamole 详细对比
|
||||
|
||||
| 特性 | JumpServer | Apache Guacamole |
|
||||
|------|-----------|-----------------|
|
||||
| **定位** | 完整堡垒机(4A 合规) | 仅远程桌面网关 |
|
||||
| **协议** | SSH, RDP, VNC, DB, K8s, VMware, Web | RDP, VNC, SSH, Telnet |
|
||||
| **认证** | LDAP, OAuth2, CAS, SAML, MFA, Radius | LDAP, RADIUS, TOTP, CAS, SAML |
|
||||
| **审计** | 完整会话录像 + 命令审计 + 回放 | 会话录像 + 日志 |
|
||||
| **RBAC** | 高级 RBAC + 命令黑/白名单 | 基础 RBAC |
|
||||
| **数据库代理** | 原生 DB 访问(DBeaver 集成) | 不支持 |
|
||||
| **多租户** | 支持 | 不原生支持 |
|
||||
| **云存储** | 审计日志可存 S3/OSS | 仅本地 |
|
||||
| **部署** | Docker / K8s / All-in-One | Docker / Tomcat |
|
||||
| **语言** | Python (Django) + Go (Koko) | Java (Servlet + C) |
|
||||
| **前端** | Vue.js + Luna 终端 | 简洁 HTML5 Web |
|
||||
| **社区** | 非常活跃(中/英文社区) | 活跃(Apache 基金会) |
|
||||
|
||||
#### 新发现的可参考项目
|
||||
|
||||
除了原有的 JumpServer 分析,本次调研发现以下同类项目值得参考:
|
||||
|
||||
| 项目 | 技术栈 | 说明 |
|
||||
|------|--------|------|
|
||||
| **Cockpit** | C + JavaScript | Red Hat 支持的 Web 系统管理面板,轻量级,可直接管理 VM/存储/网络/容器 |
|
||||
| **1Panel** | Go + Vue | 现代 Linux 运维面板,Docker/K8s 集成,应用商店,备份恢复,监控告警 |
|
||||
| **Webmin/Virtualmin** | Perl | 老牌服务器管理面板,模块化插件系统 |
|
||||
| **HestiaCP** | Bash + C | VestaCP 分支,轻量级免费面板,Let's Encrypt 集成 |
|
||||
| **ManageEngine OpManager Nexus** | Java | 商业全栈可观测性平台(网络/服务器/应用性能管理) |
|
||||
|
||||
#### 运维平台开源技术栈图谱(2025)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 运维平台技术栈全景 │
|
||||
├──────────┬──────────┬──────────┬──────────┬──────────────────┤
|
||||
│ 堡垒机 │ 监控 │ 自动化 │ 面板 │ CI/CD │
|
||||
├──────────┼──────────┼──────────┼──────────┼──────────────────┤
|
||||
│JumpServer│Prometheus│ Ansible │ Cockpit │ Jenkins │
|
||||
│Nexus │Grafana │ SaltStack│ 1Panel │ GitLab CI │
|
||||
│ │Zabbix │ Terraform│ Webmin │ GitHub Actions │
|
||||
│ │Nagios │ │ │ │
|
||||
└──────────┴──────────┴──────────┴──────────┴──────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 对 Nexus 的参考建议
|
||||
|
||||
| 洞察 | 对 Nexus 的启示 |
|
||||
|------|----------------|
|
||||
| JumpServer 是堡垒机,Nexus 是运维管理平台 | Nexus 定位不同,更侧重文件推送 + 脚本执行 + 配置管理。堡垒机功能(Web SSH + 审计)是**新增能力**,不是核心定位 |
|
||||
| JumpServer 使用 Django + DRF,Nexus 使用 FastAPI | Nexus 的技术选型更轻量、更高性能。但 JumpServer 的资产模型、权限模型设计思路可以直接复用 |
|
||||
| Cockpit 是轻量级 Web 管理工具 | Nexus 的 Tailwind 前端可以参考 Cockpit 的简洁设计哲学 — 信息密度适中,操作路径短 |
|
||||
| 1Panel 使用 Go + Vue,Docker 优先 | 如果 Nexus 未来走向容器化部署,1Panel 是参考对象 |
|
||||
| ManageEngine OpManager Nexus 是商业产品 | 功能涵盖网络监控、服务器性能、应用管理,但非开源 — 仅作为功能清单参考 |
|
||||
|
||||
**关于 BloomRPC 的澄清:** BloomRPC 是一个 gRPC 测试 GUI 工具,与运维管理平台**完全不在同一赛道**。如项目中无 gRPC 服务,无需关注。Nexus 使用 REST API + WebSocket,无 gRPC 需求。
|
||||
|
||||
**关于 Guacamole 的澄清:** 如果未来需要 RDP/VNC 能力(管理 Windows 服务器等),可考虑集成 Guacamole 作为图形协议网关。当前阶段仅 SSH 就够用。
|
||||
|
||||
### 4.3 相关链接
|
||||
|
||||
- [JumpServer 架构文档](https://docs.jumpserver.org/zh/latest/architecture/)
|
||||
- [JumpServer GitHub](https://github.com/jumpserver/jumpserver)
|
||||
- [Apache Guacamole](https://guacamole.apache.org/)
|
||||
- [Cockpit Project](https://cockpit-project.org/)
|
||||
- [1Panel — 现代 Linux 运维面板](https://github.com/1Panel-dev/1Panel)
|
||||
- [Webmin](https://www.webmin.com/)
|
||||
- [ManageEngine OpManager Nexus](https://www.manageengine.com/it-operations-management/)
|
||||
- [2025年开源服务器面板工具 TOP 10](https://devpress.csdn.net/aiedu/68b93d0fcea93309c2acf6ed.html)
|
||||
- [全开源!巨好用!网工必备的三个堡垒机](https://blog.csdn.net/SPOTO2021/article/details/143589100)
|
||||
|
||||
---
|
||||
|
||||
## 5. Tailwind CSS v4 在大型管理后台中的应用案例
|
||||
|
||||
### 5.1 调研结果摘要
|
||||
|
||||
#### 开源案例研究
|
||||
|
||||
**案例 1: Cleopatra v2**(2026 年 1 月发布)
|
||||
- GitHub: [moesaid/cleopatra](https://github.com/moesaid/cleopatra)
|
||||
- 使用 Tailwind CSS v4 + Vite 完全重写的生产级管理后台模板
|
||||
- 4 个仪表盘模板(Analytics / E-commerce / Crypto / Mission Control)
|
||||
- 10 种强调色 + Light/Dark 主题
|
||||
- **Widget 化组件架构** — SPA 式导航(无页面刷新)
|
||||
- **框架无关**(vanilla JS)— 无 React/Vue/Angular 锁定
|
||||
- 技术栈:Vite + Tailwind v4 + Handlebars + ApexCharts/Chart.js
|
||||
|
||||
**案例 2: AkaDash**(Dev.to 分享,2026)
|
||||
- 从 Tailwind v3 到 v4 的**中期迁移**经验
|
||||
- `@theme` CSS 原生配置比 v3 的 JS 配置更简洁
|
||||
- 报告 **完整构建提速 5 倍**,**增量构建提速 100 倍**(Rust Oxide 引擎)
|
||||
- 组件组织原则:**自包含组件**(charts/tables/forms/navigation/feedback)
|
||||
|
||||
**案例 3: MVPable**(TALL Stack,2025 年 5 月)
|
||||
- Tailwind CSS 4 + Alpine.js + Laravel 12 + Livewire 3 + Filament Admin Panel 4
|
||||
- DaisyUI 提供 32 个预设主题,支持黑暗/明亮模式
|
||||
|
||||
#### Tailwind v4 关键新特性
|
||||
|
||||
| 特性 | 说明 | Nexus 应用场景 |
|
||||
|------|------|---------------|
|
||||
| **CSS-first 配置 `@theme`** | 取代 `tailwind.config.js` | 定义品牌色彩体系、间距、字体 |
|
||||
| **容器查询** | 组件根据父容器尺寸自适应 | 仪表盘卡片、统计面板 |
|
||||
| **`@starting-style`** | 进入/退出过渡动画无需 JS | 侧边栏折叠、模态框动画 |
|
||||
| **动态工具值** | 不受预设值限制 | 自定义间距、尺寸 |
|
||||
| **P3 色域** | 广色域显示器更鲜艳 | 视觉图表、状态指示 |
|
||||
| **暗黑模式 CSS 变量** | 即时切换,无样式闪烁 | 整个管理后台 |
|
||||
|
||||
#### 推荐的文件结构
|
||||
|
||||
```
|
||||
web/
|
||||
├── css/
|
||||
│ ├── app.css # @import "tailwindcss" + @theme 配置
|
||||
│ └── components/ # 组件样式(仅当 Tailwind 工具类不够用时)
|
||||
├── js/
|
||||
│ ├── app.js # 入口
|
||||
│ ├── components/ # UI 组件逻辑
|
||||
│ │ ├── sidebar.js
|
||||
│ │ ├── charts.js
|
||||
│ │ └── tables.js
|
||||
│ └── lib/ # 工具函数
|
||||
├── pages/ # 纯静态 HTML
|
||||
│ ├── login.html # JWT 登录页
|
||||
│ ├── dashboard.html
|
||||
│ ├── servers.html
|
||||
│ └── settings.html
|
||||
└── assets/
|
||||
└── icons/ # SVG 图标(Heroicons / Lucide)
|
||||
```
|
||||
|
||||
#### 设计系统定义(示例)
|
||||
|
||||
```css
|
||||
/* web/css/app.css — Tailwind v4 style */
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
/* Nexus 品牌色 */
|
||||
--color-primary-50: #eff6ff;
|
||||
--color-primary-500: #3b82f6;
|
||||
--color-primary-900: #1e3a8a;
|
||||
|
||||
/* 状态色 */
|
||||
--color-success: #22c55e;
|
||||
--color-warning: #f59e0b;
|
||||
--color-danger: #ef4444;
|
||||
--color-info: #06b6d4;
|
||||
|
||||
/* 布局 */
|
||||
--spacing-sidebar: 16rem; /* 256px */
|
||||
--spacing-header: 3.5rem; /* 56px */
|
||||
|
||||
/* 字体 */
|
||||
--font-sans: "Inter", system-ui, -apple-system, sans-serif;
|
||||
--font-mono: "JetBrains Mono", "Fira Code", monospace;
|
||||
}
|
||||
|
||||
/* 暗黑模式 */
|
||||
@variant dark (&:where(.dark, .dark *));
|
||||
```
|
||||
|
||||
#### AdminLTE → Tailwind v4 组件映射表
|
||||
|
||||
| AdminLTE 概念 | Tailwind v4 等价写法 |
|
||||
|--------------|--------------------|
|
||||
| `.app-wrapper` | `flex h-screen overflow-hidden` |
|
||||
| `.main-header` | `flex items-center h-16 px-6 bg-white border-b shadow-sm` |
|
||||
| `.main-sidebar` | `hidden lg:flex lg:flex-col w-64 bg-gray-900` |
|
||||
| `.content-wrapper` | `flex-1 overflow-y-auto p-6` |
|
||||
| `.container-fluid` | `mx-auto max-w-7xl` |
|
||||
| `.card` | `bg-white rounded-xl shadow-sm border border-gray-200` |
|
||||
| `.card-header` | `px-6 py-4 border-b border-gray-200` |
|
||||
| `.card-body` | `px-6 py-4` |
|
||||
| `.info-box` | `flex items-center gap-4 bg-white rounded-xl p-5 shadow-sm border` |
|
||||
| `.info-box-icon` | `w-12 h-12 flex items-center justify-center rounded-lg` |
|
||||
| `elevation-*` / `shadow` | `shadow-sm`, `shadow-md`, `shadow-lg` |
|
||||
|
||||
#### 交互组件替换方案
|
||||
|
||||
| AdminLTE jQuery 组件 | Tailwind 替代方案 |
|
||||
|---------------------|------------------|
|
||||
| 侧边栏切换 (pushmenu) | Alpine.js: `x-data`, `x-show`, `@click` |
|
||||
| 树形菜单 (treeview) | Alpine.js: accordion 模式 |
|
||||
| 模态框 (Bootstrap Modal) | Alpine.js: `x-show + fixed backdrop` |
|
||||
| 下拉菜单 (Bootstrap Dropdown) | Alpine.js: `x-show` 切换 |
|
||||
| 数据表格 (DataTables) | Tabulator / TanStack Table |
|
||||
| 图表 (Chart.js) | Chart.js(独立可用) |
|
||||
|
||||
### 5.2 对 Nexus 的参考建议
|
||||
|
||||
| 当前状态 | 建议改进 | 优先级 |
|
||||
|---------|---------|--------|
|
||||
| ADR-006 决定全面迁移 | 参考 Cleopatra 的组件架构 — 自包含、框架无关 | P0(路线图) |
|
||||
| 第一个新前端页面是登录页 | 可同时建立 **@theme 设计系统**,后续页面直接复用 | P0 |
|
||||
| 使用纯静态 HTML + JS 调 API | 与 Cleopatra 的框架无关模式一致,其模式可参考 | P0 |
|
||||
| AdminLTE jQuery 依赖需替换 | Alpine.js 作为交互层首选(轻量 ~10KB,与 Tailwind 生态一致) | P1 |
|
||||
| 旧 PHP 端点保持 API Key,新端点用 JWT | 前端在 JWT token 过期时实现静默刷新机制 | P1 |
|
||||
| 图表需求 | Chart.js 可独立使用(无 jQuery 依赖),推荐 ApexCharts 作为替代 | P1 |
|
||||
|
||||
**采用 Alpine.js 的考虑:** Nexus 路线图已确定「纯静态 HTML + JS 调 API」,不引入 Vue/Angular/React。Alpine.js(~10KB)是最轻量的交互层方案,与 Tailwind CSS 生态天然契合,适合实现侧边栏折叠、模态框、下拉菜单、表单验证等交互。
|
||||
|
||||
**迁移策略建议:**
|
||||
|
||||
```
|
||||
Phase 1 — 设计系统(1-2天)
|
||||
├── 建立 app.css: @theme 品牌色/间距/字体/状态色
|
||||
├── 登录页 + JWT 交互(第一个 Tailwind 页面)
|
||||
└── 暗黑模式 CSS 变量 + js 切换
|
||||
|
||||
Phase 2 — 布局壳(2-3天)
|
||||
├── 新增 Tailwind 版布局(sidebar + header + main)
|
||||
├── Alpine.js 侧边栏折叠交互
|
||||
└── 与 AdminLTE 版并存,渐进替换
|
||||
|
||||
Phase 3 — 组件迁移(逐个页面)
|
||||
├── 仪表盘(统计卡片、图表)
|
||||
├── 服务器列表(数据表格、筛选)
|
||||
├── 设置页面(表单、选项)
|
||||
└── ... 按路线图顺序进行
|
||||
```
|
||||
|
||||
### 5.3 相关链接
|
||||
|
||||
- [Cleopatra v2 — Tailwind v4 管理后台模板](https://github.com/moesaid/cleopatra)
|
||||
- [How I Built a Production-Ready Dashboard Template with Tailwind CSS 4.0](https://dev.to/mikezan2024/how-i-built-a-production-ready-dashboard-template-with-tailwind-css-40-187n)
|
||||
- [Multiple Portals, One Codebase: Scalable Theming with Tailwind v4](https://wawand.co/blog/posts/managing-multiple-portals-with-tailwind/)
|
||||
- [Tailwind CSS v4 Migration Guide (2026)](https://dev.to/pockit_tools/tailwind-css-v4-migration-guide-everything-that-changed-and-how-to-upgrade-2026-5d4)
|
||||
- [Tailwind CSS v4 2026: Migration Best Practices](https://www.digitalapplied.com/blog/tailwind-css-v4-2026-migration-best-practices)
|
||||
- [Tailwind CSS v4 — 模板和类迁移指南 (DeepWiki)](https://deepwiki.com/tailwindlabs/tailwindcss/5.4-template-and-class-migration)
|
||||
- [AdminLTE 4 Migration Guide](https://adminlte.io/blog/adminlte-upgrading-from-v3-to-v4/)
|
||||
|
||||
---
|
||||
|
||||
## 总结:优先级排序的行动项
|
||||
|
||||
| 优先级 | 行动项 | 所属调研点 | 预估工作量 |
|
||||
|--------|-------|----------|-----------|
|
||||
| **P0** | main.py 添加 `uvloop.EventLoopPolicy()` | 调研3 | 1 行代码,5 分钟 |
|
||||
| **P0** | Redis 改用 `BlockingConnectionPool` + `health_check_interval` | 调研1 | 1 天(含测试) |
|
||||
| **P0** | Redis 启用混合持久化(RDB + AOF) | 调研1 | 30 分钟(配置变更) |
|
||||
| **P0** | WebSocket 升级为两层架构(内存 + Redis Pub/Sub) | 调研2 | 3-5 天 |
|
||||
| **P0** | 确认 Nginx WSS 配置已启用 | 调研2 | 30 分钟(检查) |
|
||||
| **P1** | Supervisor 配置优化(uvloop/httptools/workers/limit-concurrency) | 调研3 | 30 分钟 |
|
||||
| **P1** | 确认所有数据库操作已迁移到 SQLAlchemy Async | 调研3 | 1-2 天(审查) |
|
||||
| **P1** | 建立 Tailwind v4 @theme 设计系统 | 调研5 | 1-2 天 |
|
||||
| **P1** | 登录页用 Alpine.js 实现交互逻辑(替换 jQuery) | 调研5 | 含在登录页开发中 |
|
||||
| **P1** | WebSocket handler 添加 ping/pong + 僵尸连接清理 | 调研2 | 1 天 |
|
||||
| **P1** | 统一 asyncio.Semaphore 控制 SSH/推送并发 | 调研3 | 2-3 天 |
|
||||
| **P1** | Redis 连接池 `max_connections` 根据实际 QPS 调优 | 调研1 | 持续 |
|
||||
| **P2** | Redis Sentinel 高可用部署 | 调研1 | 1-2 天 |
|
||||
| **P2** | Redis/Prometheus 集成监控 | 调研1+2 | 2-3 天 |
|
||||
|
||||
---
|
||||
|
||||
*报告生成:AG 组 | 2026-05-21 | 基于公开技术资料调研*
|
||||
@@ -0,0 +1,703 @@
|
||||
# JumpServer & EasyNode 深度架构分析 — Nexus 技术参考报告
|
||||
|
||||
> 分析日期:2026-05-21 | 分析师:软件架构师 | 状态:ADRs 提议中
|
||||
|
||||
---
|
||||
|
||||
## 1. 执行摘要
|
||||
|
||||
**分析范围**:jumpserver/jumpserver、jumpserver/koko、jumpserver/luna、jumpserver/lion、chaos-zhu/easynode
|
||||
|
||||
对 Nexus 的核心参考价值排序:
|
||||
|
||||
| 排名 | 项目 | 技术栈 | Stars | 最大参考价值 | 直接可复用度 |
|
||||
|------|------|--------|-------|-------------|-------------|
|
||||
| 1 | **JumpServer Core** | Python + Django | 30.5k | 资产 Platform 模型、RBAC 授权链路、会话审计存储方案 | 设计思路完全可复用,代码需 Django→FastAPI 迁移 |
|
||||
| 2 | **Koko** | Go + Vue | 531 | SSH 连接池、WebSocket-TTY 代理、asciicast 录像、Redis 房间模式 | Go→Python 重写,架构模式可直接借鉴 |
|
||||
| 3 | **EasyNode** | Vue + Node.js | 2k | Web SSH 数据流、批量指令并行队列、凭据加密存储、脚本库设计 | 架构简洁适合小规模参考,Node.js→Python 重写 |
|
||||
| 4 | **Luna** | Angular + TypeScript | 303 | xterm.js 集成方式、多标签终端管理、文件管理器 UI | 直接复用前端方案 |
|
||||
| 5 | **Lion** | Go + Vue | 22 | RDP/VNC Guacamole 连接器 | 仅当 Nexus 需要图形协议时参考 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 资产模型对比分析
|
||||
|
||||
### 2.1 JumpServer 资产模型架构
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ Platform │ (模板层)
|
||||
│ - name (唯一) │
|
||||
│ - category │ host/device/database/cloud/web/gpt/custom
|
||||
│ - type │ linux/windows/mysql/k8s...
|
||||
│ - charset │
|
||||
│ - protocols ────┼──▶ PlatformProtocol (1:N)
|
||||
│ - automation ───┼──▶ PlatformAutomation (1:1)
|
||||
└────────┬─────────┘
|
||||
│ FK (PROTECT)
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Asset │ (实例层 - 多表继承)
|
||||
│ - name, address │
|
||||
│ - platform (FK) │
|
||||
│ - nodes (M2M → Node) │
|
||||
│ - zone (FK → Zone, 可选) │
|
||||
│ - connectivity │
|
||||
│ - gathered_info (JSON) │
|
||||
│ ┌──────┬──────┬──────┬──────┐ │
|
||||
│ │ Host │Device│Database│Cloud│ ... │ (子类特化)
|
||||
│ └──────┴──────┴──────┴──────┘ │
|
||||
└──────────────────────────────────────┘
|
||||
│ M2M
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Node │ (组织层 - 键位树)
|
||||
│ - key: "1:2:3" (冒号分隔层级) │
|
||||
│ - value: "WebServers" │
|
||||
│ - full_value: "/Default/Prod/Web" │
|
||||
│ - child_mark (子节点计数器) │
|
||||
│ - assets_amount (缓存) │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 核心设计决策解读
|
||||
|
||||
#### Platform = 资产模板/类型定义
|
||||
|
||||
- 不在 Asset 表直接存储 `category` 和 `type`,而是通过 `platform FK` 派生
|
||||
- 查询时用 `F()` 表达式优化:`queryset.annotate(category=F("platform__category"))`
|
||||
- **优点**:修改资产分类只需换平台引用,无需改 Asset 记录
|
||||
|
||||
#### Node = 键位树(Key-Based Tree)
|
||||
|
||||
- 不存 `parent_id` 自引用,而是用 `key` 字符串编码层级:`"1:2:3"`
|
||||
- 查询所有后代:`Q(key__istartswith=f'{self.key}:')` —— 一次查询解决
|
||||
- 查询祖先:`key.split(':')` 算出来,`key__in=[...]` 一次查询
|
||||
- **优点**:避免了递归 CTE 和嵌套集(nested set)的复杂度
|
||||
|
||||
#### 多表继承
|
||||
|
||||
- `Asset` 是基表,`Host`, `Device`, `Database`, `Cloud` 等是子表
|
||||
- 每个子类存储类别独有的字段(Host 有 os/arch,Database 有 db_name/db_type)
|
||||
|
||||
### 2.3 Nexus 适配建议
|
||||
|
||||
| JumpServer 设计 | Nexus 适配 | 改造点 |
|
||||
|----------------|-----------|--------|
|
||||
| Platform 模板 | **直接采纳** — 创建 `platforms` 表作为资产类型模板 | Django FK → SQLAlchemy `relationship` + `ForeignKey` |
|
||||
| Node 键位树 | **采纳但简化** — 2000+ 服务器规模用 `parent_id` 自引用更直观,配合 PostgreSQL `ltree` 扩展 | 键位树在 SQLAlchemy 中无原生优势 |
|
||||
| Asset 多表继承 | **改为单表+JSON** — 用 `extra_attrs JSONB` 替代多表继承 | Django 多表继承 → SQLAlchemy 单表 + JSONB |
|
||||
| Protocol 实例级覆盖 | **采纳** — 允许每个 Asset 覆盖默认端口 | 直接复用设计 |
|
||||
| Node 缓存映射 | **采纳但用 Redis** — `{node_id: [asset_ids]}` 的 Redis Hash 缓存 | Django 三级缓存 → 直接用 Redis |
|
||||
|
||||
### 2.4 推荐的 Nexus Asset 模型
|
||||
|
||||
```python
|
||||
# SQLAlchemy Async 模型
|
||||
class Platform(Base):
|
||||
__tablename__ = "platforms"
|
||||
id: int (PK)
|
||||
name: str (unique) # "Linux", "Windows", "MySQL"
|
||||
category: str # "host", "database", "device"
|
||||
type: str # "linux", "mysql", "switch"
|
||||
default_protocols: JSONB # [{"name":"ssh","port":22,"primary":true}]
|
||||
|
||||
class Asset(Base):
|
||||
__tablename__ = "assets"
|
||||
id: UUID (PK)
|
||||
name: str
|
||||
address: str # IP 或域名
|
||||
platform_id: FK → Platform
|
||||
node_id: FK → Node (可选)
|
||||
tags: M2M → Tag
|
||||
protocols: JSONB # 实例级覆盖: [{"name":"ssh","port":2222}]
|
||||
extra_attrs: JSONB # 替代多表继承: {"os":"ubuntu","arch":"x86_64",...}
|
||||
connectivity: str # "ok","err","unknown"
|
||||
last_checked_at: datetime
|
||||
|
||||
class Node(Base):
|
||||
__tablename__ = "nodes"
|
||||
id: UUID (PK)
|
||||
name: str
|
||||
parent_id: FK → Node (nullable) # 自引用树
|
||||
# 或直接用 PostgreSQL ltree:
|
||||
# path: ltree # "root.production.web"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 权限体系对比分析
|
||||
|
||||
### 3.1 JumpServer 四层权限模型
|
||||
|
||||
```
|
||||
Layer 1: 认证 (Authentication)
|
||||
└── 密码/MFA/LDAP/CAS/OIDC/SAML2/OAuth2/RADIUS
|
||||
|
||||
Layer 2: RBAC (角色权限 — 控制管理界面操作)
|
||||
└── Role → RoleBinding → User
|
||||
├── SystemAdmin / SystemAuditor / SystemUser (全局)
|
||||
└── OrgAdmin / OrgAuditor / OrgUser (组织级)
|
||||
|
||||
Layer 3: Asset Permission (资产授权 — 谁能访问什么)
|
||||
└── AssetPermission {
|
||||
users: M2M[User] ← 授权给谁
|
||||
user_groups: M2M[Group]
|
||||
assets: M2M[Asset] ← 授权哪些资产
|
||||
nodes: M2M[Node] ← 授权哪些节点(子节点自动继承)
|
||||
accounts: JSON[account_ids / "@ALL" / "@USER" / "@INPUT"]
|
||||
protocols: JSON ← 允许的协议
|
||||
actions: int ← connect/upload/download/clipboard
|
||||
date_start / date_expired ← 时间窗口
|
||||
is_active: bool ← 启用/停用
|
||||
}
|
||||
|
||||
Layer 4: ACL (访问控制列表 — 精细化控制)
|
||||
├── LoginAssetACL → accept/reject/review/notice/face_verify
|
||||
├── CommandFilterACL → allow/deny/review (正则匹配危险命令)
|
||||
├── ConnectMethodACL → 控制 WebCLI/SSH/RDP 等连接方式
|
||||
└── DataMaskingRule → 数据库查询脱敏
|
||||
```
|
||||
|
||||
### 3.2 资产授权链核心查询(JumpServer 实现)
|
||||
|
||||
```python
|
||||
# AssetPermission.get_all_assets() — 计算用户可访问的完整资产集
|
||||
def get_all_assets(self):
|
||||
# Step 1: 直接授权的资产
|
||||
asset_ids = set(self.assets.all().values_list('id', flat=True))
|
||||
# Step 2: 通过节点授权的资产(含后代节点)
|
||||
nodes_keys = self.nodes.all().values_list('key', flat=True)
|
||||
nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
|
||||
asset_ids.update(nodes_asset_ids)
|
||||
return Asset.objects.filter(id__in=asset_ids)
|
||||
```
|
||||
|
||||
### 3.3 Nexus 适配建议
|
||||
|
||||
| JumpServer 设计 | Nexus 适配 | 说明 |
|
||||
|----------------|-----------|------|
|
||||
| 四层权限模型 | **简化为三层**:认证 → RBAC → 资产授权 | Nexus 初期不需要 ACL 层的全部复杂度 |
|
||||
| AssetPermission 模型 | **直接采纳核心字段** | users/groups/assets/nodes/accounts/date_range/is_active |
|
||||
| 命令过滤 | **后期实现**,初期提供命令日志审计 | 使用正则匹配,会话中实时拦截 |
|
||||
| 账号别名(@ALL/@USER/@INPUT) | **采纳** | JumpServer 的精妙设计 |
|
||||
| 时间窗口授权 | **采纳** | date_start/date_expired,简单有效 |
|
||||
|
||||
### 3.4 推荐的 Nexus 权限模型
|
||||
|
||||
```python
|
||||
class AssetPermission(Base):
|
||||
__tablename__ = "asset_permissions"
|
||||
id: UUID (PK)
|
||||
name: str
|
||||
users: M2M[User]
|
||||
user_groups: M2M[UserGroup]
|
||||
assets: M2M[Asset]
|
||||
nodes: M2M[Node]
|
||||
accounts: JSONB # ["root", "@ALL", "@USER"]
|
||||
actions: JSONB # ["connect", "upload", "download"]
|
||||
date_start: datetime
|
||||
date_expired: datetime
|
||||
is_active: bool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 组件架构对比分析 (Core + Connector 模式)
|
||||
|
||||
### 4.1 JumpServer 组件通信架构
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ Nginx / LB │
|
||||
└───────┬──────────────┘
|
||||
│
|
||||
┌───────────────────────┼───────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Core (Django) │ │ Koko (Go) │ │ Lion (Go) │
|
||||
│ :8080 (REST) │ │ :2222 (SSH) │ │ :3389 (RDP) │
|
||||
│ :8070 (WS) │ │ :5000 (WS) │ │ :8081 (WS) │
|
||||
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
||||
│ │ │
|
||||
│ Redis Pub/Sub │ │
|
||||
├──────────────────────┼──────────────────────┤
|
||||
│ DB 0: Session Rooms │ │
|
||||
│ DB 4: Asset Cache │ │
|
||||
│ DB 6: WS Messages │ │
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌──────────────────────┐
|
||||
│ MySQL/Postgres │ │ Target Assets │
|
||||
└─────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 Bootstrap Token 注册流程
|
||||
|
||||
```
|
||||
Koko 启动
|
||||
│
|
||||
├─ 1. POST /api/v1/terminal/terminal-registrations/
|
||||
│ Header: Authorization: BootstrapToken <TOKEN>
|
||||
│ Body: {"name": "Koko-01", "type": "koko", ...}
|
||||
│
|
||||
▼
|
||||
Core 验证 Bootstrap Token (与 config.txt 比对)
|
||||
│
|
||||
├─ 2. 生成 Access Key (ID + Secret)
|
||||
│ 保存到 Core DB (terminal_terminalregistration 表)
|
||||
│ 返回 {id, key, secret}
|
||||
│
|
||||
▼
|
||||
Koko 保存 Access Key 到本地文件 (data/keys/.access_key)
|
||||
│
|
||||
├─ 3. 后续所有 API 调用使用 Access Key
|
||||
│ Header: Authorization: Key <ACCESS_KEY_ID>:<SIGNATURE>
|
||||
│
|
||||
├─ 4. 心跳保活 (每 30s)
|
||||
│ POST /api/v1/terminal/terminal-registrations/<id>/heartbeat/
|
||||
│
|
||||
▼
|
||||
稳定运行
|
||||
```
|
||||
|
||||
### 4.3 用户连接完整流程
|
||||
|
||||
```
|
||||
用户(Browser) --WS--> Luna --WS--> Koko(:5000) --REST--> Core(:8080) --Redis/DB
|
||||
│ │
|
||||
│ 1. WebSocket Connect │
|
||||
│ 2. TerminalInit(token) │
|
||||
│ │
|
||||
│ 3. Koko 验证 ConnectToken ─────────┤
|
||||
│ 4. Core 返回(asset, account, cred) │
|
||||
│ │
|
||||
│ 5. Koko --SSH--> Target Asset │
|
||||
│ 6. I/O 双向流 │
|
||||
│ 7. asciicast 实时录像 │
|
||||
│ │
|
||||
│ 8. 会话结束,上传录像 ──────────────┤
|
||||
```
|
||||
|
||||
### 4.4 Nexus 适配建议
|
||||
|
||||
| JumpServer 设计 | Nexus 适配 |
|
||||
|----------------|-----------|
|
||||
| **Connector 分离** | **采纳但简化** — Nexus 可将 SSH 代理内嵌为 FastAPI 子进程或 Docker sidecar,不需要独立的 Go 二进制 |
|
||||
| **Bootstrap Token + Access Key** | **采纳** — 内部服务间认证的最佳实践 |
|
||||
| **Redis Pub/Sub 房间模式** | **采纳** — Koko 的多实例会话共享通过 Redis Room,Nexus 多 worker 同理 |
|
||||
| **Connect Token** | **采纳** — 短生命周期的一次性连接令牌,JumpServer 最精华的安全设计 |
|
||||
|
||||
### 4.5 Nexus 推荐架构
|
||||
|
||||
```
|
||||
Nexus API Server (FastAPI)
|
||||
├── /api/v1/* REST API
|
||||
├── /ws/terminal/{token} WebSocket 终端端点
|
||||
└── SSH Proxy Service 内置 SSH 代理 (asyncssh)
|
||||
│
|
||||
├── 连接池管理
|
||||
├── asciicast 录像
|
||||
└── 命令过滤拦截
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Web SSH 方案对比
|
||||
|
||||
### 5.1 三项目数据流对比
|
||||
|
||||
#### Koko (Go) — 最成熟但最重
|
||||
|
||||
```
|
||||
浏览器(Luna, xterm.js)
|
||||
│ WebSocket (JSON: {type:"TERMINAL_DATA", data:"ls\n"})
|
||||
▼
|
||||
Koko HTTPD Server (:5000)
|
||||
├── UserWebsocket → 解析消息类型
|
||||
├── tty.go → TerminalInit(connectToken) 验证
|
||||
├── srvconn/ssh.go → 建立 SSH 连接到目标
|
||||
└── 双向 I/O 代理
|
||||
├── 浏览器 → Koko → SSH (stdin)
|
||||
├── SSH → Koko → 浏览器 (stdout)
|
||||
├── asciicast 录像 (asciinema/asciinema.go)
|
||||
└── 窗口 resize: TerminalResize → ssh.Window{Width,Height}
|
||||
```
|
||||
|
||||
#### EasyNode (Node.js) — 最简洁
|
||||
|
||||
```
|
||||
浏览器(Vue, xterm.js)
|
||||
│ WebSocket (ws://host:8082/terminal?hostId=xxx)
|
||||
▼
|
||||
Koa Server
|
||||
├── controller/terminal.js → 处理 terminal WS
|
||||
├── controller/ssh.js → 凭证管理 (CRUD for NeDB)
|
||||
└── ssh2 npm 库 → SSH 连接到目标
|
||||
└── SSH Stream ↔ WebSocket 双向转发
|
||||
```
|
||||
|
||||
#### Luna (Angular) — Web Terminal UI 参考
|
||||
|
||||
```
|
||||
Luna 核心组件:
|
||||
├── pages-terminal → 终端页面容器 (多标签)
|
||||
├── elements-terminal → xterm.js 包装组件
|
||||
├── elements-replay-asciicast → 录像回放
|
||||
├── elements-sftp → SFTP 文件管理 UI
|
||||
└── 通信层
|
||||
├── WebSocket → /ws/terminal/?token=xxx
|
||||
└── HTTP REST → Core API (/api/v1/...)
|
||||
```
|
||||
|
||||
### 5.2 终端数据流关键细节
|
||||
|
||||
#### WebSocket 消息协议 (Koko)
|
||||
|
||||
```json
|
||||
// 浏览器 → Koko
|
||||
{"id":"uuid","type":"TERMINAL_INIT","data":"{\"cols\":80,\"rows\":24,\"code\":\"...\"}"}
|
||||
{"id":"uuid","type":"TERMINAL_DATA","data":"ls -la\n"}
|
||||
{"id":"uuid","type":"TERMINAL_RESIZE","data":"{\"cols\":120,\"rows\":40}"}
|
||||
{"id":"uuid","type":"TERMINAL_BINARY","raw":"<base64>"}
|
||||
|
||||
// Koko → 浏览器
|
||||
{"id":"uuid","type":"TERMINAL_SESSION","data":"{\"session\":{...}}"}
|
||||
{"id":"uuid","type":"TERMINAL_DATA","data":"[01;34mfile[0m\n"}
|
||||
{"id":"uuid","type":"CLOSE"}
|
||||
```
|
||||
|
||||
#### SSH 连接复用 (Koko)
|
||||
|
||||
```go
|
||||
// koko/pkg/srvconn/ssh.go
|
||||
type SSHClient struct {
|
||||
*gossh.Client
|
||||
refCount int32 // 引用计数
|
||||
traceSessionMap map[*gossh.Session]time.Time // 活跃 session 追踪
|
||||
ProxyClient *SSHClient // 支持跳板机代理链
|
||||
}
|
||||
|
||||
func (s *SSHClient) AcquireSession() // refCount++ 复用连接
|
||||
func (s *SSHClient) ReleaseSession() // refCount-- 还回连接池
|
||||
```
|
||||
|
||||
### 5.3 Nexus 推荐方案
|
||||
|
||||
**综合最优方案:Koko 协议 + EasyNode 简洁架构 + Python 生态实现**
|
||||
|
||||
| 维度 | 建议 | 技术选型 |
|
||||
|------|------|---------|
|
||||
| WebSocket 协议 | **采用 Koko 的消息格式** (TERMINAL_INIT/DATA/RESIZE/CLOSE) | 已验证的协议设计 |
|
||||
| SSH 后端 | **Python asyncssh** (纯 Python, async/await 原生支持) | 比 paramiko 更适合 FastAPI 异步 |
|
||||
| 连接池 | **采用 Koko 的引用计数模式** | 复用 SSH 连接,减少握手开销 |
|
||||
| SFTP | **asyncssh 内置 SFTP 支持** | 无需额外库 |
|
||||
| 窗口 resize | **WebSocket SIGWINCH → ssh.resize_pty()** | 标准实现 |
|
||||
| 前端 | **xterm.js + xterm-addon-fit/weblinks/webgl** | 社区最成熟的 Web Terminal 方案 |
|
||||
| 录像格式 | **asciicast v2 (.cast.gz)** | 与 asciinema 生态兼容 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 会话审计方案
|
||||
|
||||
### 6.1 JumpServer 审计架构
|
||||
|
||||
```
|
||||
会话生命周期:
|
||||
Session.created → Session.connected → Session.disconnected → Session.ended
|
||||
|
||||
录像生成:
|
||||
KoKo: SSH I/O → asciinema/asciinema.go → .cast.gz (单文件)
|
||||
或: → part.1.cast + part.2.cast + ... + replay.json (流式分片)
|
||||
|
||||
存储路径:
|
||||
MEDIA_ROOT/replay/{YYYY-MM-DD}/{session_id}/
|
||||
├── {session_id}.cast.gz # asciicast SSH 录像
|
||||
├── {session_id}.replay.json # 元数据清单 (v3+)
|
||||
├── {session_id}.part.1.cast # 流式分片
|
||||
└── {session_id}.part.N.cast
|
||||
|
||||
存储后端:
|
||||
Local (default_storage) → Celery 异步上传 → S3/OSS/MinIO/Ceph (SERVER_REPLAY_STORAGE)
|
||||
|
||||
命令日志:
|
||||
session.cmd_filter_rules_acls → CommandFilterACL 实时匹配
|
||||
session.command_amount → 统计计数
|
||||
```
|
||||
|
||||
### 6.2 审计相关模型
|
||||
|
||||
```python
|
||||
# JumpServer terminal/models/session.py (核心字段推断)
|
||||
class Session:
|
||||
id: UUID
|
||||
user: FK[User]
|
||||
asset: FK[Asset]
|
||||
account: FK[Account]
|
||||
protocol: str # "ssh", "rdp", "mysql"
|
||||
remote_addr: str # 客户端 IP
|
||||
is_finished: bool
|
||||
date_start: datetime
|
||||
date_end: datetime
|
||||
has_replay: bool
|
||||
has_command: bool
|
||||
command_amount: int
|
||||
terminal: FK[TerminalRegistration] # 哪个 Connector 处理的
|
||||
```
|
||||
|
||||
### 6.3 Nexus 审计方案推荐
|
||||
|
||||
| 功能 | 方案 |
|
||||
|------|------|
|
||||
| 录像格式 | **asciicast v2** (.cast.gz) — 纯文本+gzip,存储成本低,可流式播放 |
|
||||
| 录像生成 | Python `asciinema` 兼容的输出格式,在 SSH 代理层直接生成 |
|
||||
| 录制模式 | **流式分片** (part-based) — 每 5 分钟或 1MB 切一片,避免单文件过大 |
|
||||
| 存储 | 本地磁盘 → 定时任务上传 S3/MinIO (异步) |
|
||||
| 回放 | 前端 xterm.js + asciinema-player 组件 |
|
||||
| 命令日志 | 在 SSH stdin 流中按 `\n` 分割提取,存入 `command_logs` 表 |
|
||||
| 实时监控 | WebSocket 推送在线会话列表,支持管理员实时旁观 (Room 模式) |
|
||||
| 保留策略 | 按天数自动过期删除 (配置化) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 架构决策建议 (ADR 格式)
|
||||
|
||||
### ADR-001: 资产模型采用 Platform 模板 + 单表 Asset + JSONB 扩展属性
|
||||
|
||||
**状态:** 提议
|
||||
|
||||
**上下文:** Nexus 管理 2000+ 服务器,覆盖 Linux/Windows/网络设备/数据库。JumpServer 使用 Django 多表继承实现分类,但这对 SQLAlchemy + FastAPI 过于复杂。
|
||||
|
||||
**决策:** 采用 JumpServer 的 Platform 概念(资产类型模板),但用单表 `Asset` + `extra_attrs JSONB` 替代多表继承。
|
||||
|
||||
**后果:**
|
||||
- 优点:查询简单,JSONB 索引支持高效过滤,新增资产类型无需改表
|
||||
- 缺点:无法在数据库层面约束特定类型的必填字段
|
||||
- 缓解:Pydantic 验证层做类型特定的字段校验
|
||||
|
||||
---
|
||||
|
||||
### ADR-002: Web SSH 采用 BFF (Backend For Frontend) 模式,内嵌 SSH 代理
|
||||
|
||||
**状态:** 提议
|
||||
|
||||
**上下文:** JumpServer 将 Core API 和 Koko 分离为不同进程(Django + Go)。这种分离增加了部署复杂度。Nexus 初期不需要支持多协议(RDP/VNC)。
|
||||
|
||||
**决策:** Nexus 将 SSH 代理功能内嵌在 FastAPI 进程中:
|
||||
- WebSocket 端点 `/ws/terminal/{token}` 直接在 FastAPI 中处理
|
||||
- 使用 `asyncssh` 库建立 SSH 连接
|
||||
- 连接池管理通过 Redis 共享给所有 worker(支持多进程部署)
|
||||
- Connect Token 机制照搬 JumpServer 的设计
|
||||
|
||||
**后果:**
|
||||
- 优点:部署简化(一个服务),开发效率高(全 Python)
|
||||
- 缺点:SSH 代理负载与 API 负载共用资源,大规模场景需要独立部署
|
||||
- 缓解:架构预留分离接口,未来可拆分为独立 SSH Proxy 服务
|
||||
|
||||
---
|
||||
|
||||
### ADR-003: 会话录像采用 asciicast v2 格式 + 流式分片上传
|
||||
|
||||
**状态:** 提议
|
||||
|
||||
**上下文:** JumpServer 的 asciicast 格式生态成熟。EasyNode 没有录像功能。
|
||||
|
||||
**决策:**
|
||||
- 终端 I/O 按 asciicast v2 格式记录(每行 JSON: `[time, type, data]`)
|
||||
- 每 5 分钟或 1MB 生成一个 part 文件
|
||||
- 会话结束后合并为 .cast.gz
|
||||
- 异步上传到 MinIO/S3
|
||||
|
||||
**后果:**
|
||||
- 优点:与 asciinema 生态兼容,纯文本可搜索,gzip 压缩率高
|
||||
- 缺点:不支持 RDP/VNC 图形协议录像(Nexus 不涉及)
|
||||
|
||||
---
|
||||
|
||||
### ADR-004: 权限体系采用 RBAC + AssetPermission 两层
|
||||
|
||||
**状态:** 提议
|
||||
|
||||
**上下文:** JumpServer 四层(认证+RBAC+AssetPermission+ACL)对 Nexus 初期过度设计。
|
||||
|
||||
**决策:**
|
||||
- Layer 1: JWT 认证 + MFA (TOTP)
|
||||
- Layer 2: RBAC 角色 (admin/operator/auditor/user)
|
||||
- Layer 3: AssetPermission (用户/用户组 → 资产/节点 + 账号 + 时间窗口 + 动作)
|
||||
|
||||
ACL 层(命令过滤、连接方式控制)推迟到 v2。
|
||||
|
||||
**后果:**
|
||||
- 优点:实现简单,2000+ 服务器规模够用
|
||||
- 缺点:缺少命令级精细化控制
|
||||
- 缓解:v1 提供命令日志审计(事后追溯),v2 加上实时命令过滤
|
||||
|
||||
---
|
||||
|
||||
### ADR-005: 前端终端方案采用 xterm.js + WebSocket 自定义协议
|
||||
|
||||
**状态:** 提议
|
||||
|
||||
**上下文:** Luna 使用 Angular + xterm.js,EasyNode 使用 Vue + xterm.js。两个项目的 xterm.js 集成模式类似。
|
||||
|
||||
**决策:**
|
||||
- 前端:纯 HTML/Tailwind CSS v4 + 原生 JS 的 xterm.js(不引入 Vue/Angular 框架,与 Nexus 现有技术栈一致)
|
||||
- WebSocket 消息格式:采用 Koko 的消息类型定义(TERMINAL_INIT/DATA/RESIZE/CLOSE/SFTP_*)
|
||||
- 多标签页:轻量级标签管理器
|
||||
|
||||
**后果:**
|
||||
- 优点:零额外框架依赖,体积小,性能好
|
||||
- 缺点:需要自行实现终端标签管理、会话恢复等 UI 逻辑
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术迁移评估:Django → FastAPI
|
||||
|
||||
### 8.1 关键差异对照表
|
||||
|
||||
| 维度 | Django / DRF | FastAPI | 迁移难度 |
|
||||
|------|-------------|---------|---------|
|
||||
| **ORM** | Django ORM (同步, lazy queryset) | SQLAlchemy 2.0 Async (显式 async, eager loading) | **高** |
|
||||
| **序列化器** | DRF Serializer (声明式) | Pydantic v2 (类型注解) | **中** |
|
||||
| **路由** | Django URL conf + ViewSet | FastAPI APIRouter + path decorators | **中** |
|
||||
| **中间件** | Django Middleware | FastAPI Middleware / Depends | **低** |
|
||||
| **权限** | Django Permission + DRF Permission | FastAPI Depends + 自定义权限函数 | **中** |
|
||||
| **分页** | DRF PageNumberPagination | fastapi-pagination 或自行实现 | **低** |
|
||||
| **过滤** | django-filter + FilterSet | SQLAlchemy 查询参数 + fastapi-filter | **中** |
|
||||
| **信号** | Django Signals | SQLAlchemy Events | **低** |
|
||||
| **Celery** | Django-Celery | Celery (独立) | **低** |
|
||||
| **WebSocket** | Django Channels | FastAPI 原生 WebSocket | **低** |
|
||||
| **组织隔离** | `current_org` 线程局部变量 | ContextVar 或中间件注入 | **中** |
|
||||
|
||||
### 8.2 关键迁移模式
|
||||
|
||||
#### Django ORM → SQLAlchemy Async 查询等价写法
|
||||
|
||||
```python
|
||||
# Django: 分页 + 过滤 + 预加载
|
||||
assets = Asset.objects.filter(
|
||||
platform__name="Linux", is_active=True
|
||||
).select_related("platform").prefetch_related("nodes").order_by("name")
|
||||
paginator = PageNumberPagination()
|
||||
page = paginator.paginate_queryset(assets, request)
|
||||
|
||||
# SQLAlchemy Async: 等价写法
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
stmt = (
|
||||
select(Asset)
|
||||
.options(
|
||||
selectinload(Asset.platform),
|
||||
selectinload(Asset.nodes),
|
||||
)
|
||||
.where(Asset.platform.has(name="Linux"), Asset.is_active == True)
|
||||
.order_by(Asset.name)
|
||||
)
|
||||
page = await paginate(session, stmt, offset=offset, limit=limit)
|
||||
```
|
||||
|
||||
#### DRF ViewSet → FastAPI Router
|
||||
|
||||
```python
|
||||
# Django DRF
|
||||
class AssetViewSet(OrgBulkModelViewSet):
|
||||
model = Asset
|
||||
filterset_class = AssetFilterSet
|
||||
search_fields = ("name", "address", "comment")
|
||||
|
||||
@action(methods=["GET"], detail=True, url_path="platform")
|
||||
def platform(self, request, pk=None): ...
|
||||
|
||||
# FastAPI
|
||||
router = APIRouter(prefix="/api/v1/assets/assets", tags=["assets"])
|
||||
|
||||
@router.get("/")
|
||||
async def list_assets(
|
||||
search: str = Query(None),
|
||||
platform: str = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
): ...
|
||||
|
||||
@router.get("/{asset_id}/platform")
|
||||
async def get_asset_platform(
|
||||
asset_id: UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
): ...
|
||||
```
|
||||
|
||||
#### Django Signals → SQLAlchemy Events
|
||||
|
||||
```python
|
||||
# Django
|
||||
@receiver(post_save, sender=Session)
|
||||
def on_session_end(sender, instance, **kwargs):
|
||||
if instance.is_finished:
|
||||
upload_replay_to_s3.delay(instance.id)
|
||||
|
||||
# SQLAlchemy Async
|
||||
@event.listens_for(Session, "after_update")
|
||||
def on_session_update(mapper, connection, target):
|
||||
if target.is_finished:
|
||||
# 注意: async event 需要特殊处理
|
||||
asyncio.ensure_future(upload_replay(target.id))
|
||||
```
|
||||
|
||||
### 8.3 Django ORM ↔ SQLAlchemy 2.0 常用查询对照
|
||||
|
||||
| Django ORM | SQLAlchemy 2.0 Async |
|
||||
|------------|---------------------|
|
||||
| `qs.filter(Q(a=1) \| Q(b=2))` | `where(or_(Model.a==1, Model.b==2))` |
|
||||
| `qs.annotate(cnt=Count('assets'))` | `select(Model).options()` + 子查询或 `column_property` |
|
||||
| `qs.values_list('id', flat=True)` | `select(Model.id)` + `scalars().all()` |
|
||||
| `qs.select_related('platform')` | `.options(selectinload(Model.platform))` |
|
||||
| `qs.prefetch_related('nodes')` | `.options(selectinload(Model.nodes))` |
|
||||
| `qs.bulk_create(objs)` | `session.add_all(objs)` 配合 server-side cursor |
|
||||
| `qs.bulk_update(objs, ['name'])` | `session.execute(update(Model), [...])` |
|
||||
| `qs.get_or_create()` | 手动 select → insert/return |
|
||||
| `Atomic(transaction)` | `async with session.begin():` |
|
||||
| `Node.get_all_children()` (key istartswith) | `where(Node.path.like(f'{parent_path}.%'))` |
|
||||
|
||||
### 8.4 迁移风险与注意事项
|
||||
|
||||
1. **查询性能**:Django ORM 的 lazy loading 在 SQLAlchemy Async 中必须显式声明为 `selectinload` 或 `joinedload`,否则 N+1 问题立现
|
||||
2. **事务管理**:Django 的 `ATOMIC_REQUESTS` 自动包裹请求,FastAPI 需要显式管理 `async with session.begin()`
|
||||
3. **多租户过滤**:JumpServer 的 `current_org` 线程局部变量在 async 环境中不安全,必须用 `ContextVar` 或依赖注入
|
||||
4. **WebSocket 中访问数据库**:Django Channels 支持同步 ORM,FastAPI WebSocket 中需用 `AsyncSession`
|
||||
5. **Pydantic 验证层**:CRUD 场景下,需要在 Pydantic schema 和 SQLAlchemy model 间建立清晰的转换层(create_dto / update_dto / response_dto)
|
||||
|
||||
---
|
||||
|
||||
## 9. 建议的 Nexus 实现优先级
|
||||
|
||||
| 优先级 | 模块 | 参考项目 | 工作量估计 |
|
||||
|--------|------|---------|-----------|
|
||||
| **P0** | 资产 CRUD + Platform 模板 + Node 树 | JumpServer | 2-3 周 |
|
||||
| **P0** | JWT 认证 + MFA | JumpServer | 1 周 |
|
||||
| **P0** | Web SSH 终端 (WebSocket + asyncssh + xterm.js) | Koko + EasyNode + Luna | 3-4 周 |
|
||||
| **P1** | RBAC + AssetPermission 授权 | JumpServer | 2 周 |
|
||||
| **P1** | SFTP 文件管理 (Web 界面) | Luna + EasyNode | 1-2 周 |
|
||||
| **P1** | 会话录像 (asciicast) + 存储 | JumpServer | 2 周 |
|
||||
| **P2** | 批量指令下发 | EasyNode | 1-2 周 |
|
||||
| **P2** | 命令日志 + 审计 | JumpServer | 1 周 |
|
||||
| **P2** | 凭据托管 (加密存储) | EasyNode | 1 周 |
|
||||
| **P3** | 脚本库 | EasyNode | 1 周 |
|
||||
| **P3** | 命令过滤 ACL | JumpServer | 2 周 |
|
||||
|
||||
---
|
||||
|
||||
## 参考来源
|
||||
|
||||
- [JumpServer Architecture Docs](https://docs.jumpserver.org/zh/latest/architecture/)
|
||||
- [JumpServer DeepWiki - Asset Models](https://deepwiki.com/jumpserver/jumpserver/5.2-asset-platform-and-protocol-models)
|
||||
- [JumpServer DeepWiki - Permission System](https://deepwiki.com/jumpserver/jumpserver/6.2-asset-permission-system)
|
||||
- [JumpServer DeepWiki - Terminal Components](https://deepwiki.com/jumpserver/jumpserver/7.1-terminal-components-and-protocols)
|
||||
- [JumpServer DeepWiki - Session Recording](https://deepwiki.com/jumpserver/jumpserver/7.3-session-recording-and-storage)
|
||||
- [JumpServer DeepWiki - ACLs](https://deepwiki.com/jumpserver/jumpserver/6.3-access-control-lists-(acls))
|
||||
- [Koko GitHub](https://github.com/jumpserver/koko)
|
||||
- [Luna GitHub](https://github.com/jumpserver/luna)
|
||||
- [EasyNode GitHub](https://github.com/chaos-zhu/easynode)
|
||||
@@ -0,0 +1,351 @@
|
||||
---
|
||||
name: 部门协作章程
|
||||
description: 定义 8 大部门的职责边界、协作流程、接口规范和决策权限,是项目运行的根本制度。
|
||||
emoji: 📐
|
||||
color: blue
|
||||
group: 管理组
|
||||
---
|
||||
|
||||
# 部门协作章程
|
||||
|
||||
## 一、部门总览
|
||||
|
||||
| # | 部门 | 人数 | 核心职责 | 汇报对象 |
|
||||
|---|------|------|---------|---------|
|
||||
| 👔 | 管理组 | 10 | 战略决策、资源调配、需求拆解、质量终审 | 用户 |
|
||||
| 📦 | 产品部 | 7 | 需求管理、产品路线图、市场研究、PRD 输出 | 产品总监→管理组 |
|
||||
| 🎨 | 设计部 | 10 | UI/UX 设计、Tailwind 设计系统、视觉规范 | 项目经理→管理组 |
|
||||
| 🏗️ | 工程部 | 47 | 后端开发、数据库、API、前端实现 | CTO→管理组 |
|
||||
| 🧪 | 测试部 | 35 | 测试方案、质量验收、缺陷追踪、回归验证 | 质量总监→管理组 |
|
||||
| 🔧 | JC组 | 11 | 运维部署、灾备、日志、缓存、Nginx/SSH | CTO→管理组 |
|
||||
| 🛡️ | ECC组 | 14 | 安全审计、编码规范、文档、深度研究、TDD | CTO→管理组 |
|
||||
| 🤖 | AG组 | 58 | API 设计、AI 工具、代码审查、外部情报投喂 | CTO→管理组 |
|
||||
|
||||
---
|
||||
|
||||
## 二、协作流程
|
||||
|
||||
### 2.1 全流程图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ │
|
||||
用户 ──▶ 项目经理(唯一入口) │ AG 组(横向独立支撑) │
|
||||
│ 外部情报投喂 · AI 工具 · 代码审查 · 性能优化 │
|
||||
═══════════════════════════════════════════════════════════════════════ │
|
||||
▼ │
|
||||
管理组(拆解分派 · 资源调配) │
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ 阶段 1 · 立项 · 产品部主导 │ │
|
||||
├──────────────────────────┤ │
|
||||
│ 产品经理 → PRD 输出 │ │
|
||||
│ Sprint 排序师 → 优先级列表 │ │
|
||||
│ 趋势研究员 → 竞品分析 │ │
|
||||
│ 产品能力评估 → 实现契约 │ │
|
||||
│ │ │
|
||||
│ 产品总监 → 审批 ──┐ │ │
|
||||
└──────────────────│───────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ 阶段 2 · 设计 · 设计部主导 │ │
|
||||
├──────────────────────────┤ │
|
||||
│ 趋势研究员 → 设计趋势报告 │ │
|
||||
│ 灵感分析器 → 色彩/字体分析 │ │
|
||||
│ 情绪板创作者 → 视觉方向 │ │
|
||||
│ 配色策展人 → OKLCH 色板 │ │
|
||||
│ 字体选择器 → 字体配对 │ │
|
||||
│ UX 架构师 → CSS 布局框架 │ │
|
||||
│ UI 设计师 → 组件库 + 令牌 │ │
|
||||
│ 前端设计师 → Tailwind 落地 │ │
|
||||
│ 设计向导 → 一站式编排(可选)│ │
|
||||
└──────────────────────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ 阶段 3 · 开发 · 工程部主导 │◀──── AG 组代码审查 ──── 贯穿全流程 ────▶ │
|
||||
├──────────────────────────┤ │
|
||||
│ 软件架构师 → 技术方案 │ │
|
||||
│ 后端架构师 → 路由+数据模型│ │
|
||||
│ FastAPI 开发 → API 实现 │ │
|
||||
│ 数据库专家 → Schema+迁移 │ │
|
||||
│ 前端开发者 → Tailwind 页面│ │
|
||||
│ 代码审查员 → 审查报告 │ │
|
||||
└──────────────────────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ ECC 组 · 安全 / 编码规范 │◀───────────────────────────────────────── │
|
||||
├──────────────────────────┤ │
|
||||
│ 安全审查 → 安全审计报告 │ │
|
||||
│ 编码规范 → 规范合规检查 │ │
|
||||
└──────────────────────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ 阶段 4 · 测试 · 测试部主导 │ │
|
||||
├──────────────────────────┤ │
|
||||
│ 测试结果分析师 → 测试方案 │ │
|
||||
│ API 测试员 → API 测试报告│ │
|
||||
│ 性能基准师 → 性能基准数据│ │
|
||||
│ E2E 测试 → E2E 报告 │ │
|
||||
│ 安全测试 → 安全测试报告│ │
|
||||
│ Senior QA → 综合质量评估│ │
|
||||
│ Bug Fix Brief → 修复优先级 │ │
|
||||
│ │ │
|
||||
│ 质量总监 → 放行/驳回 ──┐ │ │
|
||||
└──────────────────────│─────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ JC 组 · 部署 │ │
|
||||
├──────────────────────────┤ │
|
||||
│ 灾备规划 → 回滚方案 │ │
|
||||
│ 运维自动化 → Docker/CI/CD │ │
|
||||
└──────────────────────────┘ │
|
||||
▼ │
|
||||
┌──────────────────────────┐ │
|
||||
│ 阶段 5 · 发布 · 管理组终审 │ │
|
||||
├──────────────────────────┤ │
|
||||
│ 运维总监 → 发布许可 │ │
|
||||
│ 项目经理 → 最终放行 ✅ │ │
|
||||
└──────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 各阶段详细说明
|
||||
|
||||
#### 阶段 1:立项(产品部主导)
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| 1.1 | 管理组 | 用户需求 | 项目章程、资源分配 |
|
||||
| 1.2 | 产品部·产品经理 | 项目章程 | 产品需求文档(PRD) |
|
||||
| 1.3 | 产品部·Sprint 排序师 | PRD | Sprint 优先级列表 |
|
||||
| 1.4 | 产品部·趋势研究员 | 市场数据 | 竞品分析报告 |
|
||||
| 1.5 | 产品部·产品能力评估 | PRD | 实现契约(接口/约束/未决项) |
|
||||
| 1.6 | 管理组·产品总监 | PRD + Sprint | 最终审批 |
|
||||
|
||||
#### 阶段 2:设计(设计部主导)
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| 2.1 | 设计部·趋势研究员 | PRD | 设计趋势报告 |
|
||||
| 2.2 | 设计部·灵感分析器 | 参考网站/组件库 | 色彩/字体/布局分析 |
|
||||
| 2.3 | 设计部·情绪板创作者 | 趋势+灵感 | 视觉方向提案 |
|
||||
| 2.4 | 设计部·配色策展人 | 情绪板 | 配色方案(OKLCH 色板) |
|
||||
| 2.5 | 设计部·字体选择器 | 情绪板 | 字体配对方案 |
|
||||
| 2.6 | 设计部·UX 架构师 | 配色+字体 | CSS 架构 + 布局框架 |
|
||||
| 2.7 | 设计部·UI 设计师 | UX 架构 | 组件库 + 设计令牌 |
|
||||
| 2.8 | 设计部·前端设计师 | 组件库 | Tailwind 代码落地 |
|
||||
| 2.9 | 设计部·设计向导 | PRD | 一站式设计编排(可选流程) |
|
||||
|
||||
#### 阶段 3:开发(工程部主导)
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| 3.1 | 工程部·软件架构师 | PRD + 设计系统 | 技术方案文档 |
|
||||
| 3.2 | 工程部·后端架构师 | 技术方案 | FastAPI 路由 + 数据模型 |
|
||||
| 3.3 | 工程部·FastAPI/Python 开发 | 数据模型 | API 端点实现 |
|
||||
| 3.4 | 工程部·数据库专家 | 数据模型 | Schema + 迁移 + 索引 |
|
||||
| 3.5 | 工程部·前端开发者 | API + 设计系统 | Tailwind 页面实现 |
|
||||
| 3.6 | 工程部·代码审查员 | 代码 | 审查报告 |
|
||||
|
||||
> **AG 组**(外部情报投喂 · AI 工具 · 代码审查 · 性能优化 · API 设计)—— 不参与流水线排队。当各部门有问题时,AG 组到外部搜索、调研、获取情报,然后投喂给对应部门。横向独立支撑贯穿全流程。
|
||||
|
||||
#### ECC 组 · 安全 / 编码规范
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| E1 | ECC组·安全审查 | 代码 | 安全审计报告 |
|
||||
| E2 | ECC组·编码规范 | 代码 | 规范合规检查 |
|
||||
|
||||
#### 阶段 4:测试(测试部主导)
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| 4.1 | 测试部·测试结果分析师 | 技术方案 | 测试方案文档 |
|
||||
| 4.2 | 测试部·API 测试员 | API 端点 | API 测试报告 |
|
||||
| 4.3 | 测试部·性能基准师 | 系统 | 性能基准数据 |
|
||||
| 4.4 | 测试部·E2E 测试 | 完整功能 | E2E 测试报告 |
|
||||
| 4.5 | 测试部·安全测试 | 系统 | 安全测试报告 |
|
||||
| 4.6 | 测试部·Senior QA | 全部报告 | 综合质量评估 |
|
||||
| 4.7 | 测试部·Bug Fix Brief | 缺陷列表 | 修复优先级 |
|
||||
| 4.8 | 管理组·质量总监 | 综合报告 | 放行/驳回决策 |
|
||||
|
||||
#### JC 组 · 部署
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| J1 | JC组·灾备规划 | 系统 | 回滚方案 |
|
||||
| J2 | JC组·运维自动化 | 代码 | Docker/CI/CD 配置 |
|
||||
|
||||
#### 阶段 5:发布(管理组终审)
|
||||
|
||||
| 步骤 | 执行者 | 输入 | 输出 |
|
||||
|------|--------|------|------|
|
||||
| 5.1 | 管理组·运维总监 | 测试报告+回滚方案 | 发布许可 |
|
||||
| 5.2 | 管理组·项目经理 | 全部交付物 | 最终放行 |
|
||||
|
||||
---
|
||||
|
||||
## 三、部门接口规范
|
||||
|
||||
### 3.1 产品部 → 设计部
|
||||
|
||||
```
|
||||
产品部交付:
|
||||
- PRD(产品需求文档)
|
||||
- 用户画像 + 场景描述
|
||||
- 功能优先级列表
|
||||
|
||||
设计部接收后启动:
|
||||
- 设计趋势研究 → 情绪板 → 配色/字体 → UX 架构 → UI 组件
|
||||
```
|
||||
|
||||
### 3.2 设计部 → 工程部
|
||||
|
||||
```
|
||||
设计部交付:
|
||||
- Tailwind CSS v4 @theme 配置(颜色/字体/间距/圆角/阴影)
|
||||
- 组件库(HTML + Tailwind 类名)
|
||||
- 布局框架(Stacked/Sidebar/Multi-Column)
|
||||
- 响应式断点规格 + 暗色模式策略
|
||||
- 设计令牌文档
|
||||
|
||||
工程部接收后启动:
|
||||
- 技术方案设计 → 数据建模 → API 开发 → 前端实现
|
||||
```
|
||||
|
||||
### 3.3 工程部 → 测试部
|
||||
|
||||
```
|
||||
工程部交付:
|
||||
- 可部署的代码包(含 CI/CD 配置)
|
||||
- API 文档(Swagger/OpenAPI)
|
||||
- 数据库迁移脚本
|
||||
- 部署说明 + 环境变量清单
|
||||
|
||||
测试部接收后启动:
|
||||
- 制定测试方案 → 功能测试 → 性能测试 → 安全测试 → 验收
|
||||
```
|
||||
|
||||
### 3.4 横向组支撑
|
||||
|
||||
```
|
||||
ECC组提供:安全审计、代码规范检查、TDD 工作流、技术文档
|
||||
JC组提供:CI/CD 流水线、Docker 配置、Nginx 路由、灾备方案、回滚方案、缓存层
|
||||
AG组提供:外部情报投喂、API 设计审查、性能剖析、代码审查、AI 工具(横向独立贯穿全流程)
|
||||
```
|
||||
|
||||
### 3.5 测试部 → 管理组
|
||||
|
||||
```
|
||||
测试部交付:
|
||||
- 测试报告(通过率、缺陷数、严重程度)
|
||||
- 性能基准对比(vs 上一版本)
|
||||
- 安全扫描结果
|
||||
- 验收结论(通过/有条件通过/驳回)
|
||||
|
||||
管理组决策:
|
||||
- 通过 → 发布
|
||||
- 有条件通过 → 指定必须修复项,限期完成
|
||||
- 驳回 → 返回工程部,附测试部缺陷清单
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、决策权限矩阵
|
||||
|
||||
| 决策类型 | AG组 | JC组 | ECC组 | 工程部 | 测试部 | 设计部 | 产品部 | 管理组 |
|
||||
|---------|------|------|------|--------|--------|--------|--------|--------|
|
||||
| 技术选型 | 建议 | 建议 | 建议 | **决定** | - | - | - | 审批 |
|
||||
| 部署发布 | - | 执行 | - | 准备 | - | - | - | **批准** |
|
||||
| 回滚 | - | **执行** | - | 配合 | - | - | - | 通知 |
|
||||
| 架构变更 | 审查 | - | 审查 | 提案 | - | - | - | **批准** |
|
||||
| 安全策略 | - | - | **制定** | 执行 | 验证 | - | - | 审批 |
|
||||
| 编码规范 | 建议 | - | **制定** | 执行 | - | - | - | - |
|
||||
| 设计系统 | - | - | - | 实现 | - | **制定** | - | 审批 |
|
||||
| PRD/路线图 | - | - | - | - | - | - | **制定** | 审批 |
|
||||
| 测试标准 | - | - | - | - | **制定** | - | - | 审批 |
|
||||
| 质量放行 | - | - | - | - | 建议 | - | - | **决定** |
|
||||
| 资源调配 | - | - | - | - | - | - | - | **决定** |
|
||||
|
||||
---
|
||||
|
||||
## 五、沟通规则
|
||||
|
||||
### 5.1 用户交互原则(最高优先级)
|
||||
|
||||
**核心原则:用户只需要和项目经理对话。除非用户明确指定某个部门,否则所有内部调度由管理组自行完成。**
|
||||
|
||||
```
|
||||
用户
|
||||
│
|
||||
│ 「我要做一个 XX 功能」 ← 用户只需描述需求,无需关心哪个部门执行
|
||||
│ 「设计部直接出个方案看看」
|
||||
│ 「让 ECC 组审计一下安全」
|
||||
│
|
||||
▼
|
||||
项目经理(用户唯一联系人)
|
||||
│
|
||||
│ 项目经理理解需求后,在管理组内部协调:
|
||||
│ - 需要 PRD → 通知产品总监安排产品部
|
||||
│ - 需要设计 → 通知项目经理(设计)安排设计部
|
||||
│ - 需要开发 → 通知 CTO 安排工程部 + 横向组
|
||||
│ - 需要测试 → 通知质量总监安排测试部
|
||||
│
|
||||
▼
|
||||
管理组 ──内部分派──▶ 各执行部门
|
||||
```
|
||||
|
||||
**两种情况:**
|
||||
|
||||
| 场景 | 用户怎么做 | 管理组怎么做 |
|
||||
|------|-----------|-------------|
|
||||
| 用户**未指定**部门 | 只描述需求,发给项目经理 | 管理组自行判断需要哪些部门参与,内部调度 |
|
||||
| 用户**明确指定**了部门 | 如「让设计部直接出方案」 | 管理组直接路由到指定部门,无需判断 |
|
||||
|
||||
### 5.2 指令流向
|
||||
|
||||
```
|
||||
用户 ←──→ 项目经理(双向唯一通道)
|
||||
│
|
||||
│ 项目经理在管理组内部协调
|
||||
▼
|
||||
管理组(集体决策)
|
||||
│
|
||||
│ 按章程分派任务
|
||||
▼
|
||||
产品部 / 设计部 / 工程部 / 测试部
|
||||
│
|
||||
│ 横向支撑
|
||||
▼
|
||||
JC组 / ECC组 / AG组
|
||||
```
|
||||
|
||||
**关键约束:**
|
||||
- 用户不需要知道哪个部门在执行——项目经理负责翻译用户需求为部门任务
|
||||
- 项目经理是管理组成员,直接参与管理组决策,无需额外转述
|
||||
- 各部门之间不直接接收用户指令(除非用户明确点名)
|
||||
- 所有跨部门协调由管理组内部消化,不暴露给用户
|
||||
|
||||
### 5.3 冲突升级
|
||||
|
||||
1. **同级协商**:部门间直接沟通解决
|
||||
2. **CTO 仲裁**:技术争议由 CTO 裁定
|
||||
3. **产品总监 仲裁**:需求/设计争议由产品总监裁定
|
||||
4. **项目经理 终裁**:无法达成共识时,项目经理最终决定
|
||||
|
||||
### 5.4 信息同步
|
||||
|
||||
- 跨部门决策必须书面记录(PR 评论 / 文档 / issue)
|
||||
- 接口变更(API / Schema / 设计令牌)必须通知所有下游部门
|
||||
- 阻塞项必须在管理组周会中提出
|
||||
|
||||
---
|
||||
|
||||
## 六、附则
|
||||
|
||||
- 本章程由管理组制定和维护
|
||||
- 部门职责边界以 `docs/skills/` 中各文件的 `group:` 标记为准
|
||||
- 成员变动需同步更新 `docs/team/` 对应部门文件
|
||||
- 本章程每季度评审一次
|
||||
@@ -0,0 +1,45 @@
|
||||
# Nexus 团队编制档案
|
||||
|
||||
> 归档日期: 2026-05-21
|
||||
> 管理组归档
|
||||
|
||||
---
|
||||
|
||||
## 部门编制(194个技能角色)
|
||||
|
||||
| 部门 | 技能数 | 负责人 | 核心职责 |
|
||||
|------|--------|--------|---------|
|
||||
| 工程部 | 57 | CTO | 后端/前端/架构/DevOps/IoT/PHP |
|
||||
| AG组 | 56 | CTO | 外部情报投喂/API设计审查/代码审查/性能 |
|
||||
| 测试部 | 27 | 质量总监 | 单元/集成/E2E/性能/安全测试 |
|
||||
| ECC组 | 11 | CTO | 安全审计/编码规范/TDD/文档 |
|
||||
| 设计部 | 10 | 项目经理(设计) | UI/UX/配色/字体/趋势/前端设计 |
|
||||
| JC组 | 9 | CTO | 运维部署/CI/CD/Nginx/Redis/灾备 |
|
||||
| 管理组 | 8 | 项目经理 | 全局协调/人事/工作室/项目管理 |
|
||||
| 产品部 | 7 | 产品总监 | 产品规格/验收/竞品/用户手册/趋势 |
|
||||
| OMC组 | 2 | — | AI治理/外部上下文 |
|
||||
|
||||
## 签字表状态
|
||||
|
||||
| 部门 | 负责人 | 签名 | 日期 |
|
||||
|------|--------|------|------|
|
||||
| 管理组·项目经理 | — | — | — |
|
||||
| 产品部 | 产品总监 | — | — |
|
||||
| 设计部 | 项目经理(设计) | — | — |
|
||||
| 工程部 | CTO | — | — |
|
||||
| 测试部 | 质量总监 | — | — |
|
||||
| JC组 | CTO | — | — |
|
||||
| ECC组 | CTO | — | — |
|
||||
| AG组 | CTO | — | — |
|
||||
| 质量总监 | 质量总监 | — | — |
|
||||
|
||||
## 角色文件索引
|
||||
|
||||
- 部门角色: `docs/team/roles/*.md` (12个文件)
|
||||
- 技能文件: `docs/skills/*.md` (194个文件)
|
||||
- 签字文件: `docs/reports/signoff-2026-05-21.md`
|
||||
|
||||
---
|
||||
|
||||
> 档案位置: `docs/team/roster-2026-05-21.md`
|
||||
> 管理组归档
|
||||
@@ -1,173 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Nexus MCP Server — 测试/运维工具集 (mcp 1.x API)"""
|
||||
import json, os, re, subprocess
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
API_BASE = "http://127.0.0.1:8600"
|
||||
API_KEY = ""
|
||||
|
||||
def _load_db_creds():
|
||||
"""Load DB credentials from .env file (never hardcode)"""
|
||||
env_path = "/www/wwwroot/api.synaglobal.vip/.env"
|
||||
creds = {"user": "nexus", "pass": "", "db": "nexus"}
|
||||
try:
|
||||
if os.path.exists(env_path):
|
||||
with open(env_path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith("NEXUS_DATABASE_URL="):
|
||||
url = line.split("=", 1)[1].strip()
|
||||
m = re.match(r"mysql\+pymysql://([^:]+):([^@]+)@([^/]+)/(.+)", url)
|
||||
if m:
|
||||
creds["user"] = m.group(1)
|
||||
creds["pass"] = m.group(2)
|
||||
creds["db"] = m.group(4)
|
||||
except Exception:
|
||||
pass
|
||||
return creds
|
||||
|
||||
server = FastMCP("nexus-mcp")
|
||||
|
||||
# ================================================================
|
||||
# T1: API 调用
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def api_call(method: str = "GET", path: str = "/health", body: str = "{}"):
|
||||
"""调用 Nexus Python API。method: GET/POST/PUT/DELETE, path: /api/servers/等, body: JSON字符串"""
|
||||
import urllib.request
|
||||
url = f"{API_BASE}{path}"
|
||||
data = body.encode() if method in ("POST","PUT") else None
|
||||
req = urllib.request.Request(url, data=data, method=method)
|
||||
req.add_header("Content-Type", "application/json")
|
||||
if API_KEY: req.add_header("X-API-Key", API_KEY)
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
return json.dumps(json.loads(resp.read()), indent=2, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T2: 数据库查询 (read-only, credentials from .env)
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def db_query(sql: str = "SELECT COUNT(*) FROM servers"):
|
||||
"""直查 MySQL multisync 数据库。只读查询"""
|
||||
sql_upper = sql.strip().upper()
|
||||
if not sql_upper.startswith("SELECT") and not sql_upper.startswith("SHOW") and not sql_upper.startswith("DESCRIBE"):
|
||||
return "Error: Only SELECT/SHOW/DESCRIBE queries allowed"
|
||||
if ";" in sql.rstrip(";").strip() and len(sql.rstrip(";").strip().split(";")) > 1:
|
||||
return "Error: Multiple statements not allowed"
|
||||
try:
|
||||
creds = _load_db_creds()
|
||||
p = subprocess.run(
|
||||
['mysql', f'-u{creds["user"]}', f'-p{creds["pass"]}', creds["db"], '-e', sql],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
return p.stdout if p.returncode == 0 else p.stderr
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T3: 服务管理
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def service(action: str = "status"):
|
||||
"""管理 Python 后端服务。action: status/start/stop/restart"""
|
||||
try:
|
||||
p = subprocess.run(['systemctl', action, 'multisync'], capture_output=True, text=True, timeout=15)
|
||||
return p.stdout or p.stderr or f"multisync {action}: OK"
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T4: 日志查看
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def logs(lines: int = 50):
|
||||
"""查看 Python 后端日志。lines: 行数"""
|
||||
try:
|
||||
p = subprocess.run(['journalctl', '-u', 'multisync', '-n', str(lines), '--no-pager'],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
return p.stdout[-3000:]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T5: 运行测试
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def run_test():
|
||||
"""运行 test_api.py 端到端测试"""
|
||||
try:
|
||||
p = subprocess.run(['python3', '/www/wwwroot/api.synaglobal.vip/tests/test_api.py'],
|
||||
capture_output=True, text=True, timeout=60,
|
||||
cwd='/www/wwwroot/api.synaglobal.vip')
|
||||
return p.stdout
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T6: Git 日志
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def git_log(count: int = 10):
|
||||
"""查看 project Git 提交历史"""
|
||||
try:
|
||||
p = subprocess.run(['git', 'log', f'-{count}', '--oneline'],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
cwd='/www/wwwroot/api.synaglobal.vip')
|
||||
return p.stdout
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T7: 部署同步
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def deploy():
|
||||
"""同步最新代码到运行目录并重启服务"""
|
||||
try:
|
||||
cmds = [
|
||||
"cd /www/wwwroot/api.synaglobal.vip && git fetch --all 2>&1 && git reset --hard origin/master 2>&1",
|
||||
"systemctl restart multisync 2>&1",
|
||||
]
|
||||
result = []
|
||||
for cmd in cmds:
|
||||
p = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30)
|
||||
result.append(p.stdout or p.stderr or "OK")
|
||||
return "\n---\n".join(result)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# T8: 配置查看
|
||||
# ================================================================
|
||||
@server.tool()
|
||||
async def config_view():
|
||||
"""查看当前非敏感配置(隐藏密码/密钥)"""
|
||||
try:
|
||||
with open('/www/wwwroot/api.synaglobal.vip/.env') as f:
|
||||
lines = []
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
lines.append(line)
|
||||
continue
|
||||
for sensitive in ('DATABASE_URL', 'SECRET_KEY', 'API_KEY', 'ADMIN_PASSWORD',
|
||||
'TELEGRAM_BOT_TOKEN', 'TELEGRAM_CHAT_ID', 'ENCRYPTION_KEY'):
|
||||
if line.startswith(f'NEXUS_{sensitive}='):
|
||||
key = line.split('=', 1)[0]
|
||||
lines.append(f'{key}=***REDACTED***')
|
||||
break
|
||||
else:
|
||||
lines.append(line)
|
||||
return '\n'.join(lines)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
# ================================================================
|
||||
# Main
|
||||
# ================================================================
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(server.run_stdio_async())
|
||||
Reference in New Issue
Block a user