0f02e047cc
- Split backend/frontend deploy steps - Add deploy-frontend.sh one-liner reference - Document AppAuthMiddleware 404 and stale HTML troubleshooting - Remove obsolete dist copy instructions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
279 lines
14 KiB
Markdown
279 lines
14 KiB
Markdown
# Nexus 6.0 — Project Memory
|
||
|
||
## 项目概述
|
||
Nexus 是一个 2000+ 服务器运维管理平台,从旧的 MultiSync (PHP + AdminLTE + pymysql) 完全重构为 Clean Architecture (FastAPI + Async SQLAlchemy + Redis + WebSocket + Telegram)。前端已全面迁移到 Tailwind CSS v4 + Alpine.js,PHP 依赖已移除。
|
||
|
||
## 仓库结构
|
||
- **唯一仓库**: Nexus (Gitea, 仓库地址在 .env 中配置)
|
||
- **不再使用** multi-server-sync 仓库(旧项目,仅保留历史)
|
||
- **部署路径**: 由 `NEXUS_DEPLOY_PATH` 环境变量决定(安装向导自动写入 .env)
|
||
- **域名/CORS**: 由 `NEXUS_CORS_ORIGINS` 环境变量决定(安装向导自动写入)
|
||
|
||
## 目录结构 (仓库根目录)
|
||
```
|
||
Nexus/
|
||
├── server/ ← Python FastAPI后端 (Clean Architecture 4层)
|
||
│ ├── api/ ← API路由 (含 install.py 安装向导API)
|
||
│ ├── domain/ ← SQLAlchemy模型 (14张表)
|
||
│ ├── infrastructure/← SSH连接池 + Redis + Telegram
|
||
│ ├── background/ ← 后台任务 (心跳刷新/自检/调度/重试)
|
||
│ └── config.py ← Pydantic Settings + MySQL覆盖
|
||
├── web/app/ ← Tailwind+Alpine.js 纯静态前端 (12个HTML页面)
|
||
│ ├── install.html ← 安装向导 (5步)
|
||
│ ├── login.html ← JWT登录+TOTP双因素
|
||
│ └── ... ← index/servers/files/push/scripts等
|
||
├── deploy/ ← Supervisor配置 + Shell健康检查脚本
|
||
├── docs/ ← 项目文档
|
||
├── tests/ ← 测试
|
||
├── .env ← NOT IN GIT (安装向导生成)
|
||
└── requirements.txt ← Python依赖
|
||
```
|
||
|
||
## 启动模式
|
||
- **安装模式** (无 .env): 仅 `/api/install/` + 静态文件可用,其他路由返回503
|
||
- **正常模式** (有 .env): 完整初始化 DB/Redis/后台任务
|
||
|
||
## 已确认的关键决策
|
||
|
||
| 决策 | 选择 | 原因 |
|
||
|------|------|------|
|
||
| API_KEY | ❌ 禁改,UI不显示 | 加密一致性 + 子服务器认证 |
|
||
| SECRET_KEY | ❌ 禁改 | 加密回退密钥 |
|
||
| DATABASE_URL | ❌ 禁改 | 启动必需 |
|
||
| system_name/title | ✅ 可改 | 前端显示 |
|
||
| pool_size/overflow | ✅ 可改 | 安装向导自动推荐 |
|
||
| redis_url | ✅ 可改 | Redis连接 |
|
||
| 告警阈值 (CPU/mem/disk) | ✅ 可改 | 监控参数 (默认80%) |
|
||
| Telegram配置 | ✅ 可改 | 告警推送 |
|
||
| Agent心跳间隔 | 60s | 不太频繁 |
|
||
| Redis→MySQL刷新 | 10min | 降低MySQL负载 |
|
||
| 心跳数据流 | Agent→Redis→前端直读→10min→MySQL | 前端实时, MySQL只存历史 |
|
||
| 告警推送 | WebSocket→前端 + Telegram | 即时感知 |
|
||
| Python守护 | 3层: Supervisor + Python自检(30s) + Shell(cron 1min) | 全覆盖 |
|
||
| 安装向导 | install.html + FastAPI API | 纯Python,无PHP依赖 |
|
||
| 配置源 | 安装向导→config.json+settings表(MySQL)+.env(Python) | 三写一致 |
|
||
| Agent告警心跳 | CPU/内存>80% 或变化>10% 时主动上报 | 智能监控 |
|
||
| 仓库统一 | 所有代码统一在Nexus仓库 | 不再用两个仓库 |
|
||
|
||
## 实现状态 (Phase A+B+C+D+E) — ~99% 完成
|
||
|
||
### ✅ 已完成 (全部6步 + 技术债务 + UX增强)
|
||
| 模块 | 文件 | 说明 |
|
||
|------|------|------|
|
||
| **Step 0: 基础设施** | | Redis + WebSocket |
|
||
| E1 WebSocket推送 | server/api/websocket.py | alert/recovery/system broadcast |
|
||
| E2 心跳→Redis+告警 | server/api/agent.py | Redis write + threshold detection |
|
||
| E3 Redis→MySQL刷新 | server/background/heartbeat_flush.py | 10min batch |
|
||
| **Step 1: 数据层** | | 14张表 |
|
||
| E4 load_settings_from_db | server/config.py | MySQL overrides mutable settings |
|
||
| **Step 2: 认证层** | | JWT + TOTP |
|
||
| **Step 3: Web SSH** | | asyncssh + xterm.js |
|
||
| E5 asyncssh连接池 | server/infrastructure/ssh/asyncssh_pool.py | 引用计数模式 |
|
||
| E6 WebSSH前端 | web/app/terminal.html | xterm.js + Koko协议 + 自动resize |
|
||
| **Step 4: Sync引擎** | | 4种同步模式 |
|
||
| **Step 5: 前端迁移** | | Tailwind+Alpine.js |
|
||
| E7 13个前端页面 | web/app/*.html | 含install.html安装向导 |
|
||
| E8 安装向导API | server/api/install.py | 6端点, 无JWT, 临时引擎 |
|
||
| E9 条件启动模式 | server/main.py | 无.env=安装模式 |
|
||
| **后台任务** | | |
|
||
| E10 Python自检 | server/background/self_monitor.py | 30s loop |
|
||
| E11 Telegram推送 | server/infrastructure/telegram/__init__.py | alert/recovery/system |
|
||
| E12 /health端点 | server/api/health.py | Shell health_monitor.sh checks this |
|
||
| **技术债务** | | |
|
||
| D1 ✅ paramiko删除 | — | pool.py已删除, 全部迁移asyncssh |
|
||
| D2 ✅ install迁移 | server/api/install.py | PHP→install.html+API |
|
||
| D3 ✅ WebSSH前端 | web/app/terminal.html | xterm.js+Koko协议+全屏 |
|
||
| **安全增强** | | |
|
||
| S1 JWT全局保护 | server/api/*.py | 所有业务API都有JWT验证 |
|
||
| S2 审计日志 | server/api/*.py | 所有CUD操作记录审计日志 |
|
||
| S3 登录防暴破 | server/application/services/auth_service.py | 失败计数+15min锁定+429状态码 |
|
||
| **前端UX增强** | | |
|
||
| UX1 共享布局 | web/app/layout.js | 11页面统一侧边栏+全局搜索+用户信息 |
|
||
| UX2 Toast通知 | web/app/api.js | 全局toast()函数,4种类型 |
|
||
| UX3 全局搜索 | server/api/search.py | 跨服务器/脚本/凭据/调度搜索 |
|
||
| UX4 服务器详情 | web/app/servers.html | 点击展开详情面板(系统信息/同步日志/SSH会话) |
|
||
| UX5 脚本编辑 | web/app/scripts.html | 点击脚本名编辑+执行结果格式化 |
|
||
| UX6 推送增强 | web/app/push.html | 逐服务器状态+进度条+推送历史 |
|
||
| UX7 审计分页 | web/app/audit.html | 分页控件+操作过滤+每页50条 |
|
||
| UX8 重试操作 | web/app/retries.html | 手动重试+删除按钮+状态过滤 |
|
||
| UX9 文件浏览 | web/app/files.html | 面包屑导航+目录排序+父目录链接 |
|
||
| UX10 登录安全 | web/app/login.html | 密码可见切换+失败计数+429锁定+shake动画 |
|
||
| **P1功能补全** | | |
|
||
| P1-1 资产管理 | web/app/assets.html | Platform/Node管理页面 |
|
||
| P1-2 服务器表单 | web/app/servers.html | 补全platform/node/protocols字段 |
|
||
| P1-3 Push对接 | web/app/push.html + sync_v2.py | target_path对接+统一SyncEngineV2 |
|
||
| P1-4 安全设置 | web/app/settings.html | TOTP QR码+修改密码+API Key复制 |
|
||
| P1-5 WebSSH授权 | server/api/webssh.py | SSH凭据验证+4003关闭码+审计 |
|
||
| P1-6 Agent认证 | server/api/agent.py | 每服务器API Key + 全局回退 |
|
||
| P1-7 Config去重 | server/application/services/sync_engine_v2.py | sed去重+备份+_rollback_config() |
|
||
| **P2安全增强** | | |
|
||
| P2-1 告警防抖 | server/api/websocket.py | 5分钟冷却+Telegram去重+恢复重置 |
|
||
| P2-2 命令日志 | web/app/commands.html | 命令/会话双视图+服务器过滤+危险命令高亮 |
|
||
| P2-3 DB分页 | server/infrastructure/database/server_repo.py | DB层offset/limit+count |
|
||
| P2-4 Dashboard实时 | web/app/index.html | WS触发stats刷新+声音+标签页闪烁 |
|
||
| P2-5 移动适配 | web/app/layout.js | <768px侧边栏overlay+backdrop+表格滚动 |
|
||
| P2-6 会话安全 | server/api/auth_jwt.py + api.js | 8小时超时+JWT updated_at+刷新重用检测 |
|
||
| P2-7 凭据加密 | server/api/servers.py | Server.password/ssh_key_private Fernet加密存储 |
|
||
| P2-8 凭据脱敏 | server/api/scripts.py + servers.py | API响应不返回密码,password_set布尔标记 |
|
||
| P2-9 DB备份 | deploy/db_backup.sh | mysqldump+30天保留+cron集成 |
|
||
| **P3运维增强** | | |
|
||
| P3-1 Nginx重定向 | deploy/nginx_https.conf | 注释化301规则:裸页面路径→/app/前缀 |
|
||
| P3-2 自定义404 | server/main.py | StarletteHTTPExceptionHandler返回空白HTML,隐藏FastAPI标识 |
|
||
|
||
### 🔄 待测试
|
||
- T1: 心跳Redis写入验证
|
||
- T2: WebSocket连接测试
|
||
- T3: 3层守护测试 (kill Python→Supervisor重启)
|
||
- T4: Telegram推送测试
|
||
- T5: install.html 5步流程测试
|
||
|
||
### 测试流程 (用户确认)
|
||
1. WSL本地基础测试 → 2. 推到仓库 → 3. 外网服务器正式测试
|
||
|
||
## 配置三写机制
|
||
安装向导 Step 3 POST处理同时写入:
|
||
1. `web/data/config.json` — 共享配置 (API_BASE_URL, DB_*, API_KEY)
|
||
2. `.env` — Python后端启动必需 (NEXUS_前缀)
|
||
3. `settings` MySQL表 — 共享动态配置 (api_key, thresholds, Telegram等)
|
||
|
||
不可改项 (SECRET_KEY/API_KEY/ENCRYPTION_KEY/DATABASE_URL) 不在MySQL覆盖映射中。
|
||
|
||
## 3层守护机制
|
||
- **Layer 1**: Supervisor — Python崩溃自动重启
|
||
- **Layer 2**: Python self_monitor — 每30s检查Redis/MySQL/WebSocket
|
||
- **Layer 3**: Shell health_monitor.sh — 每分钟HTTP GET /health, 连续3次失败→supervisorctl restart + Telegram
|
||
|
||
## 实现原则(强制 — 见 `.cursor/rules/perfect-implementation.mdc`)
|
||
|
||
**所有设计和实现都必须是完美实现。**
|
||
|
||
- ❌ 不允许为了实现而实现 — 每一行代码必须有明确的目的和质量标准
|
||
- ❌ 不允许降级实现 — 不得以「先用着」为由降低安全、架构或代码质量标准
|
||
- ❌ 不允许妥协实现 — 不得在发现问题后选择绕过而非根治
|
||
- ❌ 不允许留技术债 — 发现问题必须当场修复,不得标注 TODO/FIXME 延后
|
||
- ✅ 无法完美实现时必须停下来与用户交流后再换方案,不得擅自从权
|
||
|
||
**文档纪律:**
|
||
|
||
- 新功能/架构变更:先写 `docs/design/specs/YYYY-MM-DD-*-design.md`,再写代码
|
||
- 开发前:写 `docs/design/plans/` 或 `docs/reports/` 技术文档(文件清单、步骤、测试、回滚)
|
||
- 项目缺文档的模块:改动时一并补全
|
||
- 每次修改:在 `docs/changelog/YYYY-MM-DD-*.md` 留 changelog
|
||
|
||
---
|
||
|
||
## 强制文件修改流程(不可跳步)
|
||
|
||
```
|
||
实现 → WSL本地验证 → ★审计8步★ → 部署 → 健康检查 → 浏览器验证 → changelog
|
||
```
|
||
|
||
**进度条(每次改代码必须输出,每完成一步打勾):**
|
||
|
||
```
|
||
□实现 □WSL验证 □审计8步 □部署 □健康检查 □浏览器验证 □changelog
|
||
```
|
||
|
||
用户只需看:没打勾的格子在打勾的格子后面 = 跳步了。
|
||
跳步 = 严重过程违规,用户说「你跳步了」必须立即停止并补完。
|
||
|
||
审计8步(我内部执行,用户不需要检查细节):登记→全文Read→规则扫描H→Closure表→入口表→输入→Sink→归类→DoD
|
||
审查4维度30项:安全12+逻辑8+性能5+质量5
|
||
三个铁律:每个文件都要审、每行代码都要看、每个结论都要有证据
|
||
|
||
**程序门控 v2(deploy 时自动检查 7 道,不过不让部署):**
|
||
|
||
| # | 门 | 检查内容 | 防什么 |
|
||
|---|----|---------|--------|
|
||
| 1 | Changelog门 | 文件存在 **且行数≥10** | 空壳changelog |
|
||
| 2 | Audit门 | 文件存在 **且含 Step3+Closure+DoD** | 空壳审计 |
|
||
| 3 | Test门 | test_api.py **必须存在**且通过 | 删测试绕过 |
|
||
| 4 | Lint门 | `ruff check server/` 零错误 | 代码质量问题 |
|
||
| 5 | Import门 | `import server.main` 成功 | 语法/导入错误 |
|
||
| 6 | Security门 | `bandit` 无 HIGH/MEDIUM | 安全反模式 |
|
||
| 7 | Review门 | 审计文件包含实际改动文件清单 | 假审查 |
|
||
|
||
门控脚本: `deploy/pre_deploy_check.sh`(7门)
|
||
门控日志: `deploy/gate_log.jsonl`(每次检查自动追加记录)
|
||
MCP deploy 工具在 git pull + restart 前自动执行门控检查,任何一门不过返回 `🚫 DEPLOY BLOCKED`
|
||
MCP gate_log 工具可查看历史门控记录
|
||
审计模板: `docs/audit/TEMPLATE.md`
|
||
Lint配置: `ruff.toml`
|
||
开发依赖: `requirements-dev.txt`(ruff, bandit, pytest)
|
||
|
||
## 部署流程(SSH + SCP,不是 MCP deploy)
|
||
|
||
**MCP deploy 不可用**(GitHub 等 SaaS 的远程服务器 git pull 无凭据),实际部署用 SSH 命令直接操作:
|
||
|
||
### 后端部署
|
||
|
||
```bash
|
||
# 1. Push 本地到 Gitea
|
||
git push origin main
|
||
|
||
# 2. 服务器拉取最新代码 + 重启
|
||
ssh nexus "cd /www/wwwroot/api.synaglobal.vip && git fetch --all && git reset --hard origin/main && supervisorctl restart nexus"
|
||
|
||
# 3. 健康检查
|
||
ssh nexus "sleep 3 && curl -s http://127.0.0.1:8600/health"
|
||
# 预期: "ok"(纯文本,非 JSON)
|
||
```
|
||
|
||
### 前端部署(Vuetify SPA)
|
||
|
||
Vite 直接输出到 `web/app/`(后端 StaticFiles 挂载点),无需 dist 复制步骤:
|
||
|
||
```bash
|
||
# 一键部署脚本(构建+打包+上传+提取+重启+验证)
|
||
bash deploy/deploy-frontend.sh
|
||
|
||
# 或手动步骤:
|
||
cd frontend && npx vite build && cd ..
|
||
tar czf /tmp/nexus-frontend.tar.gz -C web/app index.html assets/
|
||
scp /tmp/nexus-frontend.tar.gz nexus:/tmp/nexus-frontend.tar.gz
|
||
ssh nexus "cd /www/wwwroot/api.synaglobal.vip/web/app && tar xzf /tmp/nexus-frontend.tar.gz && rm /tmp/nexus-frontend.tar.gz"
|
||
ssh nexus "supervisorctl restart nexus"
|
||
```
|
||
|
||
**注意**: 前端构建产物 (`web/app/index.html`, `web/app/assets/`) 已在 `.gitignore` 中,不进入 git。每次部署需重新构建+上传。
|
||
|
||
### 浏览器验证
|
||
```bash
|
||
curl -s -o /dev/null -w "%{http_code}" http://47.254.123.106:8600/app/ # 预期: 200
|
||
```
|
||
|
||
### 部署常遇问题
|
||
| 问题 | 解决 |
|
||
|------|------|
|
||
| `/app/` 返回 404 | AppAuthMiddleware 拦截 — 确认 `/app/` 和 `/app/index.html` 在 `_APP_PUBLIC_PATHS` 中 |
|
||
| `/app/` 返回旧 HTML (Tailwind/Alpine) | `git reset --hard` 恢复了旧文件 — 需要重新 tar+scp 前端构建产物 |
|
||
| MCP deploy → git pull 失败 `could not read Username` | 服务器 remote URL 没配 token,用 `git remote set-url` |
|
||
| Gate 2 Audit BLOCKED | 创建 `docs/audit/YYYY-MM-DD-topic.md`,含 Step3+Closure+DoD |
|
||
| Gate 3 Test BLOCKED (401) | 服务器 .env 中 `NEXUS_TEST_ADMIN_PASSWORD` 与数据库不一致 |
|
||
| Gate 6 Security BLOCKED (bandit HIGH) | md5 → sha256 等,参看 bandit 报告修复 |
|
||
| Gate 7 Review BLOCKED | 审计文件缺少实际改动文件清单 |
|
||
|
||
### 部署信息速查
|
||
- **部署用户**: root
|
||
- **部署目录**: `/www/wwwroot/api.synaglobal.vip/`
|
||
- **Supervisor 服务名**: `nexus`
|
||
- **Supervisor 配置**: `/www/server/panel/plugin/supervisor/etc/supervisor.conf`
|
||
- **SSH 别名**: `ssh nexus` (47.254.123.106, key: id_rsa_nexus)
|
||
- **后端端口**: 8600
|
||
- **Gitea Token SHA**: `26fee743a9332895b55f79b2dbe2e931dc7c2fe5` (nexus-deploy-token, write:repository)
|
||
|
||
**用户审查清单(验证AI是否偷懒):**
|
||
1. 看 `deploy/gate_log.jsonl` — 每次门控都有记录,没有记录 = 没过门控
|
||
2. 看审计文件 — 必须有 Closure 表(每个H的判定+依据)、Step 3 规则扫描、DoD
|
||
3. 看进度条 — ☑必须在□前面,否则跳步
|
||
4. 看 changelog — 行数≥10,不能是空壳
|
||
|
||
## 数据流
|
||
```
|
||
Agent心跳(60s) → Redis(实时) → 前端直读 → 10min批量 → MySQL(历史)
|
||
告警: CPU/mem/disk > threshold → WebSocket推送浏览器 + Telegram推送手机
|
||
恢复: 之前告警的指标恢复正常 → 自动推送恢复通知
|
||
Redis心跳key: heartbeat:{server_id} (HSET, TTL=600s)
|
||
Redis告警key: alerts:{server_id} (SET, TTL=3600s)
|
||
```
|