# 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 管理与自动发现** * 管理端支持手工创建节点、编辑节点名、删除节点 * 管理端手工创建节点时,直接为该节点生成专属 `agent_token` * 预创建节点时,持有该 `agent_token` 的 Agent 会占据该节点位,并持续以该 Token 完成后续鉴权 * 系统同时维护一个全局 `discovery_token`,任意新节点可使用该 Token 自动接入 Server * Agent 使用全局 `discovery_token` 首次注册成功后,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,用于节点占位与后续正式鉴权 约束: * 管理端手工创建节点时必须直接生成 `agent_token` * 一个节点位只对应一个 `agent_token` * 全局 `discovery_token` 不存放在 `nodes` 表,而由系统配置统一维护 * 使用全局 `discovery_token` 自动接入的节点,应在注册成功后获得新的专属 `agent_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 置换 + 发布前可预览变更 ```