From 861d759f97e2b9fe4c9633b5a20a502cb2a5c32a Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 10 Mar 2026 15:13:28 +0800 Subject: [PATCH] feat: Update development guidelines and plan for ATSFlare V2 - Revised development guidelines to include new features for V2, such as Agent management and automatic discovery. - Expanded the data model to support agent tokens and discovery tokens. - Updated the development plan to reflect changes in Agent management, including CRUD operations and token replacement. - Enhanced the requirements for the second phase of development, focusing on security improvements and user experience. --- atsf_server/model/node.go | 90 +- docs/design.md | 1585 ++++++++++++++++---------------- docs/development-guidelines.md | 883 +++++++++--------- docs/development-plan.md | 36 +- 4 files changed, 1322 insertions(+), 1272 deletions(-) diff --git a/atsf_server/model/node.go b/atsf_server/model/node.go index 1c1399cf..207f8b72 100644 --- a/atsf_server/model/node.go +++ b/atsf_server/model/node.go @@ -1,29 +1,61 @@ -package model - -import "time" - -type Node struct { - ID uint `json:"id" gorm:"primaryKey"` - NodeID string `json:"node_id" gorm:"uniqueIndex;size:64;not null"` - Name string `json:"name" gorm:"size:128;not null"` - IP string `json:"ip" gorm:"size:64;not null"` - AgentVersion string `json:"agent_version" gorm:"size:64;not null"` - NginxVersion string `json:"nginx_version" gorm:"size:64"` - Status string `json:"status" gorm:"size:16;not null;default:'offline'"` - CurrentVersion string `json:"current_version" gorm:"size:32"` - LastSeenAt time.Time `json:"last_seen_at"` - LastError string `json:"last_error" gorm:"size:1024"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -func ListNodes() (nodes []*Node, err error) { - err = DB.Order("id desc").Find(&nodes).Error - return nodes, err -} - -func GetNodeByNodeID(nodeID string) (*Node, error) { - node := &Node{} - err := DB.Where("node_id = ?", nodeID).First(node).Error - return node, err -} +package model + +import "time" + +type Node struct { + ID uint `json:"id" gorm:"primaryKey"` + NodeID string `json:"node_id" gorm:"uniqueIndex;size:64;not null"` + Name string `json:"name" gorm:"size:128;not null"` + IP string `json:"ip" gorm:"size:64;not null"` + AgentToken string `json:"-" gorm:"size:128;uniqueIndex"` + DiscoveryToken string `json:"-" gorm:"size:128;uniqueIndex"` + AgentVersion string `json:"agent_version" gorm:"size:64;not null"` + NginxVersion string `json:"nginx_version" gorm:"size:64"` + Status string `json:"status" gorm:"size:16;not null;default:'offline'"` + CurrentVersion string `json:"current_version" gorm:"size:32"` + LastSeenAt time.Time `json:"last_seen_at"` + LastError string `json:"last_error" gorm:"size:1024"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func ListNodes() (nodes []*Node, err error) { + err = DB.Order("id desc").Find(&nodes).Error + return nodes, err +} + +func GetNodeByNodeID(nodeID string) (*Node, error) { + node := &Node{} + err := DB.Where("node_id = ?", nodeID).First(node).Error + return node, err +} + +func GetNodeByID(id uint) (*Node, error) { + node := &Node{} + err := DB.First(node, id).Error + return node, err +} + +func GetNodeByAgentToken(token string) (*Node, error) { + node := &Node{} + err := DB.Where("agent_token = ?", token).First(node).Error + return node, err +} + +func GetNodeByDiscoveryToken(token string) (*Node, error) { + node := &Node{} + err := DB.Where("discovery_token = ?", token).First(node).Error + return node, err +} + +func (node *Node) Insert() error { + return DB.Create(node).Error +} + +func (node *Node) Update() error { + return DB.Save(node).Error +} + +func (node *Node) Delete() error { + return DB.Delete(node).Error +} diff --git a/docs/design.md b/docs/design.md index d694387e..4b060bc1 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,790 +1,795 @@ -# ATSFlare MVP 设计文档 - -## 1. 目标 - -先做一个能用的版本,不做平台化过度设计。第一版只解决 3 件事: - -* 配置发布与同步 -* 节点心跳检测 -* Nginx 反向代理配置下发 - -系统定位是内部自用的控制面,不是面向外部租户的 CDN SaaS。 - ---- - -## 2. 第一版范围(已完成) - -### 已做 - -* Web 管理端维护反代规则 -* 配置发布生成版本 -* Agent 定时同步并应用配置 -* Agent 控制本机 Nginx 校验与 reload -* 节点注册、心跳、在线状态展示 -* 展示每个节点当前生效版本和最近一次应用结果 - -### 不做(第一版) - -* 多租户 -* WAF、限流、Bot、防刷 -* 灰度发布、节点分组、分批发布 -* 对象存储、消息队列、Redis、Prometheus -* 复杂缓存策略管理 -* 证书托管与自动签发 -* Purge、中台审计、审批流 -* mid-tier / 分层缓存 - -第一版默认所有节点消费同一份全量配置,不做差异化下发。 - ---- - -## 2.5 第二版范围 - -在 MVP 闭环稳定运行的基础上,第二版聚焦以下增量能力。 - -### 要做 - -**2.5.1 HTTPS/TLS 支持** - -* `proxy_routes` 增加 HTTPS 相关字段:`enable_https`、`cert_id`、`redirect_http` -* 渲染器根据字段生成 HTTPS `server` 块(443 端口),并可选生成 HTTP → HTTPS 重定向块 -* 控制面托管证书并下发到节点本地,支持手动导入与文件导入 - -**2.5.2 域名管理与证书托管** - -* 新增 `managed_domains` 表:管理业务域名,支持精确域名与通配符域名(如 `*.example.com`) -* 新增 `tls_certificates` 表:保存证书与私钥,支持手动粘贴导入和证书文件上传导入 -* 控制面新增证书管理与域名管理页面 -* 在反代规则编辑时,输入域名后自动匹配可用证书(包含通配符匹配) - -**2.5.3 Agent Token 管理** - -* 新增 `agent_tokens` 表:支持创建多个命名 Token,记录备注、创建人、过期时间 -* 认证中间件改为查表验证,不再依赖单个全局环境变量 -* 提供 Token CRUD 管理 API 及前端页面 -* 旧的全局 Token 环境变量作为引导 Token,仅在数据库无 Token 记录时生效(bootstrap 模式) - -**2.5.4 路由增强** - -* `proxy_routes` 增加 `custom_headers` 字段(JSON 格式),支持每条路由追加自定义 `proxy_set_header` 指令 -* 渲染器按 `custom_headers` 内容注入到对应 `server` 块 - -**2.5.5 配置预览与变更摘要** - -* 新增"配置预览"接口:在不实际发布的情况下,返回基于当前启用规则渲染的 Nginx 配置 -* 新增"变更摘要"接口:对比当前激活版本与新渲染结果,返回新增、删除、修改的域名列表 -* 前端发布页接入预览与变更摘要,让管理员在点击发布前确认变化 - -### 仍不做(第二版) - -* 多租户 -* WAF、限流、Bot、防刷 -* 节点分组与差异化下发 -* 对象存储、消息队列、Redis、Prometheus -* 证书自动签发(ACME) -* Purge、中台审计、审批流 -* mid-tier / 分层缓存 -* 复杂缓存策略配置 - ---- - -## 3. 技术约束 - -### Server - -控制中心直接基于现有 `atsf_server` 的 `gin-template` 工程开发: - -* Web 框架:Gin -* ORM:GORM -* 前端:沿用现有 web 管理端 -* 鉴权:沿用 gin-template 登录体系 - -### 数据库 - -只使用 SQLite,不引入其他中间件: - -* 不配置 `SQL_DSN`,直接走项目现有 SQLite 初始化逻辑 -* 不配置 `REDIS_CONN_STRING`,会退化为 cookie session - -### Agent - -Agent 使用 Go 单体程序: - -* 单二进制 -* systemd 管理 -* 优先调用独立 Nginx,而不是依赖系统全局 Nginx -* 显式配置 `nginx_path` 时,直接调用该路径下的 Nginx -* 未配置 `nginx_path` 时,默认通过 Docker 运行独立 Nginx 容器 -* 管理本机 Nginx 路由配置文件和 reload -* Agent 生成资源默认统一落在 `./data`,也允许通过单个基路径配置覆盖 -* Agent 启动时会校验本地路由文件哈希与控制面激活版本是否一致 -* Docker 模式启动时会重建独立 Nginx 容器,避免复用故障容器 - -### Nginx 管理边界 - -第一版只管理最核心的反代映射: - -* 重点生成独立的 Nginx 路由配置文件,例如 `/etc/nginx/conf.d/atsflare_routes.conf` -* `nginx.conf`、TLS 证书、缓存细节、upstream 高级配置先保持节点本地静态配置 -* Agent 可以管理独立安装路径下的 Nginx,或者独立 Docker Nginx 容器 - -也就是说,MVP 先把 Nginx 当成“可集中配置的反向代理”,不是完整网关平台。 - ---- - -## 4. 总体架构 - -```text - ┌────────────────────────────┐ - │ ATSFlare Server │ - │ gin-template + SQLite │ - │ Admin UI + Admin API │ - └──────────────┬─────────────┘ - │ - HTTP API / Config Pull - │ - ┌──────────────────┴──────────────────┐ - │ │ - ┌────────▼────────┐ ┌────────▼────────┐ - │ Nginx Agent 1 │ │ Nginx Agent N │ - │ heartbeat/sync │ │ heartbeat/sync │ - │ nginx reload │ │ nginx reload │ - └────────┬────────┘ └────────┬────────┘ - │ │ - ┌─────▼─────┐ ┌─────▼─────┐ - │ Nginx │ │ Nginx │ - │ reverse │ │ reverse │ - │ proxy │ │ proxy │ - └─────┬─────┘ └─────┬─────┘ - │ │ - └──────────────► Origin ◄────────────┘ -``` - -设计原则只有 3 条: - -* Server 只保存配置和节点状态,不直接 SSH 改机器 -* Agent 是唯一的落地入口 -* 所有发布都是“新版本生效”,不是在线修改当前文件 - ---- - -## 5. 核心对象 - -### 5.1 proxy_routes(第一版) - -反代规则表,控制 `Host -> Origin` 映射。 - -建议字段: - -* `id` -* `domain` -* `origin_url` -* `enabled` -* `remark` -* `created_at` -* `updated_at` - -约束: - -* `domain` 唯一 -* `origin_url` 必须是合法的 `http://` 或 `https://` -* 第一版一条域名只对应一个源站,不做源站池 - -第二版新增字段: - -* `enable_https` — 是否启用 HTTPS(bool,默认 false) -* `cert_id` — 关联托管证书 ID(nullable,未启用 HTTPS 时可为空) -* `redirect_http` — 是否将 HTTP 重定向到 HTTPS(bool,默认 false) -* `custom_headers` — 自定义 `proxy_set_header` 指令(JSON 格式,存字符串) - -### 5.2 config_versions(第一版) - -发布版本表,保存不可变快照。 - -建议字段: - -* `id` -* `version` -* `snapshot_json` -* `rendered_config` -* `checksum` -* `is_active` -* `created_by` -* `created_at` - -说明: - -* `snapshot_json` 保存发布时的完整规则快照 -* `rendered_config` 保存渲染后的 Nginx 路由配置 -* 第一版直接存 SQLite,不单独上对象存储 - -第二版沿用第一版字段,不新增分组字段。 - -### 5.3 nodes(第一版) - -节点表,保存当前状态。 - -建议字段: - -* `id` -* `node_id` -* `name` -* `ip` -* `agent_version` -* `nginx_version` -* `status` -* `current_version` -* `last_seen_at` -* `last_error` -* `created_at` -* `updated_at` - -第二版沿用第一版字段,不新增分组字段。 - -### 5.4 apply_logs(第一版) - -节点应用记录。 - -建议字段: - -* `id` -* `node_id` -* `version` -* `result` -* `message` -* `created_at` - -### 5.5 tls_certificates(第二版新增) - -证书托管表,用于保存证书与私钥内容。 - -建议字段: - -* `id` -* `name` — 证书名称(唯一) -* `cert_pem` — 证书 PEM 内容 -* `key_pem` — 私钥 PEM 内容 -* `not_before` — 证书生效时间 -* `not_after` — 证书过期时间 -* `remark` -* `created_at` -* `updated_at` - -### 5.6 managed_domains(第二版新增) - -域名管理表,用于维护可选域名及其默认证书关系。 - -建议字段: - -* `id` -* `domain` — 域名(支持精确域名和 `*.example.com`) -* `cert_id` — 关联 `tls_certificates.id`(nullable) -* `enabled` -* `remark` -* `created_at` -* `updated_at` - -### 5.7 agent_tokens(第二版新增) - -Agent Token 管理表,替代全局单一 Token。 - -建议字段: - -* `id` -* `token` — Token 值,唯一,不可变 -* `name` — Token 备注名称 -* `created_by` — 创建人 -* `expires_at` — 过期时间(nullable,null 表示永不过期) -* `is_active` — 是否有效 -* `created_at` -* `updated_at` - ---- - -## 6. 配置发布模型 - -第一版不做增量发布,也不做 bundle 文件仓库。 - -发布逻辑: - -1. 管理员在后台修改 `proxy_routes` -2. 点击“发布” -3. Server 校验规则 -4. Server 根据当前全部启用规则渲染出完整 Nginx 路由配置 -5. 生成新 `config_versions` 记录 -6. 将该版本标记为当前激活版本 -7. Agent 下一次心跳或轮询时发现新版本并拉取 - -### 版本原则 - -* 一个版本就是一份完整快照 -* 版本不可变 -* 节点只拉取当前激活版本 -* 回滚本质上是重新激活旧版本 - -### 版本号建议 - -```text -20260309-001 -20260309-002 -``` - -### 发布校验 - -发布前至少做以下检查: - -* `domain` 不能为空 -* `origin_url` 合法 -* 不允许重复域名 -* 至少存在 1 条启用规则 - ---- - -## 7. Nginx 配置策略 - -第一版只生成独立的 Nginx 路由配置文件,这样最简单,也最容易验证。 - -### 规则映射 - -```conf -server { - listen 80; - server_name www.example.com; - - location / { - proxy_pass http://10.0.0.10:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -server { - listen 80; - server_name api.example.com; - - location / { - proxy_pass http://10.0.0.20:9000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -### HTTPS 处理 - -第一版不在控制中心管理证书,第二版开始支持证书托管。 - -约定如下: - -* 第一版:Nginx 的监听端口、证书、TLS 相关配置由节点本地预先准备 -* 第二版:控制中心托管证书并在配置下发时生成对应证书文件与 HTTPS 配置引用 -* 第二版:反代规则可通过 `cert_id` 绑定证书,并支持 HTTP → HTTPS 重定向 - -### 缓存处理 - -第一版不开放缓存策略配置: - -* 是否开启缓存由节点静态配置决定 -* 控制中心不管理 TTL、Header 改写、缓存规则 - ---- - -## 8. Server 模块设计 - -控制中心仍然是单体应用,不拆服务。 - -### 8.1 管理端模块 - -* 登录鉴权 -* 反代规则 CRUD -* 发布版本管理 -* 节点状态页面 -* 应用日志查看 - -### 8.2 Agent API 模块 - -* 节点注册 -* 心跳上报 -* 获取当前激活版本 -* 下载指定版本配置 -* 上报应用结果 - -### 8.3 渲染模块 - -职责很简单: - -* 从 `proxy_routes` 读取全部启用规则 -* 按固定模板拼出 Nginx 路由配置 -* 计算 checksum -* 写入 `config_versions` - -这层不要引入复杂 DSL,第一版直接围绕 `domain -> origin_url` 即可。 - ---- - -## 9. Agent 模块设计 - -Agent 做成一个 Go 单体进程即可。 - -### 9.1 本地职责 - -* 读取本地配置 -* 定时心跳 -* 拉取新版本 -* 覆盖 Nginx 路由配置文件 -* 执行 `nginx -t` 和 `nginx -s reload` -* 上报应用结果 -* 保存本地最近成功版本 - -### 9.2 建议的本地文件 - -* `/etc/atsf-agent/config.yaml` -* `/var/lib/atsf-agent/state.json` -* `/etc/nginx/conf.d/atsflare_routes.conf` -* `/etc/nginx/conf.d/atsflare_routes.conf.bak` - -### 9.3 最小工作流 - -```text -1. Agent 启动 -2. 读取或生成 node_id -3. 上报 heartbeat -4. 获取当前激活版本元数据 -5. 若版本变更,则下载 rendered_config -6. 备份旧路由配置文件 -7. 写入新路由配置文件 -8. 调用 `nginx -t` -9. 校验通过后执行 `nginx -s reload` -10. 记录结果并上报 -11. 进入下一轮 -``` - -### 9.4 失败处理 - -第一版只做最基本的容错: - -* 拉取失败:继续使用本地旧配置 -* 配置校验或 reload 失败:恢复备份文件并再次校验后 reload -* Server 不可用:不影响 Nginx 继续转发 - ---- - -## 10. 心跳与在线状态 - -心跳不单独搞复杂监控系统,直接走业务表。 - -### 心跳内容 - -Agent 每次上报: - -* `node_id` -* `name` -* `ip` -* `agent_version` -* `nginx_version` -* `current_version` -* `last_apply_result` -* `timestamp` - -### 状态判定 - -建议规则: - -* 15 秒一次心跳 -* 超过 45 秒未上报记为 `offline` -* 最近一次应用失败但仍有心跳,记为 `warning` -* 正常心跳且版本一致,记为 `online` - ---- - -## 11. API 设计 - -### 11.1 管理端 API(第一版,已实现) - -* `GET /api/proxy-routes/` -* `POST /api/proxy-routes/` -* `PUT /api/proxy-routes/:id` -* `DELETE /api/proxy-routes/:id` -* `GET /api/config-versions/` -* `GET /api/config-versions/active` -* `POST /api/config-versions/publish` -* `PUT /api/config-versions/:id/activate` -* `GET /api/nodes/` -* `GET /api/apply-logs/` - -### 11.2 Agent API(第一版,已实现) - -* `POST /api/agent/nodes/register` -* `POST /api/agent/nodes/heartbeat` -* `GET /api/agent/config-versions/active` -* `POST /api/agent/apply-logs` - -### 11.3 第二版新增管理端 API - -* `GET /api/tls-certificates/` — 证书列表 -* `POST /api/tls-certificates/` — 手动导入证书(粘贴 PEM) -* `POST /api/tls-certificates/import-file` — 证书文件导入 -* `PUT /api/tls-certificates/:id` — 更新证书备注/状态 -* `DELETE /api/tls-certificates/:id` — 删除证书 -* `GET /api/managed-domains/` — 域名列表 -* `POST /api/managed-domains/` — 创建域名并可绑定默认证书 -* `PUT /api/managed-domains/:id` — 更新域名配置 -* `DELETE /api/managed-domains/:id` — 删除域名 -* `GET /api/tls-certificates/match?domain=` — 按输入域名返回匹配证书(支持 `*.example.com`) -* `GET /api/agent-tokens/` — Token 列表 -* `POST /api/agent-tokens/` — 创建 Token -* `DELETE /api/agent-tokens/:id` — 撤销 Token -* `GET /api/config-versions/preview` — 预览当前启用规则的渲染结果(不写库) -* `GET /api/config-versions/diff` — 对比当前激活版本与待发布的变更摘要 - -### 11.4 鉴权方案 - -管理端: - -* 直接沿用 gin-template 的登录态 - -Agent(第一版): - -* 预共享 Token,请求头 `X-Agent-Token`,Token 值来自环境变量 - -Agent(第二版): - -* Token 改为查 `agent_tokens` 表验证 -* 环境变量 Token 仅作 bootstrap 引导 Token,数据库有记录时不再使用 -* 后续可升级 mTLS - ---- - -## 12. 页面设计 - -### 12.1 登录页 - -沿用 gin-template 现有登录。 - -### 12.2 反代规则页(第一版,已实现) - -展示和编辑: - -* 域名 -* 源站地址 -* 是否启用 -* 备注 - -第二版新增字段: - -* 是否启用 HTTPS -* 证书选择(自动匹配候选证书,支持通配符) -* 是否 HTTP → HTTPS 重定向 -* 自定义请求头(JSON 编辑器) - -### 12.3 发布版本页(第一版,已实现) - -展示: - -* 版本号 -* 发布时间 -* 发布人 -* 是否当前激活 - -动作: - -* 立即发布 -* 激活旧版本 - -第二版新增: - -* 发布前展示配置预览与变更摘要 - -### 12.4 节点页(第一版,已实现) - -展示: - -* 节点名 -* IP -* 在线状态 -* 当前版本 -* 最后心跳时间 -* 最近错误 - -### 12.5 应用记录页(第一版,已实现) - -展示: - -* 节点 -* 版本 -* 成功/失败 -* 错误信息 -* 时间 - -### 12.6 Token 管理页(第二版新增) - -展示: - -* Token 名称 -* 创建人 -* 过期时间 -* 是否有效 - -动作: - -* 创建 Token -* 撤销 Token - -### 12.7 证书管理页(第二版新增) - -展示: - -* 证书名称 -* 有效期(起止时间) -* 绑定域名数量 -* 备注 - -动作: - -* 手动导入证书(粘贴 PEM) -* 文件导入证书 -* 删除证书 - -### 12.8 域名管理页(第二版新增) - -展示: - -* 域名(支持 `*.example.com`) -* 绑定证书 -* 是否启用 -* 备注 - -动作: - -* 创建域名 -* 绑定/更换证书 -* 删除域名 - ---- - -## 13. 代码组织建议 - -### Server(第一版,已实现) - -```text -atsf_server/ - controller/ - proxy_route.go - config_version.go - node.go - agent.go - model/ - proxy_route.go - config_version.go - node.go - apply_log.go - router/ - api-router.go - service/ - proxy_route.go - config_version.go - agent.go -``` - -### Server(第二版新增) - -```text -atsf_server/ - controller/ - tls_certificate.go # 证书管理 - managed_domain.go # 域名管理 - agent_token.go # Token 管理 - model/ - tls_certificate.go # TLSCertificate 模型 - managed_domain.go # ManagedDomain 模型 - agent_token.go # AgentToken 模型 - service/ - tls_certificate.go # 证书导入与匹配逻辑 - managed_domain.go # 域名管理逻辑 - agent_token.go # Token 创建与验证 - renderer.go # 抽离渲染逻辑(HTTPS 支持扩展) - middleware/ - agent-auth.go # 改为查表验证 -``` - -### Agent(第一版,已实现) - -```text -atsf_agent/ - cmd/agent/main.go - internal/config/config.go - internal/heartbeat/service.go - internal/sync/service.go - internal/nginx/manager.go - internal/state/state.go - internal/httpclient/client.go - internal/protocol/agent_api.go -``` - -### Agent(第二版) - -第二版 Agent 无需新增模块,只需在现有模块内扩展: - -* `sync`: 拉取包含 HTTPS 与证书引用的渲染配置并应用 -* `nginx`: 写入控制面托管证书生成的本地文件并参与 `nginx -t` / reload - ---- - -## 14. 开发顺序 - -### 第一版(已完成) - -1. Server 建表、AutoMigrate -2. 反代规则 CRUD 与发布逻辑 -3. Agent API 与节点状态表 -4. Agent 同步、落盘、reload、回滚 -5. 管理端页面 -6. 联调和部署文档 - -### 第二版(当前阶段) - -按以下顺序执行,前项完成后再推进下一项: - -1. HTTPS/TLS 支持(ProxyRoute 扩展字段 + 渲染器 + 前端表单) -2. 域名管理与证书托管(managed_domains/tls_certificates + 证书导入 + 自动匹配) -3. Agent Token 管理(agent_tokens 表 + 中间件改造 + 前端 Token 管理页) -4. 路由增强(custom_headers 字段 + 渲染器注入 + 前端表单) -5. 配置预览与变更摘要(preview 接口 + diff 接口 + 前端发布确认弹窗) - ---- - -## 15. 关键取舍 - -第一版故意做这些取舍: - -* 不抽象 zone、origin pool、policy 这些平台概念 -* 不做复杂发布编排,所有节点统一拉当前版本 -* 不管理 Nginx 全部配置,只先管独立生成的路由配置文件 -* 不引入 Redis、MQ、对象存储,先把单机 SQLite 跑起来 -* 不为了“以后可能会用到”提前把系统拆复杂 - -只要这版能稳定完成下面这条链路,就算成功: - -```text -后台改规则 -> 点击发布 -> Agent 拉到新版本 -> Nginx reload -> 节点状态可见 -``` - -这就是当前阶段最需要的 MVP。 - -### 第二版取舍 - -* HTTPS 支持由控制面托管证书,但只支持导入,不做自动签发与自动续期 -* 第二版不做节点分组,所有节点继续消费同一份激活版本 -* Token 管理不做细粒度权限(如只读 Token),第二版所有 Token 权限一致 -* 路由自定义头不做模板变量,只支持静态 key-value,避免过早引入 DSL -* 配置预览只展示渲染结果,不实际验证 Nginx 语法,真实校验仍由 Agent 完成 - -第二版成功标准: - -```text -HTTPS 路由可生效 + 控制面可托管证书并按域名自动匹配(含通配符)+ Token 可在界面管理 + 发布前可预览变更 -``` +# ATSFlare MVP 设计文档 + +## 1. 目标 + +先做一个能用的版本,不做平台化过度设计。第一版只解决 3 件事: + +* 配置发布与同步 +* 节点心跳检测 +* Nginx 反向代理配置下发 + +系统定位是内部自用的控制面,不是面向外部租户的 CDN SaaS。 + +--- + +## 2. 第一版范围(已完成) + +### 已做 + +* Web 管理端维护反代规则 +* 配置发布生成版本 +* Agent 定时同步并应用配置 +* Agent 控制本机 Nginx 校验与 reload +* 节点注册、心跳、在线状态展示 +* 展示每个节点当前生效版本和最近一次应用结果 + +### 不做(第一版) + +* 多租户 +* WAF、限流、Bot、防刷 +* 灰度发布、节点分组、分批发布 +* 对象存储、消息队列、Redis、Prometheus +* 复杂缓存策略管理 +* 证书托管与自动签发 +* Purge、中台审计、审批流 +* mid-tier / 分层缓存 + +第一版默认所有节点消费同一份全量配置,不做差异化下发。 + +--- + +## 2.5 第二版范围 + +在 MVP 闭环稳定运行的基础上,第二版聚焦以下增量能力。 + +### 要做 + +**2.5.1 HTTPS/TLS 支持** + +* `proxy_routes` 增加 HTTPS 相关字段:`enable_https`、`cert_id`、`redirect_http` +* 渲染器根据字段生成 HTTPS `server` 块(443 端口),并可选生成 HTTP → HTTPS 重定向块 +* 控制面托管证书并下发到节点本地,支持手动导入与文件导入 + +**2.5.2 域名管理与证书托管** + +* 新增 `managed_domains` 表:管理业务域名,支持精确域名与通配符域名(如 `*.example.com`) +* 新增 `tls_certificates` 表:保存证书与私钥,支持手动粘贴导入和证书文件上传导入 +* 控制面新增证书管理与域名管理页面 +* 在反代规则编辑时,输入域名后自动匹配可用证书(包含通配符匹配) + +**2.5.3 Agent 管理与自动发现** + +* 管理端支持手工创建节点、编辑节点名、删除节点 +* `nodes` 表增加 Agent 鉴权 Token 与自动发现 Token 字段 +* 首次接入不再依赖全局环境变量 Token,而是依赖管理端为节点生成的自动发现 Token +* Agent 首次注册成功后,Server 下发节点专属 Agent Token,Agent 本地完成 Token 置换 +* Agent 默认自动探测主机名与 IP,也允许通过配置覆盖 + +**2.5.4 路由增强** + +* `proxy_routes` 增加 `custom_headers` 字段(JSON 格式),支持每条路由追加自定义 `proxy_set_header` 指令 +* 渲染器按 `custom_headers` 内容注入到对应 `server` 块 + +**2.5.5 配置预览与变更摘要** + +* 新增"配置预览"接口:在不实际发布的情况下,返回基于当前启用规则渲染的 Nginx 配置 +* 新增"变更摘要"接口:对比当前激活版本与新渲染结果,返回新增、删除、修改的域名列表 +* 前端发布页接入预览与变更摘要,让管理员在点击发布前确认变化 + +### 仍不做(第二版) + +* 多租户 +* WAF、限流、Bot、防刷 +* 节点分组与差异化下发 +* 对象存储、消息队列、Redis、Prometheus +* 证书自动签发(ACME) +* Purge、中台审计、审批流 +* mid-tier / 分层缓存 +* 复杂缓存策略配置 + +--- + +## 3. 技术约束 + +### Server + +控制中心直接基于现有 `atsf_server` 的 `gin-template` 工程开发: + +* Web 框架:Gin +* ORM:GORM +* 前端:沿用现有 web 管理端 +* 鉴权:沿用 gin-template 登录体系 + +### 数据库 + +只使用 SQLite,不引入其他中间件: + +* 不配置 `SQL_DSN`,直接走项目现有 SQLite 初始化逻辑 +* 不配置 `REDIS_CONN_STRING`,会退化为 cookie session + +### Agent + +Agent 使用 Go 单体程序: + +* 单二进制 +* systemd 管理 +* 优先调用独立 Nginx,而不是依赖系统全局 Nginx +* 显式配置 `nginx_path` 时,直接调用该路径下的 Nginx +* 未配置 `nginx_path` 时,默认通过 Docker 运行独立 Nginx 容器 +* 管理本机 Nginx 路由配置文件和 reload +* Agent 生成资源默认统一落在 `./data`,也允许通过单个基路径配置覆盖 +* Agent 启动时会校验本地路由文件哈希与控制面激活版本是否一致 +* Docker 模式启动时会重建独立 Nginx 容器,避免复用故障容器 + +### Nginx 管理边界 + +第一版只管理最核心的反代映射: + +* 重点生成独立的 Nginx 路由配置文件,例如 `/etc/nginx/conf.d/atsflare_routes.conf` +* `nginx.conf`、TLS 证书、缓存细节、upstream 高级配置先保持节点本地静态配置 +* Agent 可以管理独立安装路径下的 Nginx,或者独立 Docker Nginx 容器 + +也就是说,MVP 先把 Nginx 当成“可集中配置的反向代理”,不是完整网关平台。 + +--- + +## 4. 总体架构 + +```text + ┌────────────────────────────┐ + │ ATSFlare Server │ + │ gin-template + SQLite │ + │ Admin UI + Admin API │ + └──────────────┬─────────────┘ + │ + HTTP API / Config Pull + │ + ┌──────────────────┴──────────────────┐ + │ │ + ┌────────▼────────┐ ┌────────▼────────┐ + │ Nginx Agent 1 │ │ Nginx Agent N │ + │ heartbeat/sync │ │ heartbeat/sync │ + │ nginx reload │ │ nginx reload │ + └────────┬────────┘ └────────┬────────┘ + │ │ + ┌─────▼─────┐ ┌─────▼─────┐ + │ Nginx │ │ Nginx │ + │ reverse │ │ reverse │ + │ proxy │ │ proxy │ + └─────┬─────┘ └─────┬─────┘ + │ │ + └──────────────► Origin ◄────────────┘ +``` + +设计原则只有 3 条: + +* Server 只保存配置和节点状态,不直接 SSH 改机器 +* Agent 是唯一的落地入口 +* 所有发布都是“新版本生效”,不是在线修改当前文件 + +--- + +## 5. 核心对象 + +### 5.1 proxy_routes(第一版) + +反代规则表,控制 `Host -> Origin` 映射。 + +建议字段: + +* `id` +* `domain` +* `origin_url` +* `enabled` +* `remark` +* `created_at` +* `updated_at` + +约束: + +* `domain` 唯一 +* `origin_url` 必须是合法的 `http://` 或 `https://` +* 第一版一条域名只对应一个源站,不做源站池 + +第二版新增字段: + +* `enable_https` — 是否启用 HTTPS(bool,默认 false) +* `cert_id` — 关联托管证书 ID(nullable,未启用 HTTPS 时可为空) +* `redirect_http` — 是否将 HTTP 重定向到 HTTPS(bool,默认 false) +* `custom_headers` — 自定义 `proxy_set_header` 指令(JSON 格式,存字符串) + +### 5.2 config_versions(第一版) + +发布版本表,保存不可变快照。 + +建议字段: + +* `id` +* `version` +* `snapshot_json` +* `rendered_config` +* `checksum` +* `is_active` +* `created_by` +* `created_at` + +说明: + +* `snapshot_json` 保存发布时的完整规则快照 +* `rendered_config` 保存渲染后的 Nginx 路由配置 +* 第一版直接存 SQLite,不单独上对象存储 + +第二版沿用第一版字段,不新增分组字段。 + +### 5.3 nodes(第一版) + +节点表,保存当前状态。 + +建议字段: + +* `id` +* `node_id` +* `name` +* `ip` +* `agent_version` +* `nginx_version` +* `status` +* `current_version` +* `last_seen_at` +* `last_error` +* `created_at` +* `updated_at` + +第二版沿用第一版字段,不新增分组字段。 + +### 5.4 apply_logs(第一版) + +节点应用记录。 + +建议字段: + +* `id` +* `node_id` +* `version` +* `result` +* `message` +* `created_at` + +### 5.5 tls_certificates(第二版新增) + +证书托管表,用于保存证书与私钥内容。 + +建议字段: + +* `id` +* `name` — 证书名称(唯一) +* `cert_pem` — 证书 PEM 内容 +* `key_pem` — 私钥 PEM 内容 +* `not_before` — 证书生效时间 +* `not_after` — 证书过期时间 +* `remark` +* `created_at` +* `updated_at` + +### 5.6 managed_domains(第二版新增) + +域名管理表,用于维护可选域名及其默认证书关系。 + +建议字段: + +* `id` +* `domain` — 域名(支持精确域名和 `*.example.com`) +* `cert_id` — 关联 `tls_certificates.id`(nullable) +* `enabled` +* `remark` +* `created_at` +* `updated_at` + +### 5.7 nodes(第二版扩展) + +节点表在第二版增加节点管理与自动发现字段。 + +新增字段建议: + +* `agent_token` — 节点专属 Agent Token,用于注册完成后的正式鉴权 +* `discovery_token` — 自动发现 Token,仅用于首次接入 + +约束: + +* `agent_token` 与 `discovery_token` 都应为随机生成值 +* `discovery_token` 仅用于首次接入,注册成功后应失效或清空 +* 删除节点后,该节点关联的 Token 必须立即失效 + +--- + +## 6. 配置发布模型 + +第一版不做增量发布,也不做 bundle 文件仓库。 + +发布逻辑: + +1. 管理员在后台修改 `proxy_routes` +2. 点击“发布” +3. Server 校验规则 +4. Server 根据当前全部启用规则渲染出完整 Nginx 路由配置 +5. 生成新 `config_versions` 记录 +6. 将该版本标记为当前激活版本 +7. Agent 下一次心跳或轮询时发现新版本并拉取 + +### 版本原则 + +* 一个版本就是一份完整快照 +* 版本不可变 +* 节点只拉取当前激活版本 +* 回滚本质上是重新激活旧版本 + +### 版本号建议 + +```text +20260309-001 +20260309-002 +``` + +### 发布校验 + +发布前至少做以下检查: + +* `domain` 不能为空 +* `origin_url` 合法 +* 不允许重复域名 +* 至少存在 1 条启用规则 + +--- + +## 7. Nginx 配置策略 + +第一版只生成独立的 Nginx 路由配置文件,这样最简单,也最容易验证。 + +### 规则映射 + +```conf +server { + listen 80; + server_name www.example.com; + + location / { + proxy_pass http://10.0.0.10:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + listen 80; + server_name api.example.com; + + location / { + proxy_pass http://10.0.0.20:9000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +### HTTPS 处理 + +第一版不在控制中心管理证书,第二版开始支持证书托管。 + +约定如下: + +* 第一版:Nginx 的监听端口、证书、TLS 相关配置由节点本地预先准备 +* 第二版:控制中心托管证书并在配置下发时生成对应证书文件与 HTTPS 配置引用 +* 第二版:反代规则可通过 `cert_id` 绑定证书,并支持 HTTP → HTTPS 重定向 + +### 缓存处理 + +第一版不开放缓存策略配置: + +* 是否开启缓存由节点静态配置决定 +* 控制中心不管理 TTL、Header 改写、缓存规则 + +--- + +## 8. Server 模块设计 + +控制中心仍然是单体应用,不拆服务。 + +### 8.1 管理端模块 + +* 登录鉴权 +* 反代规则 CRUD +* 发布版本管理 +* 节点状态页面 +* 应用日志查看 + +### 8.2 Agent API 模块 + +* 节点注册 +* 心跳上报 +* 获取当前激活版本 +* 下载指定版本配置 +* 上报应用结果 + +### 8.3 渲染模块 + +职责很简单: + +* 从 `proxy_routes` 读取全部启用规则 +* 按固定模板拼出 Nginx 路由配置 +* 计算 checksum +* 写入 `config_versions` + +这层不要引入复杂 DSL,第一版直接围绕 `domain -> origin_url` 即可。 + +--- + +## 9. Agent 模块设计 + +Agent 做成一个 Go 单体进程即可。 + +### 9.1 本地职责 + +* 读取本地配置 +* 定时心跳 +* 拉取新版本 +* 覆盖 Nginx 路由配置文件 +* 执行 `nginx -t` 和 `nginx -s reload` +* 上报应用结果 +* 保存本地最近成功版本 + +### 9.2 建议的本地文件 + +* `/etc/atsf-agent/config.yaml` +* `/var/lib/atsf-agent/state.json` +* `/etc/nginx/conf.d/atsflare_routes.conf` +* `/etc/nginx/conf.d/atsflare_routes.conf.bak` + +### 9.3 最小工作流 + +```text +1. Agent 启动 +2. 读取或生成 node_id +3. 上报 heartbeat +4. 获取当前激活版本元数据 +5. 若版本变更,则下载 rendered_config +6. 备份旧路由配置文件 +7. 写入新路由配置文件 +8. 调用 `nginx -t` +9. 校验通过后执行 `nginx -s reload` +10. 记录结果并上报 +11. 进入下一轮 +``` + +### 9.4 失败处理 + +第一版只做最基本的容错: + +* 拉取失败:继续使用本地旧配置 +* 配置校验或 reload 失败:恢复备份文件并再次校验后 reload +* Server 不可用:不影响 Nginx 继续转发 + +--- + +## 10. 心跳与在线状态 + +心跳不单独搞复杂监控系统,直接走业务表。 + +### 心跳内容 + +Agent 每次上报: + +* `node_id` +* `name` +* `ip` +* `agent_version` +* `nginx_version` +* `current_version` +* `last_apply_result` +* `timestamp` + +### 状态判定 + +建议规则: + +* 15 秒一次心跳 +* 超过 45 秒未上报记为 `offline` +* 最近一次应用失败但仍有心跳,记为 `warning` +* 正常心跳且版本一致,记为 `online` + +--- + +## 11. API 设计 + +### 11.1 管理端 API(第一版,已实现) + +* `GET /api/proxy-routes/` +* `POST /api/proxy-routes/` +* `PUT /api/proxy-routes/:id` +* `DELETE /api/proxy-routes/:id` +* `GET /api/config-versions/` +* `GET /api/config-versions/active` +* `POST /api/config-versions/publish` +* `PUT /api/config-versions/:id/activate` +* `GET /api/nodes/` +* `GET /api/apply-logs/` + +### 11.2 Agent API(第一版,已实现) + +* `POST /api/agent/nodes/register` +* `POST /api/agent/nodes/heartbeat` +* `GET /api/agent/config-versions/active` +* `POST /api/agent/apply-logs` + +### 11.3 第二版新增管理端 API + +* `GET /api/tls-certificates/` — 证书列表 +* `POST /api/tls-certificates/` — 手动导入证书(粘贴 PEM) +* `POST /api/tls-certificates/import-file` — 证书文件导入 +* `PUT /api/tls-certificates/:id` — 更新证书备注/状态 +* `DELETE /api/tls-certificates/:id` — 删除证书 +* `GET /api/managed-domains/` — 域名列表 +* `POST /api/managed-domains/` — 创建域名并可绑定默认证书 +* `PUT /api/managed-domains/:id` — 更新域名配置 +* `DELETE /api/managed-domains/:id` — 删除域名 +* `GET /api/tls-certificates/match?domain=` — 按输入域名返回匹配证书(支持 `*.example.com`) +* `GET /api/agent-tokens/` — Token 列表 +* `POST /api/agent-tokens/` — 创建 Token +* `DELETE /api/agent-tokens/:id` — 撤销 Token +* `GET /api/config-versions/preview` — 预览当前启用规则的渲染结果(不写库) +* `GET /api/config-versions/diff` — 对比当前激活版本与待发布的变更摘要 + +### 11.4 鉴权方案 + +管理端: + +* 直接沿用 gin-template 的登录态 + +Agent(第一版): + +* 预共享 Token,请求头 `X-Agent-Token`,Token 值来自环境变量 + +Agent(第二版): + +* Agent 正式鉴权改为查 `nodes.agent_token` +* 首次注册使用 `nodes.discovery_token` +* 不再依赖全局环境变量 Agent Token +* 后续可升级 mTLS + +--- + +## 12. 页面设计 + +### 12.1 登录页 + +沿用 gin-template 现有登录。 + +### 12.2 反代规则页(第一版,已实现) + +展示和编辑: + +* 域名 +* 源站地址 +* 是否启用 +* 备注 + +第二版新增字段: + +* 是否启用 HTTPS +* 证书选择(自动匹配候选证书,支持通配符) +* 是否 HTTP → HTTPS 重定向 +* 自定义请求头(JSON 编辑器) + +### 12.3 发布版本页(第一版,已实现) + +展示: + +* 版本号 +* 发布时间 +* 发布人 +* 是否当前激活 + +动作: + +* 立即发布 +* 激活旧版本 + +第二版新增: + +* 发布前展示配置预览与变更摘要 + +### 12.4 节点页(第一版,已实现) + +展示: + +* 节点名 +* IP +* 在线状态 +* 当前版本 +* 最后心跳时间 +* 最近错误 + +### 12.5 应用记录页(第一版,已实现) + +展示: + +* 节点 +* 版本 +* 成功/失败 +* 错误信息 +* 时间 + +### 12.6 节点管理页(第二版增强) + +展示: + +* 节点名 +* Node ID +* 自动发现 Token(仅待接入节点展示) +* 在线状态 +* 当前版本 +* 最后心跳时间 +* 最近错误 + +动作: + +* 创建节点 +* 编辑节点名 +* 删除节点 + +### 12.7 证书管理页(第二版新增) + +展示: + +* 证书名称 +* 有效期(起止时间) +* 绑定域名数量 +* 备注 + +动作: + +* 手动导入证书(粘贴 PEM) +* 文件导入证书 +* 删除证书 + +### 12.8 域名管理页(第二版新增) + +展示: + +* 域名(支持 `*.example.com`) +* 绑定证书 +* 是否启用 +* 备注 + +动作: + +* 创建域名 +* 绑定/更换证书 +* 删除域名 + +--- + +## 13. 代码组织建议 + +### Server(第一版,已实现) + +```text +atsf_server/ + controller/ + proxy_route.go + config_version.go + node.go + agent.go + model/ + proxy_route.go + config_version.go + node.go + apply_log.go + router/ + api-router.go + service/ + proxy_route.go + config_version.go + agent.go +``` + +### Server(第二版新增) + +```text +atsf_server/ + controller/ + tls_certificate.go # 证书管理 + managed_domain.go # 域名管理 + node.go # 节点管理 + model/ + tls_certificate.go # TLSCertificate 模型 + managed_domain.go # ManagedDomain 模型 + service/ + tls_certificate.go # 证书导入与匹配逻辑 + managed_domain.go # 域名管理逻辑 + node.go # 节点管理与自动发现逻辑 + renderer.go # 抽离渲染逻辑(HTTPS 支持扩展) + middleware/ + agent-auth.go # 改为查节点专属 Token 验证 +``` + +### Agent(第一版,已实现) + +```text +atsf_agent/ + cmd/agent/main.go + internal/config/config.go + internal/heartbeat/service.go + internal/sync/service.go + internal/nginx/manager.go + internal/state/state.go + internal/httpclient/client.go + internal/protocol/agent_api.go +``` + +### Agent(第二版) + +第二版 Agent 无需新增模块,只需在现有模块内扩展: + +* `sync`: 拉取包含 HTTPS 与证书引用的渲染配置并应用 +* `nginx`: 写入控制面托管证书生成的本地文件并参与 `nginx -t` / reload + +--- + +## 14. 开发顺序 + +### 第一版(已完成) + +1. Server 建表、AutoMigrate +2. 反代规则 CRUD 与发布逻辑 +3. Agent API 与节点状态表 +4. Agent 同步、落盘、reload、回滚 +5. 管理端页面 +6. 联调和部署文档 + +### 第二版(当前阶段) + +按以下顺序执行,前项完成后再推进下一项: + +1. HTTPS/TLS 支持(ProxyRoute 扩展字段 + 渲染器 + 前端表单) +2. 域名管理与证书托管(managed_domains/tls_certificates + 证书导入 + 自动匹配) +3. Agent 管理(节点 CRUD + discovery token + 节点专属 agent token) +4. 路由增强(custom_headers 字段 + 渲染器注入 + 前端表单) +5. 配置预览与变更摘要(preview 接口 + diff 接口 + 前端发布确认弹窗) + +--- + +## 15. 关键取舍 + +第一版故意做这些取舍: + +* 不抽象 zone、origin pool、policy 这些平台概念 +* 不做复杂发布编排,所有节点统一拉当前版本 +* 不管理 Nginx 全部配置,只先管独立生成的路由配置文件 +* 不引入 Redis、MQ、对象存储,先把单机 SQLite 跑起来 +* 不为了“以后可能会用到”提前把系统拆复杂 + +只要这版能稳定完成下面这条链路,就算成功: + +```text +后台改规则 -> 点击发布 -> Agent 拉到新版本 -> Nginx reload -> 节点状态可见 +``` + +这就是当前阶段最需要的 MVP。 + +### 第二版取舍 + +* HTTPS 支持由控制面托管证书,但只支持导入,不做自动签发与自动续期 +* 第二版不做节点分组,所有节点继续消费同一份激活版本 +* 节点专属 Token 不做额外权限分级,第二版仅区分 discovery token 与 agent token 两种用途 +* 路由自定义头不做模板变量,只支持静态 key-value,避免过早引入 DSL +* 配置预览只展示渲染结果,不实际验证 Nginx 语法,真实校验仍由 Agent 完成 + +第二版成功标准: + +```text +HTTPS 路由可生效 + 控制面可托管证书并按域名自动匹配(含通配符)+ 节点可通过 discovery token 自动接入并完成 token 置换 + 发布前可预览变更 +``` diff --git a/docs/development-guidelines.md b/docs/development-guidelines.md index 71548d5c..28270ff1 100644 --- a/docs/development-guidelines.md +++ b/docs/development-guidelines.md @@ -1,437 +1,446 @@ -# ATSFlare 开发规范 - -## 1. 适用范围 - -本规范适用于 ATSFlare 第一版与第二版阶段。 - -项目第一版已完成以下能力: - -* 配置发布与同步 -* 节点心跳检测 -* Nginx 反向代理配置下发 - -第二版在此基础上新增以下能力: - -* HTTPS/TLS 路由支持 -* 证书托管(手动导入与文件导入) -* 域名管理与证书自动匹配(支持 `*.example.com`) -* Agent Token 管理 -* 路由自定义请求头 -* 配置预览与变更摘要 - -当前明确仍不做: - -* 多租户 -* WAF、限流、Bot、防刷 -* 百分比灰度发布 -* 节点分组与差异化下发 -* Redis、MQ、对象存储、Prometheus -* 复杂缓存策略、证书自动签发、Purge、审批流 -* mid-tier、分层缓存、复杂策略编排 - -超出以上范围的需求,必须先更新设计文档,再开始编码。 - -## 2. 技术基线 - -### 2.1 Server - -控制中心基于现有 `atsf_server` 开发: - -* Web 框架:Gin -* ORM:GORM -* 数据库:SQLite -* 前端:现有 `atsf_server/web` -* 登录体系:沿用 gin-template 现有能力 - -约束: - -* 默认不配置 `SQL_DSN` -* 默认不配置 `REDIS_CONN_STRING` -* 不为了 MVP 引入新的基础设施依赖 - -### 2.2 Agent - -Agent 放在 `atsf_agent`,使用 Go 单体程序开发。 - -约束: - -* 单二进制 -* systemd 运行 -* 优先调用独立 Nginx,不依赖系统全局 Nginx -* 支持通过 `nginx_path` 显式指定独立 Nginx 可执行文件 -* 未指定 `nginx_path` 时,默认通过 Docker 启动独立 Nginx 容器 -* Agent 生成资源默认统一放在 `./data`,可通过 `data_dir` 统一覆盖 -* 负责本机 Nginx 路由配置写入、校验、reload、状态上报 - -### 2.3 Nginx 配置边界 - -第一版控制面只管理独立生成的 Nginx 路由配置文件,例如 `/etc/nginx/conf.d/atsflare_routes.conf`。第二版在此基础上增加证书托管能力。 - -第一版以下内容不纳入控制面: - -* `nginx.conf` -* 缓存策略 -* upstream 高级配置 - -第二版约束: - -* 允许在控制面托管 TLS 证书并下发给节点 -* 仅支持证书导入(手动粘贴与文件导入),不做自动签发/续期 -* `nginx.conf`、缓存策略、upstream 高级配置仍保持节点本地静态配置 - -## 3. 仓库职责划分 - -### 3.1 `atsf_server` - -负责: - -* 管理端 UI -* 管理端 API -* Agent API -* 数据存储 -* 配置渲染 -* 版本发布 -* 节点状态展示 - -### 3.2 `atsf_agent` - -负责: - -* 节点注册 -* 心跳上报 -* 拉取激活版本 -* 写入本地 Nginx 路由配置 -* 调用 `nginx -t` 和 `nginx -s reload` -* 失败回滚 -* 上报应用结果 -* 管理独立 Nginx 路径或 Docker Nginx 容器 - -### 3.3 `docs` - -负责: - -* 设计边界 -* 开发规范 -* 开发计划 -* 部署与联调说明 - -## 4. 开发原则 - -所有实现都必须遵守以下原则: - -* 先完成闭环,再做抽象。 -* 不为了"以后可能会支持"提前引入复杂模型。 -* Server 只管状态和配置,不直接 SSH 改节点。 -* Agent 是唯一落地入口。 -* 所有发布都是"新版本激活",不是在线覆盖编辑。 -* 第一版与第二版:所有节点默认拉同一份全量配置,不做节点分组差异化下发。 -* 能用 SQLite 解决的问题,不引入额外中间件。 -* 新功能优先复用现有 gin-template 结构,不平行造第二套框架。 - -## 5. 数据模型规范 - -第一版核心实体(已实现): - -* `proxy_routes` -* `config_versions` -* `nodes` -* `apply_logs` - -第二版新增实体: - -* `tls_certificates` — 证书托管 -* `managed_domains` — 域名管理与证书绑定 -* `agent_tokens` — Agent Token 管理 - -约束(全版本): - -* 不新增 `zone`、`origin_pool`、`policy`、`deployment` 这类平台化对象 -* `proxy_routes` 一条域名只对应一个 `origin_url` -* `config_versions` 必须保存完整快照和渲染后的 Nginx 路由配置 -* 激活版本全局只能有一个,不引入分组维度 -* 回滚通过"激活旧版本"实现,不直接修改历史记录 -* `agent_tokens` 中的 Token 值不可更新,只能创建或撤销 -* 域名到证书匹配必须支持精确匹配和通配符匹配(如 `*.example.com`) - -如需新增表,必须先证明它服务于当前迭代版本的主链路。 - -## 6. Server 开发规范 - -### 6.1 分层约束 - -Server 代码按以下职责拆分: - -* `controller/`: 参数解析、调用 service、返回 JSON -* `service/`: 业务逻辑、校验、渲染、版本切换 -* `model/`: 数据表结构、查询和持久化 -* `router/`: 路由注册 -* `middleware/`: 认证、鉴权、限流等横切逻辑 -* `common/`: 通用工具和配置 - -禁止行为: - -* controller 直接拼接复杂业务逻辑 -* controller 直接操作多个 model 形成事务链 -* middleware 承担业务逻辑 -* 为简单需求引入新的平台层抽象 - -### 6.2 API 约定 - -管理端和 Agent API 统一使用 JSON。 - -响应结构沿用现有模板风格: - -```json -{ - "success": true, - "message": "", - "data": {} -} -``` - -约束: - -* 成功或失败都返回清晰 `message` -* 列表接口返回稳定字段,不临时拼装结构 -* 新接口命名优先使用复数资源风格 -* Agent API 固定放在 `/api/agent/*` - -### 6.3 鉴权规范 - -管理端: - -* 继续复用 gin-template 的登录、角色和 session 体系 - -Agent(第一版): - -* 预共享单 Token,来自环境变量 -* 请求头统一使用 `X-Agent-Token` -* Agent 与管理端认证逻辑必须分开 - -Agent(第二版): - -* Token 改为查 `agent_tokens` 表验证 -* 环境变量 Token 降级为 bootstrap 模式:数据库存在有效 Token 记录时,环境变量 Token 不再有效 -* Token 创建时生成随机值,不允许外部传入 -* Token 值不可更新,仅支持撤销(设 `is_active=false`) - -注意: - -* 不要让 Agent 接口走用户登录态 -* 不要把 Nginx 命令暴露成远程管理接口 - -### 6.4 数据库规范 - -SQLite 是唯一默认数据库。 - -要求: - -* 新增模型后必须在 `model.InitDB()` 中加入 `AutoMigrate` -* 不写 MySQL/PostgreSQL 特有 SQL -* 不依赖外部迁移工具作为 MVP 前提 -* 时间字段统一使用 GORM 常规时间类型 - -### 6.5 发布与渲染规范 - -发布逻辑必须满足: - -* 发布时读取全部启用的 `proxy_routes` -* 生成完整的 Nginx 路由配置 -* 计算 checksum -* 保存快照到 `config_versions` -* 通过切换 `is_active` 激活版本 - -版本号格式固定为: - -```text -YYYYMMDD-NNN -``` - -例如: - -```text -20260309-001 -``` - -## 7. Agent 开发规范 - -### 7.1 模块边界 - -建议目录如下: - -```text -atsf_agent/ - cmd/agent/ - internal/config/ - internal/heartbeat/ - internal/sync/ - internal/nginx/ - internal/state/ - internal/httpclient/ -``` - -职责要求: - -* `config`: 本地配置读取 -* `heartbeat`: 心跳请求和状态组装 -* `sync`: 检查版本、下载配置、触发应用 -* `nginx`: 封装 Nginx 校验、reload 和文件写入 -* `state`: 本地成功版本和运行状态缓存 -* `httpclient`: Server API 调用 - -### 7.2 行为规范 - -Agent 必须满足以下行为: - -* 启动后生成或读取本地 `node_id` -* 周期性心跳 -* 周期性检查激活版本 -* 发现新版本后先备份旧文件 -* 写入新路由配置文件 -* 先执行 `nginx -t` -* 校验通过后执行 `nginx -s reload` -* 失败时回滚备份并再次校验和 reload -* 上报最终应用结果 -* 优先使用 `nginx_path` -* 未配置 `nginx_path` 时自动准备并使用 Docker Nginx 容器 -* 启动时先校验本地路由文件 checksum 与控制面激活版本是否一致 -* Docker 模式启动时应重建容器,而不是继续复用异常停止的旧容器 - -### 7.3 容错规范 - -第一版最少保证: - -* Server 不可用时,Nginx 继续使用旧配置 -* 下载失败时,不修改本地配置 -* 配置校验或 reload 失败时,自动尝试回滚 -* 本地状态文件损坏时,允许重新初始化,但不能删除正在生效的 Nginx 配置 -* Docker 容器异常停止时,启动阶段应自动重建容器并重新校验配置 - -### 7.4 外部命令规范 - -Agent 调用 Nginx 命令时必须: - -* 明确记录执行命令和返回错误 -* 设置合理超时 -* 不依赖交互式输入 -* 不通过 shell 拼接不可信参数 - -## 8. 前端开发规范 - -MVP 前端只做最小管理界面,不重做整套后台。 - -要求: - -* 继续使用现有 React 结构和 `semantic-ui-react` -* 页面只增加 MVP 必需页面 -* 不额外引入新的大型前端框架 -* API 请求统一放在 `web/src/helpers/api.js` 或同类 helper 中 -* 页面状态优先保持简单,不提前引入复杂全局状态管理 - -第一版只需要以下页面: - -* 反代规则页 -* 发布版本页 -* 节点状态页 -* 应用记录页 - -## 9. 代码风格规范 - -### 9.1 Go - -* 保持 package 名称简短且小写 -* 错误必须显式处理,不允许静默吞错 -* 函数尽量只做一件事 -* 输入校验放在 controller 或 service 边界 -* 业务枚举值使用明确常量,不使用魔法字符串散落代码 -* 仅在复杂逻辑前添加简短注释,不写废话注释 - -### 9.2 命名 - -* 表名和模型名使用业务语义,不沿用模板示例语义 -* 统一使用 `route`, `version`, `node`, `apply log` 这些术语 -* 不混用 `client`、`edge`、`agent` 指代同一模块,统一叫 `agent` - -### 9.3 日志 - -要求记录这些关键事件: - -* 发布成功/失败 -* Agent 注册 -* 心跳异常 -* 配置下载失败 -* Nginx 校验或 reload 成功/失败 -* 回滚触发 - -日志内容要可定位问题,但不要打印敏感 Token。 - -## 10. 测试与验收规范 - -### 10.1 第一版最低测试要求(已完成) - -Server 至少覆盖: - -* `origin_url` 校验 -* `domain` 重复校验 -* Nginx 路由配置渲染结果 -* 激活版本切换逻辑 -* 节点在线状态判定逻辑 - -Agent 至少覆盖: - -* 版本比较逻辑 -* 配置文件备份和回滚逻辑 -* `nginx -t` 或 `nginx -s reload` 失败分支 -* 本地状态文件读写 - -### 10.2 第二版新增测试要求 - -Server 新增覆盖: - -* HTTPS server 块渲染正确性(`enable_https=true` 时生成 443 块,`redirect_http=true` 时生成重定向块) -* HTTP-only 路由渲染结果不受 HTTPS 字段影响 -* 证书导入逻辑(手动导入、文件导入)和 PEM 校验逻辑 -* 域名证书匹配逻辑(精确匹配与 `*.example.com` 通配符匹配) -* `custom_headers` 注入到渲染结果的正确性 -* `agent_tokens` 创建与查表验证逻辑 -* Token 撤销后验证失败 -* bootstrap Token 降级行为(数据库有 Token 时环境变量 Token 失效) -* 预览接口不写库 -* diff 接口变更摘要计算正确性 - -### 10.3 联调验收标准(第一版,已完成) - -1. 管理端新增反代规则并成功发布版本 -2. Agent 能检测到新版本并拉取 -3. Agent 成功写入 Nginx 路由配置文件 -4. Agent 成功执行 `nginx -t` 和 `nginx -s reload` -5. 节点页能看到当前版本和最后心跳 -6. 当 reload 失败时,Agent 能回滚到旧配置并上报失败 - -### 10.4 联调验收标准(第二版) - -1. 创建含 HTTPS 字段的路由并发布,Nginx 能以 HTTPS 正确转发 -2. 控制面可手动导入和文件导入证书,导入后可被路由选择 -3. 反代规则输入域名后可自动匹配证书,且支持 `*.example.com` -4. 通过管理界面创建 Token,Agent 使用新 Token 成功访问 -5. 撤销 Token 后 Agent 请求返回 401 -6. 路由配置自定义头后,渲染结果包含对应指令 -7. 发布页预览展示正确渲染结果 -8. 变更摘要正确列出域名变化 - -## 11. 文档维护规范 - -出现以下情况时必须同步更新文档: - -* 产品版本范围变化(V1 → V2 → V3) -* API 发生破坏性变更 -* 数据模型新增或删除 -* Agent 本地文件路径变更 -* 部署方式变化 -* 新增或撤销对中间件/基础设施的依赖 - -优先更新: - -* `docs/design.md` -* `docs/development-guidelines.md` -* `docs/development-plan.md` +# ATSFlare 开发规范 + +## 1. 适用范围 + +本规范适用于 ATSFlare 第一版与第二版阶段。 + +项目第一版已完成以下能力: + +* 配置发布与同步 +* 节点心跳检测 +* Nginx 反向代理配置下发 + +第二版在此基础上新增以下能力: + +* HTTPS/TLS 路由支持 +* 证书托管(手动导入与文件导入) +* 域名管理与证书自动匹配(支持 `*.example.com`) +* Agent 管理与自动发现 +* 路由自定义请求头 +* 配置预览与变更摘要 + +当前明确仍不做: + +* 多租户 +* WAF、限流、Bot、防刷 +* 百分比灰度发布 +* 节点分组与差异化下发 +* Redis、MQ、对象存储、Prometheus +* 复杂缓存策略、证书自动签发、Purge、审批流 +* mid-tier、分层缓存、复杂策略编排 + +超出以上范围的需求,必须先更新设计文档,再开始编码。 + +## 2. 技术基线 + +### 2.1 Server + +控制中心基于现有 `atsf_server` 开发: + +* Web 框架:Gin +* ORM:GORM +* 数据库:SQLite +* 前端:现有 `atsf_server/web` +* 登录体系:沿用 gin-template 现有能力 + +约束: + +* 默认不配置 `SQL_DSN` +* 默认不配置 `REDIS_CONN_STRING` +* 不为了 MVP 引入新的基础设施依赖 + +### 2.2 Agent + +Agent 放在 `atsf_agent`,使用 Go 单体程序开发。 + +约束: + +* 单二进制 +* systemd 运行 +* 优先调用独立 Nginx,不依赖系统全局 Nginx +* 支持通过 `nginx_path` 显式指定独立 Nginx 可执行文件 +* 未指定 `nginx_path` 时,默认通过 Docker 启动独立 Nginx 容器 +* Agent 生成资源默认统一放在 `./data`,可通过 `data_dir` 统一覆盖 +* 负责本机 Nginx 路由配置写入、校验、reload、状态上报 + +### 2.3 Nginx 配置边界 + +第一版控制面只管理独立生成的 Nginx 路由配置文件,例如 `/etc/nginx/conf.d/atsflare_routes.conf`。第二版在此基础上增加证书托管能力。 + +第一版以下内容不纳入控制面: + +* `nginx.conf` +* 缓存策略 +* upstream 高级配置 + +第二版约束: + +* 允许在控制面托管 TLS 证书并下发给节点 +* 仅支持证书导入(手动粘贴与文件导入),不做自动签发/续期 +* `nginx.conf`、缓存策略、upstream 高级配置仍保持节点本地静态配置 + +## 3. 仓库职责划分 + +### 3.1 `atsf_server` + +负责: + +* 管理端 UI +* 管理端 API +* Agent API +* 数据存储 +* 配置渲染 +* 版本发布 +* 节点状态展示 + +### 3.2 `atsf_agent` + +负责: + +* 节点注册 +* 心跳上报 +* 拉取激活版本 +* 写入本地 Nginx 路由配置 +* 调用 `nginx -t` 和 `nginx -s reload` +* 失败回滚 +* 上报应用结果 +* 管理独立 Nginx 路径或 Docker Nginx 容器 + +### 3.3 `docs` + +负责: + +* 设计边界 +* 开发规范 +* 开发计划 +* 部署与联调说明 + +## 4. 开发原则 + +所有实现都必须遵守以下原则: + +* 先完成闭环,再做抽象。 +* 不为了"以后可能会支持"提前引入复杂模型。 +* Server 只管状态和配置,不直接 SSH 改节点。 +* Agent 是唯一落地入口。 +* 所有发布都是"新版本激活",不是在线覆盖编辑。 +* 第一版与第二版:所有节点默认拉同一份全量配置,不做节点分组差异化下发。 +* 能用 SQLite 解决的问题,不引入额外中间件。 +* 新功能优先复用现有 gin-template 结构,不平行造第二套框架。 + +## 5. 数据模型规范 + +第一版核心实体(已实现): + +* `proxy_routes` +* `config_versions` +* `nodes` +* `apply_logs` + +第二版新增实体: + +* `tls_certificates` — 证书托管 +* `managed_domains` — 域名管理与证书绑定 + +第二版扩展实体: + +* `nodes` — 增加 `agent_token`、`discovery_token`,用于节点管理与自动发现 + +约束(全版本): + +* 不新增 `zone`、`origin_pool`、`policy`、`deployment` 这类平台化对象 +* `proxy_routes` 一条域名只对应一个 `origin_url` +* `config_versions` 必须保存完整快照和渲染后的 Nginx 路由配置 +* 激活版本全局只能有一个,不引入分组维度 +* 回滚通过"激活旧版本"实现,不直接修改历史记录 +* 域名到证书匹配必须支持精确匹配和通配符匹配(如 `*.example.com`) +* `nodes.discovery_token` 仅用于首次接入,接入成功后必须失效 +* 删除节点必须立即使该节点凭证失效 + +如需新增表,必须先证明它服务于当前迭代版本的主链路。 + +## 6. Server 开发规范 + +### 6.1 分层约束 + +Server 代码按以下职责拆分: + +* `controller/`: 参数解析、调用 service、返回 JSON +* `service/`: 业务逻辑、校验、渲染、版本切换 +* `model/`: 数据表结构、查询和持久化 +* `router/`: 路由注册 +* `middleware/`: 认证、鉴权、限流等横切逻辑 +* `common/`: 通用工具和配置 + +禁止行为: + +* controller 直接拼接复杂业务逻辑 +* controller 直接操作多个 model 形成事务链 +* middleware 承担业务逻辑 +* 为简单需求引入新的平台层抽象 + +### 6.2 API 约定 + +管理端和 Agent API 统一使用 JSON。 + +响应结构沿用现有模板风格: + +```json +{ + "success": true, + "message": "", + "data": {} +} +``` + +约束: + +* 成功或失败都返回清晰 `message` +* 列表接口返回稳定字段,不临时拼装结构 +* 新接口命名优先使用复数资源风格 +* Agent API 固定放在 `/api/agent/*` + +### 6.3 鉴权规范 + +管理端: + +* 继续复用 gin-template 的登录、角色和 session 体系 + +Agent(第一版): + +* 预共享单 Token,来自环境变量 +* 请求头统一使用 `X-Agent-Token` +* Agent 与管理端认证逻辑必须分开 + +Agent(第二版): + +* 首次接入使用 `discovery_token` +* 注册成功后下发节点专属 `agent_token` +* 后续请求改为查 `nodes.agent_token` 验证 +* 不再依赖全局环境变量 Agent Token + +注意: + +* 不要让 Agent 接口走用户登录态 +* 不要把 Nginx 命令暴露成远程管理接口 + +### 6.4 数据库规范 + +SQLite 是唯一默认数据库。 + +要求: + +* 新增模型后必须在 `model.InitDB()` 中加入 `AutoMigrate` +* 不写 MySQL/PostgreSQL 特有 SQL +* 不依赖外部迁移工具作为 MVP 前提 +* 时间字段统一使用 GORM 常规时间类型 + +### 6.5 发布与渲染规范 + +发布逻辑必须满足: + +* 发布时读取全部启用的 `proxy_routes` +* 生成完整的 Nginx 路由配置 +* 计算 checksum +* 保存快照到 `config_versions` +* 通过切换 `is_active` 激活版本 + +版本号格式固定为: + +```text +YYYYMMDD-NNN +``` + +例如: + +```text +20260309-001 +``` + +## 7. Agent 开发规范 + +### 7.1 模块边界 + +建议目录如下: + +```text +atsf_agent/ + cmd/agent/ + internal/config/ + internal/heartbeat/ + internal/sync/ + internal/nginx/ + internal/state/ + internal/httpclient/ +``` + +职责要求: + +* `config`: 本地配置读取 +* `heartbeat`: 心跳请求和状态组装 +* `sync`: 检查版本、下载配置、触发应用 +* `nginx`: 封装 Nginx 校验、reload 和文件写入 +* `state`: 本地成功版本和运行状态缓存 +* `httpclient`: Server API 调用 + +### 7.2 行为规范 + +Agent 必须满足以下行为: + +* 启动后生成或读取本地 `node_id` +* 未显式配置 `node_name` 时自动获取主机名 +* 未显式配置 `node_ip` 时自动获取本机 IP +* 周期性心跳 +* 周期性检查激活版本 +* 发现新版本后先备份旧文件 +* 写入新路由配置文件 +* 先执行 `nginx -t` +* 校验通过后执行 `nginx -s reload` +* 失败时回滚备份并再次校验和 reload +* 上报最终应用结果 +* 优先使用 `nginx_path` +* 未配置 `nginx_path` 时自动准备并使用 Docker Nginx 容器 +* 启动时先校验本地路由文件 checksum 与控制面激活版本是否一致 +* Docker 模式启动时应重建容器,而不是继续复用异常停止的旧容器 +* 若本地 `agent_token` 为空且配置了 `discovery_token`,则应自动发起首次注册并完成 Token 置换 + +### 7.3 容错规范 + +第一版最少保证: + +* Server 不可用时,Nginx 继续使用旧配置 +* 下载失败时,不修改本地配置 +* 配置校验或 reload 失败时,自动尝试回滚 +* 本地状态文件损坏时,允许重新初始化,但不能删除正在生效的 Nginx 配置 +* Docker 容器异常停止时,启动阶段应自动重建容器并重新校验配置 + +### 7.4 外部命令规范 + +Agent 调用 Nginx 命令时必须: + +* 明确记录执行命令和返回错误 +* 设置合理超时 +* 不依赖交互式输入 +* 不通过 shell 拼接不可信参数 + +## 8. 前端开发规范 + +MVP 前端只做最小管理界面,不重做整套后台。 + +要求: + +* 继续使用现有 React 结构和 `semantic-ui-react` +* 页面只增加 MVP 必需页面 +* 不额外引入新的大型前端框架 +* API 请求统一放在 `web/src/helpers/api.js` 或同类 helper 中 +* 页面状态优先保持简单,不提前引入复杂全局状态管理 + +第一版只需要以下页面: + +* 反代规则页 +* 发布版本页 +* 节点状态页 +* 应用记录页 + +## 9. 代码风格规范 + +### 9.1 Go + +* 保持 package 名称简短且小写 +* 错误必须显式处理,不允许静默吞错 +* 函数尽量只做一件事 +* 输入校验放在 controller 或 service 边界 +* 业务枚举值使用明确常量,不使用魔法字符串散落代码 +* 仅在复杂逻辑前添加简短注释,不写废话注释 + +### 9.2 命名 + +* 表名和模型名使用业务语义,不沿用模板示例语义 +* 统一使用 `route`, `version`, `node`, `apply log` 这些术语 +* 不混用 `client`、`edge`、`agent` 指代同一模块,统一叫 `agent` + +### 9.3 日志 + +要求记录这些关键事件: + +* 发布成功/失败 +* Agent 注册 +* 心跳异常 +* 配置下载失败 +* Nginx 校验或 reload 成功/失败 +* 回滚触发 + +日志内容要可定位问题,但不要打印敏感 Token。 + +## 10. 测试与验收规范 + +### 10.1 第一版最低测试要求(已完成) + +Server 至少覆盖: + +* `origin_url` 校验 +* `domain` 重复校验 +* Nginx 路由配置渲染结果 +* 激活版本切换逻辑 +* 节点在线状态判定逻辑 + +Agent 至少覆盖: + +* 版本比较逻辑 +* 配置文件备份和回滚逻辑 +* `nginx -t` 或 `nginx -s reload` 失败分支 +* 本地状态文件读写 + +### 10.2 第二版新增测试要求 + +Server 新增覆盖: + +* HTTPS server 块渲染正确性(`enable_https=true` 时生成 443 块,`redirect_http=true` 时生成重定向块) +* HTTP-only 路由渲染结果不受 HTTPS 字段影响 +* 证书导入逻辑(手动导入、文件导入)和 PEM 校验逻辑 +* 域名证书匹配逻辑(精确匹配与 `*.example.com` 通配符匹配) +* `custom_headers` 注入到渲染结果的正确性 +* 节点创建、编辑、删除逻辑 +* `discovery_token` 首次接入成功后失效 +* 删除节点后 Agent 请求立即返回 401 +* Agent 自动探测主机名与 IP,且允许配置覆盖 +* Agent 首次注册成功后完成本地 token 置换 +* 预览接口不写库 +* diff 接口变更摘要计算正确性 + +### 10.3 联调验收标准(第一版,已完成) + +1. 管理端新增反代规则并成功发布版本 +2. Agent 能检测到新版本并拉取 +3. Agent 成功写入 Nginx 路由配置文件 +4. Agent 成功执行 `nginx -t` 和 `nginx -s reload` +5. 节点页能看到当前版本和最后心跳 +6. 当 reload 失败时,Agent 能回滚到旧配置并上报失败 + +### 10.4 联调验收标准(第二版) + +1. 创建含 HTTPS 字段的路由并发布,Nginx 能以 HTTPS 正确转发 +2. 控制面可手动导入和文件导入证书,导入后可被路由选择 +3. 反代规则输入域名后可自动匹配证书,且支持 `*.example.com` +4. 通过管理界面创建节点并生成 discovery token,Agent 使用 discovery token 成功接入 +5. 删除节点后 Agent 请求返回 401 +6. 路由配置自定义头后,渲染结果包含对应指令 +7. 发布页预览展示正确渲染结果 +8. 变更摘要正确列出域名变化 + +## 11. 文档维护规范 + +出现以下情况时必须同步更新文档: + +* 产品版本范围变化(V1 → V2 → V3) +* API 发生破坏性变更 +* 数据模型新增或删除 +* Agent 本地文件路径变更 +* 部署方式变化 +* 新增或撤销对中间件/基础设施的依赖 + +优先更新: + +* `docs/design.md` +* `docs/development-guidelines.md` +* `docs/development-plan.md` diff --git a/docs/development-plan.md b/docs/development-plan.md index d2fcd0d1..40fa8a4f 100644 --- a/docs/development-plan.md +++ b/docs/development-plan.md @@ -111,27 +111,30 @@ MVP 已于第一版完成。当前进入第二版迭代。 * 通配符证书可匹配子域名(如 `api.example.com` 匹配 `*.example.com`) * 无匹配证书时前端给出明确提示 -### V2 Phase 3: Agent Token 管理 +### V2 Phase 3: Agent 管理 目标: -* 支持多个命名 Token -* Token 可通过管理界面创建和撤销 -* 中间件改为查库验证 +* 实现对节点的增删改查 +* 实现节点自动发现机制 + 交付: -* `agent_tokens` 表与模型 * agent-auth 中间件改造(查表验证) -* 全局 Token 环境变量降级为 bootstrap 模式 -* Token CRUD API -* 前端 Token 管理页 +* 移除全局 Token 环境变量, 节点不再通过该方式连接server +* 引入自动发现TOKEN, 需要用户手动在节点页创建, 持有该TOKEN的节点会自动连接到SERVER +* Node CRUD API +* 前端 节点 管理页 完成标准: -* 新建 Token 后 Agent 可用该 Token 访问 Agent API -* 撤销 Token 后 Agent 请求立即返回 401 -* 全局环境变量 Token 仅在数据库无有效记录时生效 +* 用户启动server后需要手动在节点页添加节点, 此时会生成随机TOKEN, 持有该TOKEN的节点可以加入server. server可以修改节点的名称 +* 用户可以编辑节点, 包括节点名 +* 删除节点后 Agent 请求立即返回 401 +* 节点配置文件不再要求填写节点名和IP地址, 节点名默认从主机名获取, IP也是自动获取. 手动指定则为覆盖 +* 节点配置文件agent_token为空, 自动发现TOKEN配置为server生成值后, 会自己向server注册, 注册后会进行TOKEN置换, 更新节点配置文件agent_token + ### V2 Phase 4: 路由自定义头 @@ -175,7 +178,7 @@ MVP 已于第一版完成。当前进入第二版迭代。 1. HTTPS/TLS 支持(对现有渲染链路影响最小,独立可测) 2. 域名管理与证书自动匹配(与 HTTPS 强相关,尽早完成闭环) -3. Agent Token 管理(安全性改善,早做早稳) +3. Agent 管理(节点 CRUD + 自动发现 + token 置换) 4. 路由自定义头(纯增量,对现有结构影响小) 5. 配置预览与变更摘要(纯只读接口,最后补充) @@ -205,10 +208,11 @@ MVP 已于第一版完成。当前进入第二版迭代。 ### V2 Phase 3 检查项 -* 可通过管理界面创建 Token -* 新 Token 可被 Agent 使用 -* 撤销 Token 后访问立即失败 -* 全局 Token 环境变量在数据库有记录时失效 +* 可通过管理界面创建节点并生成 discovery token +* Agent 可使用 discovery token 自动接入并完成 agent token 置换 +* 用户可编辑节点名并删除节点 +* 删除节点后 Agent 请求立即失败 +* Agent 在未显式配置节点名/IP 时可自动探测 ### V2 Phase 4 检查项