Files
OpenFlare/docs/design/development.md
T
2026-05-13 11:44:01 +08:00

295 lines
10 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.
# 开发约束
本文档融合原开发规范、前端规范与开发计划,是 OpenFlare `1.0.0` 之后的工程约束入口。
## 当前结论
* 第一版至第六版的主线能力已经全部完成。
* `1.0.0` 是当前正式基线。
* 已完成阶段的过程性任务以代码、测试与 Git 历史为准。
* 新工作优先以缺陷修复、可维护性改进、文档与测试补强为主。
当前开发优先级:
1. 稳定性。
2. 升级与回滚链路可靠性。
3. 文档准确性。
4. 测试覆盖补强。
5. 在既有边界内的小步迭代。
## 变更准入
新需求进入实现前,按以下顺序判断:
1. 是否符合 [产品边界](./)。
2. 是否符合本文档的后端、Agent 与前端约束。
3. 是否会破坏现有发布、同步、回滚或升级主链路。
4. 是否需要同步更新部署、配置、README 或文档站页面。
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
任何合入正式基线的改动,至少应满足:
* 不破坏 Agent 心跳、同步、发布与回滚主链路。
* 不破坏现有 OpenResty 主配置托管模型。
* 不降低总览、节点详情与访问分析的既有可用性。
* 有与风险相称的测试或联调验证。
* 文档与代码保持一致。
## 技术基线
Server:
* Go 1.24+
* Gin
* GORM
* SQLite / PostgreSQL
* 现有登录体系
Agent:
* Go 1.24+
* 单二进制
* 节点本地执行
* `openresty_path` 优先
* 无 `openresty_path` 时默认 Docker OpenResty
Frontend:
* Next.js 15 App Router
* React 19
* TypeScript 5
* Tailwind CSS 4
* TanStack Query
* React Hook Form + Zod
* Zustand 仅用于轻量客户端状态
* ESLint + Prettier
* Vitest + Testing Library + Playwright
* pnpm
## Server 分层
| 目录 | 职责 |
| --- | --- |
| `controller/` | 参数解析、调用 service、返回响应 |
| `service/` | 业务逻辑、校验、事务编排、渲染 |
| `model/` | 模型定义与持久化 |
| `router/` | 路由注册 |
| `middleware/` | 认证、鉴权、限流等横切逻辑 |
| `common/` | 配置、全局状态与初始化入口 |
| `utils/` | 纯工具函数与通用 helper |
禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
## Agent 分层
Agent 保持现有模块边界:
* `config`
* `heartbeat`
* `sync`
* `openresty` / `nginx`
* `state`
* `httpclient`
* `protocol`
* `internal/updater`
要求:
* 每个模块职责单一。
* 外部命令调用集中封装。
* 状态落盘与配置落盘分离。
## Frontend 分层
推荐目录:
```text
app/
components/
features/
lib/
hooks/
store/
types/
styles/
tests/
```
职责约束:
* `app/`:路由、布局、页面组装。
* `features/`:按业务域组织模块。
* `components/`:跨 feature 复用组件。
* `lib/`:请求客户端、环境变量、工具函数、常量。
* `store/`:少量跨页面 UI 状态。
* `types/`:共享类型定义。
页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
## 数据模型规范
当前有效实体:
* `proxy_routes`
* `origins`
* `config_versions`
* `nodes`
* `auth_sources`
* `external_accounts`
* `node_system_profiles`
* `apply_logs`
* `tls_certificates`
* `managed_domains`
* `node_request_reports`
* `node_access_logs`
* `node_metric_snapshots`
* `traffic_analytics_rollups`
* `node_health_events`
* `options`
通用约束:
* 不新增平台化对象,除非设计文档明确要求。
* `origins` 仅作为可复用源站地址目录,字段保持轻量。
* `proxy_routes` 以“网站配置”作为聚合边界,必须包含唯一 `site_name` 与非空 `domains` 列表。
* `proxy_routes.domains` 中的每个域名都必须全局唯一,列表第一项视为主域名。
* `proxy_routes` 继续允许保存一个或多个上游地址用于负载均衡,但不引入独立 `origin_pool`。
* 遗留 `domain` 字段只能作为 `domains[0]` 的兼容镜像;新代码不得继续以该字段作为唯一业务输入。
* `proxy_routes` 如关联 `origins`,必须同时保存可直接渲染的 `origin_url`。
* 上游统一使用 named `upstream` + keepalive;单上游如带 base path 或 query,应在 `proxy_pass` 上补回 URI,多上游仅允许纯 `scheme://host[:port]`。
* 流量限制、反向代理与缓存配置当前都归属站点级 `proxy_routes`。
* HTTPS 证书绑定必须通过与 `domains` 平行的 `domain_cert_ids` 逐域名保存;未绑定证书的域名不得参与 HTTPS 渲染。
* `config_versions` 必须保存完整快照与渲染结果。
* 全局同时只能有一个激活版本。
* 回滚通过重新激活旧版本实现。
* `nodes` 只保留控制面状态与低频摘要。
* 观测数据必须按节点与时间窗口关联,快照与聚合结果采用追加式模型。
* 原始访问明细必须有受控保留策略。
* `auth_sources` 仅保存管理端第三方登录源配置,当前支持 `github` 与 `oidc`。
* `external_accounts` 是第三方账号与本地用户的唯一绑定来源;旧 `users.github_id` 仅用于兼容迁移,不得作为新登录流程的业务输入。
## 数据库迁移
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
数据库版本号定义在 `openflare_server/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
如果迁移失败或校验失败,启动流程必须中止,且不得提升数据库版本记录。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
## API 与鉴权
管理端与 Agent API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
```json
{
"success": true,
"message": "",
"data": {}
}
```
约定:
* Agent API 固定放在 `/api/agent/*`。
* 总览与节点详情优先使用专用聚合接口。
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
* 管理端继续复用现有登录、角色与 Session。
* 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root Session。
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
* 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。
* Agent 正式请求统一使用节点专属 `agent_token`。
* 首次接入可使用全局 `discovery_token`。
* Agent 请求头统一使用 `X-Agent-Token`。
禁止暴露远程 shell 或任意命令执行入口,禁止在日志中打印完整 Token,禁止绕过占位符约束保存不可渲染的主配置模板。
## 发布与运行
发布逻辑必须保持:
* 发布时读取全部启用的 `proxy_routes`。
* 同时读取 OpenResty 主配置参数、反代性能参数与缓存参数。
* 生成完整 OpenResty 配置。
* 计算 `checksum`。
* 写入 `config_versions`。
* 通过切换 `is_active` 激活版本。
版本约束:
* 版本号格式固定为 `YYYYMMDD-NNN`。
* 不在线修改历史版本。
* 不做按节点分组的差异化版本。
* 预览与 diff 是只读能力,不产生发布记录。
Agent 必须满足:
* 启动后读取或生成本地 `node_id`。
* 周期性心跳与同步。
* 常规同步优先依据 heartbeat 返回的版本摘要判断。
* 发现新版本时先备份旧文件。
* 写入主配置、路由配置与必要证书文件。
* 写入新配置后以运行态恢复为目标执行激活,Docker 模式优先重建容器并确认容器保持运行。
* 新配置激活失败时必须先尝试用目标配置恢复运行,再回滚到旧配置并重新拉起 OpenResty。
* 回滚后 OpenResty 恢复正常时上报警告;回滚后仍无法恢复运行时上报失败。
* 某个目标 `version + checksum` 一旦应用失败并回退,Agent 必须在本地状态中阻断该目标的重复应用。
## 前端请求、状态与类型
所有 API 请求必须统一经过 `lib/api/`:
* 统一处理 `success/message/data` 响应结构。
* 统一处理鉴权失效、网络异常和通用错误消息。
* 统一维护资源接口与请求路径。
状态分层:
* 服务端状态:TanStack Query。
* 页面临时状态:组件内部 `useState`。
* 跨页面 UI 状态:Zustand。
要求开启 TypeScript 严格模式,禁止滥用 `any`,API 响应、表单输入、业务实体必须有明确类型。
## 表单、交互、样式与主题
表单统一使用 React Hook Form 与 Zod。
高风险操作必须二次确认、展示操作对象名称,并明确成功与失败反馈。
样式原则:
* 统一使用 Tailwind CSS 与现有 token 体系。
* 优先复用已有基础组件与布局组件。
* 保持视觉层级、留白与语义颜色一致。
主题要求:
* 同时支持 `light`、`dark`、`system`。
* 用户选择必须持久化。
* 首屏尽量避免主题闪烁。
## 测试与交付
* 关键业务逻辑必须有单元测试或等效回归测试。
* Agent 主链路修改必须验证同步、应用与回滚。
* 前端页面至少覆盖加载态、空态、错误态与成功反馈。
* Go 版本调整时,同步检查 `go.mod`、Dockerfile 与 CI 工作流。
## 后续维护方式
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
* 产品边界变动:更新 [产品边界](./)。
* 工程约束变动:更新本文档。
* 部署与配置变动:更新 [部署说明](../guide/deployment.md)、[配置项](../reference/configuration.md) 与 README。
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](./),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。