Files
Nexus/docs/design/specs/design-standards.md
T
2026-07-08 22:31:31 +08:00

145 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Nexus 设计标准
> 最后更新: 2026-05-22
> 适用范围: 所有功能迭代、代码审查、安全审计
---
## 1. 安全模型:信任管理员 + 防注入/XSS
**核心理念: 认证管理员是可信用户,但必须防御注入攻击和 XSS。**
运维管理后台的认证管理员即为可信用户,应用层不对操作内容做业务限制。
但必须防御技术层面的攻击向量(命令注入、路径注入、XSS)。
**依据**:
- 使用者均为经过认证的运维管理员
- 绕过应用层限制的门槛极低(SSH 直连即可)
- 应用层业务限制增加维护成本,假安全感大于实际安全收益
- 但技术层面注入攻击是真实风险,必须防御
**实施要求**:
- 所有 API 端点必须校验 JWT 令牌(`Depends(get_current_admin)`
- WebSocket 连接必须通过 JWT 查询参数认证(ADR-011
- Agent 通信使用 X-API-Key 认证
- Shell 命令参数必须 `shlex.quote()` 防注入
- 前端禁止在模板字面量中直接输出用户内容(防 XSS)
- 数据库凭据加密存储(Fernet + AES-CBC
**不做的事**:
- ❌ 不在应用层限制上传文件类型
- ❌ 不在应用层限制文件写入目标路径
- ❌ 不在应用层限制执行命令的业务内容
- ❌ 不做 RBAC 权限体系(保持"鉴权即信任")
- ❌ 不做会话录像(asciicast
---
## 2. 配置存储
| 层 | 存储 | 用途 |
|----|------|------|
| 热层 | Redis | 心跳、缓存、告警去重、Pub/Sub (TTL 管理) |
| 持久层 | MySQL | 服务器、日志、设置、审计 |
| 文件 | `.env` | 启动必需: DB连接、SECRET_KEY、API_KEY |
**配置加载优先级**: `.env` (启动) → MySQL settings 表 (动态覆盖)
**原则**: 可热配项走 MySQL settings`load_settings_from_db` 启动时加载),不可热配项走 `.env`
**不可改配置** (`.env` only):
- SECRET_KEY, API_KEY, ENCRYPTION_KEY, DATABASE_URL
**可改配置** (MySQL settings 可覆盖):
- system_name/title, pool_size/overflow, redis_url, 告警阈值, Telegram 配置
---
## 3. 性能基线
| 指标 | 值 |
|------|-----|
| API 安全并发 | ≤50 |
| P50 延迟 | <60ms |
| P95 延迟 | <300ms |
| 最大文件上传 | 500MB |
| sync_logs 保留 | 30 天 |
| 心跳批量刷到 MySQL | 每 10min |
| SSH 连接池 | 最大 100 连接,空闲 5min 关闭 |
---
## 4. 代码规范
### Python 后端
- 所有 API 路由使用 Pydantic 模型验证请求体(`server/api/schemas.py`
- 所有 API 路由使用 JWT 认证(`Depends(get_current_admin)`
- Shell 命令参数必须 `shlex.quote()` 防注入
- DB session 由 DbSessionMiddleware 自动管理,通过 `request.state.db` 共享
- Service 层通过依赖注入获取 Repository 实例(`server/api/dependencies.py`
- 加密使用 Fernet (Python) + AES-CBC (PHP 兼容),密钥统一从 API_KEY/SECRET_KEY 派生
- Redis 操作使用 `redis.asyncio`,连接池为 `BlockingConnectionPool`
### 前端 (Tailwind + Alpine.js)
- 所有页面引入 `api.js` 共享模块(JWT 自动刷新、apiFetch 封装)
- JWT 令牌存储在 localStorage,自动刷新(2分钟内到期触发)
- 所有 API 调用使用 `apiFetch()` 自动附带 Authorization 头
- CORS 由 `NEXUS_CORS_ORIGINS` 环境变量配置
- 侧边栏和导航在所有页面保持一致
---
## 5. 测试标准
**收尾前必修**: 部署完成后必须用浏览器直接访问页面验证核心流程。curl / API 测试不能替代浏览器验收。
**检查清单**:
- [ ] 登录 → 仪表盘正常加载(含 WebSocket 实时更新)
- [ ] 核心页面可交互(点击、表单提交有响应)
- [ ] 浏览器 Console 无报错
- [ ] 浏览器 Network 面板无 404/500
- [ ] JWT 令牌自动刷新正常
---
## 6. 架构模式
### 后台任务
| 任务 | 间隔 | 文件 |
|------|------|------|
| heartbeat_flush | 10min | `server/background/heartbeat_flush.py` |
| self_monitor | 30s | `server/background/self_monitor.py` |
| schedule_runner | 60s | `server/background/schedule_runner.py` |
| retry_runner | 5min | `server/background/retry_runner.py` |
| asyncssh_cleanup | 60s | `server/infrastructure/ssh/asyncssh_pool.py` |
所有后台任务使用 `asyncio.create_task()``lifespan` 中启动,支持优雅关闭。
### SSH 连接池
引用计数模式 (ADR-004):
- `ssh_pool.acquire(server)` → 获取/创建连接,ref_count +1
- `ssh_pool.release(server_id)` → ref_count -1,空闲连接保留复用
- 清理循环每 60s 扫描,空闲 >5min 的连接自动关闭
### WebSocket 两层架构 (ADR-010)
- Layer 1: 内存 `ConnectionManager` 管理本 worker 的 WebSocket 连接
- Layer 2: Redis Pub/Sub `nexus:alerts` 频道中继跨 worker 消息
- Ping/pong 心跳 30s,僵尸连接 60s 清理
---
## 7. 不做清单(永久)
- ❌ RBAC 权限体系 (保持"鉴权即信任")
- ❌ 会话录像 asciicast (不需要)
- ❌ 移动端适配 (运维不会用手机管理服务器)
- ❌ MQTT 协议 (HTTP+Redis 足够 2000 台)
- ❌ 文件差异比较
- ❌ 图片/PDF 预览
- ❌ 推送结果邮件报告