mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-01 06:36:38 +08:00
docs: 核查并润色文档,对齐项目实际实现
- 删除未经验证的环境要求(Docker 版本号、浏览器条目)与括号废话 - 故障排查改为真实处理路径(升级→重新发布→强制同步→重建 Agent→提交 issue),删除仅开发时用的排障章节 - 删除设计文档中的测试与验收、实现检查清单、贡献者阅读建议等开发内容 - 修正与代码不符的事实:reset-passwd 命令名、证书续签窗口 7 天、Pages 检查间隔 1440 分钟、Relay vhost 端口 8080、SSO 仅支持 OIDC 等 - 去除口语化表述与无意义括号,改写「不是…而是…」句式 - 同步修正文档站链接锚点,构建验证通过
This commit is contained in:
@@ -92,12 +92,12 @@ sequenceDiagram
|
||||
Agent 对数据面 OpenResty 的管控实现了端到端的闭环,包含配置落地、语法验证、平滑重载和异常状态捕获:
|
||||
|
||||
### 1. 配置文件的落地组织
|
||||
同步成功后,Agent 会将配置按照特定的物理结构写入到本地 `/etc/nginx/openflare-lua/` 目录下(或配置指定的 `LuaDir`):
|
||||
同步成功后,Agent 将配置写入 `data_dir` 下(默认相对路径 `etc/nginx/`、`etc/openflare/`、`var/lib/openflare/`,具体以 `agent.json` 中 `main_config_path`、`route_config_path`、`cert_dir`、`lua_dir`、`runtime_config_dir`、`pages_dir` 等字段为准):
|
||||
* `nginx.conf`:主配置文件(替换相关占位符,配置性能参数、Shared Dictionaries 及全局 Server)。
|
||||
* `routes.conf`:路由配置文件(由 Agent 生成,包含所有代理网站的 Server 块、证书路径、缓存及速率限制指令)。
|
||||
* `conf.d/openflare_routes.conf`:路由配置文件(由 Agent 生成,包含所有代理网站的 Server 块、证书路径、缓存及速率限制指令)。
|
||||
* `certs/`:证书存放目录(文件命名为 `{cert_id}.crt` 和 `{cert_id}.key`)。
|
||||
* `waf/` 与 `pow/`:WAF 及防 CC 挑战所需的专用 Lua 运行时脚本。
|
||||
* `waf_config.json` 与 `waf_ip_groups.json`:WAF 过滤引擎所需的结构化规则配置文件。
|
||||
* `lua/waf/` 与 `lua/pow/`:WAF 及防 CC 挑战所需的专用 Lua 运行时脚本。
|
||||
* `etc/openflare/waf_config.json` 与 `waf_ip_groups.json`:WAF 过滤引擎所需的结构化规则配置文件。
|
||||
* `pages_dir`:Pages 静态站点部署目录,默认位于 `data_dir/var/lib/openflare/pages`。当激活配置引用 Pages **项目**时,Agent 按 `project_id` 请求控制面「最新激活包」(hash + package),以流式方式写入临时文件并执行实际响应上限与 SHA-256 校验,再安全解压到 `projects/{project_id}/releases/{hash}`。解压后会复核文件数与总字节,绝对防御上限为 2 GiB 包、1,000 个文件、单文件及总量 8 GiB;随后原子切换 `current` 并**立即删除同项目其它历史 release**(仅保留最新)。项目内切换激活无需重发主配置;多项目对账时单项目失败不阻塞其它项目。
|
||||
|
||||
### 2. 精细化的重载动作
|
||||
@@ -105,13 +105,13 @@ Agent 对数据面 OpenResty 的管控实现了端到端的闭环,包含配置
|
||||
2. **写入并替换占位符**:将最新拉取的模板写入,自动将模板中的绝对路径占位符(如 `__OPENFLARE_LUA_DIR__`、`__OPENFLARE_PAGES_DIR__`)替换为本地实际运行路径。
|
||||
3. **语法校验**:调用 `openresty -t -c <temp_nginx.conf>` 进行严格的语法测试。
|
||||
4. **平滑重载**:若校验通过,将新配置移至正式路径,执行 `openresty -s reload`。若 OpenResty 处于未启动状态,则使用当前配置拉起进程。
|
||||
5. **捕获异常**:校验或重载失败时,Agent 会截获标准错误输出(stderr),提取前 2000 个字符的详细报错信息。
|
||||
5. **捕获异常**:校验或重载失败时,Agent 截获命令标准输出(stderr/stdout)作为失败详情上报。
|
||||
|
||||
---
|
||||
|
||||
## 发布与配置应用模型
|
||||
|
||||
OpenFlare 摒弃了动态 Patch 节点配置的落后方式,采用 **不可变配置版本发布模型**。
|
||||
OpenFlare 采用 **不可变配置版本发布模型**,而非对节点配置进行在线动态 Patch。
|
||||
|
||||
```text
|
||||
修改规则 -> 预览 / 查看 diff -> 发布 -> 生成完整配置版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
|
||||
|
||||
@@ -206,19 +206,3 @@ OpenResty 健康与连接数 --> 边缘健康(瞬时,不作 24h 业务总量
|
||||
| Pages artifact 与仓库构建分离 | 现有来源只导入预构建产物;未来 checkout/build 由 Server 隔离 executor 完成并复用 artifact pipeline,Agent 不执行第三方构建 |
|
||||
|
||||
---
|
||||
|
||||
## 贡献者阅读建议
|
||||
|
||||
修改系统架构或开发新功能前,请按以下顺序阅读:
|
||||
|
||||
1. **[产品边界](./index.md)**:了解 OpenFlare 核心定位与不允许逾越的设计边界。
|
||||
2. **[Agent 与发布模型](./agent-design.md)**:理解版本快照同步及失败回滚的安全兜底逻辑。
|
||||
3. **细分领域设计**:
|
||||
* Zone 与域名相关开发:阅读 [Zone 与域名资源设计](./zone-design.md)。
|
||||
* Cloudflare DNS 指向开发:阅读 [Cloudflare DNS 指向设计](./cloudflare-pointing.md)。
|
||||
* 穿透相关开发:阅读 [内网穿透隧道设计](./tunnel-design.md)。
|
||||
* WAF 相关开发:阅读 [WAF 设计](./waf-design.md) 与 [WAF 可编排规则设计](./waf-orchestration-design.md)。
|
||||
* Pages 托管开发:阅读 [Pages 静态托管设计](./pages-design.md)。
|
||||
* 监控同步开发:阅读 [Uptime Kuma 监控同步设计](./kuma-design.md)。
|
||||
* 看板/访问日志/节点指标开发:阅读 [观测数据传输模型](./observability-transport-model.md) 与 [边缘可观测与业务流量统计](./observability-design.md)。
|
||||
4. **[仓库结构](./index.md#仓库结构)**:明确各个物理目录分层职责,避免堆砌和重复开发。
|
||||
|
||||
@@ -187,8 +187,7 @@ OpenFlare 库表为 Source of Truth。每个成员期望:
|
||||
| 可选域名 | `GET /domains/available` |
|
||||
|
||||
* 成功 `response.OK`;失败 `response.Abort*`;**永不**在 JSON 中返回 Token。
|
||||
* Handler 与 `logics.go` 分离;CF 客户端可 mock 接口。
|
||||
* 变更后维护 Swagger(`make swagger`)。
|
||||
* Handler 与 `logics.go` 分离;CF 客户端以接口抽象便于替换。
|
||||
|
||||
## 前端
|
||||
|
||||
@@ -207,18 +206,9 @@ OpenFlare 库表为 Source of Truth。每个成员期望:
|
||||
* 典型:未配置 Token、Token 无效、节点无 IP、CF 无 Zone、同名多 A、限流。
|
||||
* Token 仅服务端解密使用;响应与日志禁止明文 Token。
|
||||
|
||||
## 数据迁移与测试
|
||||
## 数据迁移
|
||||
|
||||
* goose 双方言(PG/SQLite)新建三张表;默认值与 Go 零值一致。
|
||||
* 单测:Token 解析、reconcile 0/1/多条、橙云只初始化新成员、移出删远端(mock)、节点 IP 变更入队。
|
||||
* 禁止单测打真实 Cloudflare。
|
||||
|
||||
## 文档与边界同步
|
||||
|
||||
* 更新 [Zone 与域名资源设计](./zone-design.md):Zone 仍不内建权威 DNS;可选本模块负责 CF A 指向。
|
||||
* 更新 [系统架构](./architecture.md) 核心对象与阅读建议。
|
||||
* 更新 [产品边界](./index.md) 能力表。
|
||||
* 实现完成后写入 `docs/changelog/index.md` 的 `[Unreleased]`(纯设计文档变更不写 changelog)。
|
||||
|
||||
## 关键决策摘要
|
||||
|
||||
|
||||
@@ -204,7 +204,6 @@ access.log cache_status=$upstream_cache_status
|
||||
| 模型/默认 | 创建路由默认 `cache_policy=static`;读写时 `url`→`all` |
|
||||
| 快照 | `config_version` 快照规范化 |
|
||||
| UI | `proxy-routes/detail/components/cache-section.tsx` |
|
||||
| 测试 | `pkg/render/openresty/render_test.go` 等 |
|
||||
|
||||
---
|
||||
|
||||
@@ -235,21 +234,7 @@ access.log cache_status=$upstream_cache_status
|
||||
|
||||
---
|
||||
|
||||
## 7. 验证要点
|
||||
|
||||
* 渲染:无 Cookie/Auth/请求 Cache-Control 旁路;含 `proxy_cache_valid` 三行;`proxy_no_cache` 含 `$upstream_http_set_cookie`。
|
||||
* 单测:内置表含 `css`/`js`/`map`/`mjs`,**不含** `html`/`json`。
|
||||
* 手动:
|
||||
* 带 session Cookie 请求 `/a.js` → 第二次 `HIT`;
|
||||
* `/index.html` + `static` → 未缓存;
|
||||
* 源站对 eligible 路径返回 `Set-Cookie` → 不入库(持续 MISS/不 HIT);
|
||||
* 源站 `Cache-Control: private` → 不入库。
|
||||
* 观测:access log 三态与原始 `cache_status` 一致。
|
||||
* 生效:配置版本发布并节点应用后验证。
|
||||
|
||||
---
|
||||
|
||||
## 8. 决策矩阵(防漏判)
|
||||
## 7. 决策矩阵(防漏判)
|
||||
|
||||
| 场景 | CF | OpenFlare(本设计) |
|
||||
| --- | --- | --- |
|
||||
@@ -264,18 +249,7 @@ access.log cache_status=$upstream_cache_status
|
||||
|
||||
---
|
||||
|
||||
## 9. 后续路线图
|
||||
|
||||
1. Auth 完整 RFC/CF 条件缓存(Lua)
|
||||
2. 强制 Edge TTL / `proxy_ignore_headers`(Cache Rules 级)
|
||||
3. Purge API
|
||||
4. Cache Rules(有序规则 + 动作)
|
||||
5. 全局默认可缓存扩展名可配置;可选对齐 CF 更长扩展名表
|
||||
6. HEAD→GET
|
||||
|
||||
---
|
||||
|
||||
## 10. 决策记录
|
||||
## 8. 决策记录
|
||||
|
||||
| 决策 | 选择 | 原因 |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
在多节点的网关架构中,监控系统的状态与反向代理路由的状态通常是相互脱节的:
|
||||
1. **录入开销大**:每当网关控制面新增或下线一个站点,管理员都必须在监控系统(如 Uptime Kuma)中重复配置对应的探测地址与告警策略。
|
||||
2. **数据不一致**:当代理路由域名发生变更或切换 HTTPS 时,容易遗漏修改监控参数,导致监控系统误报或漏报。
|
||||
3. **环境污染隐患**:如果简单的在监控中执行全量“删除-重建”同步,不仅会清空监控系统中的历史统计指标和 SLA 曲线,还会影响到用户在此监控实例上自行配置的、与网关无关的其他监控任务。
|
||||
3. **环境污染隐患**:若在监控中执行全量“删除-重建”同步,会清空监控系统中的历史统计指标与 SLA 曲线,还会影响用户在此监控实例上自行配置的、与网关无关的其他监控任务。
|
||||
|
||||
为了解决这些痛点,OpenFlare 引入了基于客户端/服务器模式的 **Uptime Kuma 自动监控同步机制**,实现网关站点路由定义与可用性监测系统的强一致、低开销以及零污染同步。
|
||||
|
||||
@@ -106,4 +106,4 @@ stateDiagram-v2
|
||||
* Server 周期性(每 1 分钟)通过后台的 Cron Job 探测是否达到配置的同步间隔(`UptimeKumaSyncInterval`)。
|
||||
* 任务内部设计了互斥锁(Mutex Locking)。如果前一次同步请求因为网络延迟等原因尚未结束,下一次调度将自动跳过,防止并发多个 Socket.IO 连接对 Uptime Kuma 实例造成 DDOS 冲击。
|
||||
2. **WebSocket 状态监听**:
|
||||
* 同步程序利用 Socket.IO 的事件监听机制,在连接建立后,必须等到监听到 `monitorList` 事件的完整列表推送后,才允许向下执行差分算法,以规避因为数据加载不完整导致误删监控项的边界情况。
|
||||
* 同步程序利用 Socket.IO 的事件监听机制,在连接建立后,必须等到监听到 `monitorList` 事件的完整列表推送后,才允许向下执行差分算法,避免因数据加载不完整导致误删监控项。
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
## 1. 业务背景与产品范围
|
||||
|
||||
### 背景与痛点
|
||||
根据我们的系统安全分析,OpenFlare 的登录端点 `/api/user/login` 虽然配置了基于 IP 的限流限制,但由于缺少用户维度的防护机制,攻击者可使用代理池绕过 IP 限制对高权限账户(如 `root`)实施撞库和暴力破解。同时,对于系统登录页面,标准的视觉验证码对用户体验和无障碍不够友好。
|
||||
OpenFlare 的登录端点 `/api/v1/user/login` 缺少用户维度的防护机制,攻击者可使用代理池对高权限账户(如 `root`)实施撞库和暴力破解。同时,标准的视觉验证码对登录页用户体验和无障碍不够友好。
|
||||
|
||||
### 产品范围与技术选型
|
||||
* **技术选型**:Cap (Proof-of-Work 驱动的无感无图像验证码解决方案)。
|
||||
- **核心原理**:客户端(Widget/网页)从服务器获取工作量证明 (PoW) 的难题,使用浏览器后台计算求解并将答案回传。服务器验证答案的正确性,完成人机识别。
|
||||
- **优势**:无感、无图像验证、不依赖任何外部第三方 API 节点(私密)、包极小。
|
||||
* **接入范围**:控制面 Server 登录 API(`/api/user/login`)以及前端登录页面。
|
||||
* **配置粒度**:支持管理员通过控制台 Option 表随时开启/关闭验证码(`CapLoginEnabled`)。
|
||||
* **接入范围**:控制面 Server 登录 API(`/api/v1/user/login`)以及前端登录页面。
|
||||
* **配置粒度**:支持管理员通过控制台 Option 表随时开启/关闭验证码(`cap_login_enabled`)。
|
||||
|
||||
---
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
* 暴露 `POST /api/cap/challenge` 接口,为客户端分发 PoW 难题和签名的 JWT Token。
|
||||
* 暴露 `POST /api/cap/redeem` 接口,校验客户端提交的 PoW 解答并核发带有失效时间的登录凭证(Redeem Token)。
|
||||
* 将 Redeem Token 与对应过期时间保存在内存缓存/Redis 缓存中。
|
||||
* 在 `POST /api/user/login` 接口中,若启用了验证码保护,先校验并消耗(单次失效)对应的 `cap-token`。
|
||||
* 在 `POST /api/v1/user/login` 接口中,若启用了验证码保护,先校验并消耗(单次失效)对应的 `cap-token`。
|
||||
|
||||
### 2.2 验证流时序图
|
||||
```mermaid
|
||||
@@ -51,7 +51,7 @@ sequenceDiagram
|
||||
Server->>Browser: 返回 {success: false, reason}
|
||||
end
|
||||
User->>Browser: 输入账号密码,点击登录
|
||||
Browser->>Server: POST /api/user/login (在 HTTP 请求头中携带 X-Cap-Token)
|
||||
Browser->>Server: POST /api/v1/user/login (在 HTTP 请求头中携带 X-Cap-Token)
|
||||
alt CapLoginEnabled = true
|
||||
Server->>Server: Middleware (CapAuth) 校验并消费 X-Cap-Token
|
||||
alt token 合法且未过期且未被消费
|
||||
@@ -80,7 +80,7 @@ sequenceDiagram
|
||||
"error_msg": "",
|
||||
"data": {
|
||||
"challenge": {
|
||||
"c": 50,
|
||||
"c": 1,
|
||||
"s": 32,
|
||||
"d": 4
|
||||
},
|
||||
@@ -108,7 +108,7 @@ sequenceDiagram
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 登录接口 (POST /api/user/login)
|
||||
#### 3. 登录接口 (POST /api/v1/user/login)
|
||||
* **请求负载保持不变**:
|
||||
```json
|
||||
{
|
||||
@@ -124,4 +124,4 @@ sequenceDiagram
|
||||
1. **JWT 临时状态绑定**:难题在生成时就被签入 JWT payload,包含过期时间限制(10 分钟)。
|
||||
2. **Replay 拦截(Nonce 消耗)**:当客户端调用 `/redeem` 提交解答时,后端在缓存中标记该 JWT Signature 已使用。重复提交相同的解密包将返回 `already_redeemed`。
|
||||
3. **Redeem 一次性核销(单次失效)**:当客户端登录并提交 `cap-token` 时,后端在检验到合法性后立即从缓存中删除该 Key,防止黑客提取历史正确的 `cap-token` 进行重放登录。
|
||||
4. **验证机制无感化**:通过调整 `c (难题数)=50`,`d (难度)=4`,普通用户在桌面端和移动端只需 0.5 秒至 1.5 秒即可静默解出,极大地兼顾了用户体验和反爬效果。
|
||||
4. **验证机制无感化**:通过调整 `c (难题数)`、`d (难度)` 等参数平衡求解耗时与反爬强度,用户在后台静默解出,不打断登录流程。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 日志存储解耦
|
||||
|
||||
你会学到:哪些表属于日志用途、为什么不能绑死 ClickHouse,以及新增一张日志表时必须走哪条代码路径。逐步落地步骤见 `.agents/skills/logstore/SKILL.md`。
|
||||
你会学到:哪些表属于日志用途、为什么不能绑死 ClickHouse,以及新增一张日志表时必须走哪条代码路径。
|
||||
|
||||
观测字段与上报协议仍以 [观测上报协议与表结构](./observability-data-model.md) 为准;本文只约定**存到哪、怎么切库**。
|
||||
|
||||
@@ -70,7 +70,7 @@ ClickHouse 上的小时级物化视图(如 `of_access_log_hourly`)只服务
|
||||
|
||||
## 5. 新增一张日志表
|
||||
|
||||
列名必须在 ClickHouse / PostgreSQL / SQLite 三套 goose 中一致。顺序与禁止项见 `logstore` skill。要点:
|
||||
列名必须在 ClickHouse / PostgreSQL / SQLite 三套 goose 迁移中一致。要点:
|
||||
|
||||
* 高频表:CH 用 `MergeTree` + `toYYYYMM`;PG 用 `PARTITION BY RANGE(时间列)`,主键含分区键;SQLite 普通表 + 索引。
|
||||
* ID 用 snowflake `uint64`,迁移时原样保留。
|
||||
@@ -83,7 +83,4 @@ ClickHouse 上的小时级物化视图(如 `of_access_log_hourly`)只服务
|
||||
|
||||
## 6. 相关文档
|
||||
|
||||
* 开发步骤:`.agents/skills/logstore/SKILL.md`
|
||||
* DDL:`.agents/skills/database-migration/SKILL.md`
|
||||
* 批量写入:`.agents/skills/clickhouse-batchwriter/SKILL.md`
|
||||
* 实现前设计稿(历史):[日志数据库解耦设计](../superpowers/specs/2026-08-08-log-database-decoupling-design.md)
|
||||
* 观测字段与上报协议:[观测上报协议与表结构](./observability-data-model.md)
|
||||
|
||||
@@ -264,7 +264,7 @@ Agent:tail access.log → 解析 JSON 行 → 原样字段上报(可截断 p
|
||||
#### 边界
|
||||
|
||||
* Pages 静态 / 无 `proxy_cache` 的 location:多为空或 `-` → **未使用缓存**,不得标成「命中」。
|
||||
* 第一期只做明细可见;命中率看板、hourly 维度可后续用同一列聚合。
|
||||
* 明细详情展示缓存状态;命中率看板与 hourly 维度可基于同一列扩展。
|
||||
|
||||
**单次心跳条数建议:**
|
||||
|
||||
@@ -302,10 +302,10 @@ Agent:tail access.log → 解析 JSON 行 → 原样字段上报(可截断 p
|
||||
|
||||
写入关系库健康事件表(现有模型即可),不进访问日志湖。
|
||||
|
||||
### 3.8 Go 协议草图(目标)
|
||||
### 3.8 Go 协议结构
|
||||
|
||||
```go
|
||||
// pkg/protocol/agent.go(目标形态,实现时替换旧类型)
|
||||
// pkg/protocol/agent.go(当前实现)
|
||||
|
||||
type NodePayload struct {
|
||||
SchemaVersion int `json:"schema_version,omitempty"`
|
||||
@@ -447,7 +447,7 @@ type BufferedFacts struct {
|
||||
|
||||
---
|
||||
|
||||
## 5. 表结构(目标 DDL)
|
||||
## 5. 表结构(DDL)
|
||||
|
||||
> 引擎与 TTL 与现网一致倾向:访问日志 90 天,指标 30 天。
|
||||
> `id` 使用控制面 Snowflake/唯一 UInt64。
|
||||
@@ -556,7 +556,6 @@ GROUP BY node_id, hour, host;
|
||||
2. 即便存每小时 UV,对多小时窗口 **相加会严重高估**(同一 IP 跨小时重复计)。
|
||||
3. 产品「24h 独立访客」只认整窗 `uniqExact`;趋势图主序列是请求量/错误/字节,分时 UV 非主指标。
|
||||
|
||||
可选未来:若需要分时 UV 曲线,再单独加 `AggregatingMergeTree` 状态表或查询时对明细做 `uniqExact` 按小时 group(成本更高,不阻塞当前看板)。
|
||||
### 5.3 L3 事实表:`of_node_metric_snapshots`(保留,语义明确)
|
||||
|
||||
```sql
|
||||
@@ -761,18 +760,7 @@ Agent 解析:
|
||||
|
||||
---
|
||||
|
||||
## 10. 实现检查清单
|
||||
|
||||
- [x] `pkg/protocol`:仅 v2 字段,无兼容别名
|
||||
- [x] Agent:只组 `host_metrics` / `edge_health` / `access_logs` / `buffered`
|
||||
- [x] Server:无 request_reports / openresty 吞吐;健康当前态 PG、时序 CH
|
||||
- [x] CH migration:`request_length`、`request_time_ms`、`of_node_edge_health`、`of_access_log_hourly`、hourly 回填
|
||||
- [x] 看板/Zone API 统一读 access log 聚合
|
||||
- [x] UV:整窗 uniqExact;Zone 曲线标明分桶 UV;小时趋势不绘 UV
|
||||
|
||||
---
|
||||
|
||||
## 11. 修订记录
|
||||
## 10. 修订记录
|
||||
|
||||
| 日期 | 说明 |
|
||||
| --- | --- |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 边缘可观测与业务流量统计重构设计
|
||||
|
||||
你会学到:当前观测链路为何出现「看板 OpenResty 出站」与「Zone 已提供数据」不一致、字段与聚合为何冗余,以及目标架构如何让 **Agent 只上报事实、Server 只解释事实**,业务流量以访问日志为唯一真相源。
|
||||
你会学到:本次重构要解决的问题(「看板 OpenResty 出站」与「Zone 已提供数据」不一致、字段与聚合冗余),以及目标架构如何让 **Agent 只上报事实、Server 只解释事实**,业务流量以访问日志为唯一真相源。
|
||||
|
||||
---
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
### 2.1 产品约束(继承)
|
||||
|
||||
* 单租户、全局单激活配置;观测不引入多租户计费隔离。
|
||||
* ClickHouse 为访问日志与时序观测的强制分析存储。
|
||||
* 访问日志与时序观测走可切换日志主库(默认 ClickHouse,可切换 PostgreSQL/SQLite),见 [日志存储解耦](./logstore.md)。
|
||||
* Agent 无入向控制、Pull 模型;离线期间本地 OpenResty 继续服务,观测可本地缓冲后补传。
|
||||
|
||||
### 2.2 工程约束
|
||||
@@ -98,9 +98,9 @@ Server = 入库 + 聚合 + 归属 + 趋势 + 对账
|
||||
|
||||
---
|
||||
|
||||
## 4. 现状问题(基线)
|
||||
## 4. 重构前的问题(基线)
|
||||
|
||||
### 4.1 当前数据流(冗余)
|
||||
### 4.1 重构前数据流(冗余)
|
||||
|
||||
```text
|
||||
一次 HTTP 请求
|
||||
@@ -307,11 +307,10 @@ Agent 职责:
|
||||
|
||||
### 7.4 OpenResty 本地观测
|
||||
|
||||
**收敛后建议:**
|
||||
收敛后的状态:
|
||||
|
||||
* 保留:健康检查、`stub_status` 当前连接。
|
||||
* 删除主路径依赖:`log.lua` 中对 request/status/domain/rx/tx 的 shared dict 业务计数,以及 `/openflare/observability` 作为 TrafficReport 来源。
|
||||
* 若短期内保留 endpoint 供调试,不得再写入 Server 权威分析表。
|
||||
* 主路径不再依赖 `log.lua` 的 shared dict 业务计数;`/openflare/observability` 只返回健康与连接快照,不作为业务报表来源。
|
||||
|
||||
### 7.5 与 Agent 设计文档的关系
|
||||
|
||||
@@ -479,7 +478,7 @@ bytes_sent (= $body_bytes_sent), request_length
|
||||
### 11.4 健康状态权威
|
||||
|
||||
* **当前态**:PG `openresty_status` / `openresty_message`。
|
||||
* **时序**:CH `of_node_edge_health`(status + connections;无 message)。
|
||||
* **时序**:日志主库 `of_node_edge_health`(status + connections;无 message)。
|
||||
|
||||
### 11.5 UV
|
||||
|
||||
@@ -497,37 +496,7 @@ bytes_sent (= $body_bytes_sent), request_length
|
||||
|
||||
---
|
||||
|
||||
## 13. 验证标准
|
||||
|
||||
### 13.1 对账
|
||||
|
||||
在仅有单一 Zone 产生流量的环境:
|
||||
|
||||
```text
|
||||
看板「已提供数据」(24h) ≈ Zone「已提供的数据总计」(24h)
|
||||
误差仅来自时间窗对齐(整点截断)与未计入 Host
|
||||
```
|
||||
|
||||
多 Zone 时:
|
||||
|
||||
```text
|
||||
sum(各 Zone 已提供) + sum(未归属 Host) = 全局已提供
|
||||
```
|
||||
|
||||
### 13.2 回归
|
||||
|
||||
* Agent 单测:只解析与 offset,不出现业务 sum 断言为「上报契约」。
|
||||
* Server:Zone stats 与 dashboard business traffic 共用聚合测例。
|
||||
* 前端:文案快照/测试中不再出现业务含义的「OpenResty 出站」与「已提供数据」双卡片。
|
||||
|
||||
### 13.3 性能
|
||||
|
||||
* 24h 看板聚合 P95 可接受(必要时 hourly MV)。
|
||||
* 心跳 payload 体积:明细批量有上限;超限拆缓冲,不在 Agent 做摘要替代。
|
||||
|
||||
---
|
||||
|
||||
## 14. 风险与权衡
|
||||
## 13. 风险与权衡
|
||||
|
||||
| 风险 | 缓解 |
|
||||
| --- | --- |
|
||||
@@ -543,7 +512,7 @@ sum(各 Zone 已提供) + sum(未归属 Host) = 全局已提供
|
||||
|
||||
---
|
||||
|
||||
## 15. 关键决策摘要
|
||||
## 14. 关键决策摘要
|
||||
|
||||
| 决策 | 选择 | 否决方案 |
|
||||
| --- | --- | --- |
|
||||
@@ -556,7 +525,7 @@ sum(各 Zone 已提供) + sum(未归属 Host) = 全局已提供
|
||||
|
||||
---
|
||||
|
||||
## 16. 文档与代码映射(落地时)
|
||||
## 15. 文档与代码映射
|
||||
|
||||
| 区域 | 主要路径 |
|
||||
| --- | --- |
|
||||
@@ -568,8 +537,6 @@ sum(各 Zone 已提供) + sum(未归属 Host) = 全局已提供
|
||||
| 看板 | `internal/apps/openflare/dashboard/`、`internal/apps/openflare/observability/analytics.go` |
|
||||
| 前端 | `frontend/app/(main)/page.tsx`、`components/dashboard/*`、`websites/.../zone-overview.tsx` |
|
||||
|
||||
实现计划见:`docs/plan/20260717-observability-redesign.md`。
|
||||
|
||||
**推荐阅读顺序:**
|
||||
|
||||
1. **[观测数据传输模型](./observability-transport-model.md)**(最新:传什么、从哪采、频率、示例 JSON)
|
||||
@@ -577,7 +544,7 @@ sum(各 Zone 已提供) + sum(未归属 Host) = 全局已提供
|
||||
|
||||
---
|
||||
|
||||
## 17. 修订记录
|
||||
## 16. 修订记录
|
||||
|
||||
| 日期 | 说明 |
|
||||
| --- | --- |
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 0. 先记住三层(不要混)
|
||||
## 0. 先记住三层
|
||||
|
||||
| 层 | 回答的问题 | 唯一数据来源 | 产品例子 |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -215,7 +215,7 @@ cache_status ← $upstream_cache_status 【缓存状态;UI 可推导命中/
|
||||
|
||||
落库表:`of_node_access_logs`(可选 Server 侧 `of_access_log_hourly` 加速,**Agent 不写**)。
|
||||
|
||||
### 4.4 频率再强调
|
||||
### 4.4 上报频率
|
||||
|
||||
```text
|
||||
请求发生 ──立即──► 写 access.log
|
||||
@@ -229,9 +229,9 @@ Server ──立即/批量──► CH
|
||||
|
||||
## 5. L2 健康:edge_health 与 `/openflare/observability`
|
||||
|
||||
### 5.1 本机监测口(合并后目标)
|
||||
### 5.1 本机监测口
|
||||
|
||||
**只保留一个接口:**
|
||||
**数据采集接口:**
|
||||
|
||||
```http
|
||||
GET http://127.0.0.1:{openresty_observability_port}/openflare/observability
|
||||
@@ -241,7 +241,7 @@ GET http://127.0.0.1:{openresty_observability_port}/openflare/observability
|
||||
|
||||
**职责:** 回答「OpenResty 此刻怎样」,**不**回答业务已提供多少数据。
|
||||
|
||||
#### 返回示例(目标 JSON)
|
||||
#### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -263,7 +263,7 @@ GET http://127.0.0.1:{openresty_observability_port}/openflare/observability
|
||||
| `connections.active` | **瞬时** | Nginx 连接状态(原 stub_status Active) | 当前活跃连接 |
|
||||
| `reading` / `writing` / `waiting` | **瞬时** | 同上细分 | 可选但建议带 |
|
||||
|
||||
**不返回(已从目标模型删除):**
|
||||
**不返回(已删除):**
|
||||
|
||||
| 旧字段 | 原因 |
|
||||
| --- | --- |
|
||||
@@ -272,9 +272,9 @@ GET http://127.0.0.1:{openresty_observability_port}/openflare/observability
|
||||
| `source_countries` | 从未实现;国家走 Server GeoIP |
|
||||
| `server.accepts/handled/requests` | 进程累计 counter,易与业务请求混淆;主路径不收录 |
|
||||
|
||||
**`/openflare/stub_status`:** 合并进上述 JSON 后 **删除**(过渡期可双挂,Agent 只打合并口)。
|
||||
**`/openflare/stub_status`:** 保留;`/openflare/observability` 内部读取该口组装连接数 JSON,Agent 健康检查也直接探测该口。
|
||||
|
||||
### 5.2 采集机制(读快照,不是「调用才开始统计业务」)
|
||||
### 5.2 采集机制(读快照)
|
||||
|
||||
```text
|
||||
Nginx 在连接建立/释放时维护 Active connections 等
|
||||
@@ -284,9 +284,8 @@ Agent GET /openflare/observability
|
||||
只读取「当前值」拼 JSON 返回
|
||||
```
|
||||
|
||||
- **不是** GET 一次才去扫 access.log。
|
||||
- **不是** 60 秒业务均值。
|
||||
- 是 **瞬时 gauge 快照**。
|
||||
- 不扫 access.log、不算 60 秒业务均值。
|
||||
- 返回 **瞬时 gauge 快照**。
|
||||
|
||||
### 5.3 上报示例(装进 NodePayload)
|
||||
|
||||
@@ -361,7 +360,7 @@ Agent 读本机(如 `/proc`、磁盘统计等),**每次组包时读一次*
|
||||
|
||||
---
|
||||
|
||||
## 7. 一次完整上报示例(拼起来)
|
||||
## 7. 一次完整上报示例
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -466,13 +465,13 @@ t=6s 下一轮…
|
||||
|
||||
---
|
||||
|
||||
## 10. 旧模型对照(帮助消歧)
|
||||
## 10. 旧模型对照
|
||||
|
||||
| 旧做法 | 新模型 |
|
||||
| --- | --- |
|
||||
| Lua dict 60s 窗 request_count + Agent 10s 拉 + Server sum | **删除**;请求数 = 日志 count |
|
||||
| openresty_tx 当「出站」 | **删除**;已提供数据 = `sum(bytes_sent)` |
|
||||
| 两个口 observability + stub_status | **合并为一个** observability,只返回连接/探活 |
|
||||
| 两个口 observability + stub_status | 数据采集统一走 observability;stub_status 保留为探活与内部读取口 |
|
||||
| TrafficReport 预聚合 | **删除**;协议与 API 均无此路径 |
|
||||
| 业务与网卡混称「流量」 | **分文案、分 API、分表** |
|
||||
| 健康 status/message | **PG 最新态权威**;CH 仅 status+连接时序 |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* **默认可视**:默认启用,默认状态码标签 `500-599`,默认 OpenFlare 极简错误页。
|
||||
* **可自定义**:管理员可在线编辑完整 HTML;空 HTML 表示使用内置默认模板。
|
||||
* **状态码透传**:HTTP 响应 `status` 保持原错误码(如 502、522);页面正文通过 `{{status}}` 展示同一数值。
|
||||
* **全局统一**:侧栏「网站管理 → 错误页」单一配置,全站反代路由共用。
|
||||
* **全局统一**:侧栏「网站管理 → 响应页面」单一配置,全站反代路由共用。
|
||||
* **与发布一致**:配置经 Option 持久化,进入配置版本快照后随发布/回滚下发。
|
||||
|
||||
### 1.2 非目标
|
||||
@@ -35,12 +35,13 @@
|
||||
| 条件 | 行为 |
|
||||
| --- | --- |
|
||||
| 开关开启,且响应状态码落在展开后的集合内 | 返回自定义/默认 HTML,**status 不变** |
|
||||
| 开关开启且启用 GET-only,非 GET 请求返回匹配状态码 | 透传源站原始响应,不替换 |
|
||||
| 开关关闭 | 不生成 `error_page` 相关指令,透传 |
|
||||
| 状态码不在集合内 | 不替换 |
|
||||
| Pages 上游路由 | 不应用本功能 |
|
||||
| 源站成功返回 2xx/3xx/4xx(未配置时) | 不替换 |
|
||||
|
||||
实现上对反代 `location` 启用 `proxy_intercept_errors on`,因此**源站返回的**匹配 5xx 等也会被拦截,而不仅是网关本地生成的 502。
|
||||
全方法模式下对反代 `location` 启用 `proxy_intercept_errors on`,因此**源站返回的**匹配 5xx 等也会被拦截,而不仅是网关本地生成的 502;GET-only 模式改用 Lua header/body 过滤器仅替换 GET 响应正文。
|
||||
|
||||
### 2.2 状态码标签语法
|
||||
|
||||
@@ -81,6 +82,7 @@ Tags Input 每条标签:
|
||||
| `origin_error_page_enabled` | bool 字符串 | `true` | 总开关 |
|
||||
| `origin_error_page_status_codes` | JSON 字符串数组 | `["500-599"]` | 原始标签 |
|
||||
| `origin_error_page_html` | 文本 | `""` | 空 = 内置默认;最大 **256 KiB** |
|
||||
| `origin_error_page_get_only` | bool 字符串 | `false` | 仅对 GET 请求替换错误页,其它方法透传 |
|
||||
|
||||
API 复用:
|
||||
|
||||
@@ -106,6 +108,7 @@ API 复用:
|
||||
OriginErrorPageEnabled bool
|
||||
OriginErrorPageStatusCodes []string // 原始标签
|
||||
OriginErrorPageHTML string // 空则渲染器用内置默认
|
||||
OriginErrorPageGetOnly bool
|
||||
```
|
||||
|
||||
构建快照时从 Option 读取;Agent 只消费快照,不直读控制面 DB。
|
||||
@@ -121,26 +124,27 @@ OriginErrorPageHTML string // 空则渲染器用内置默认
|
||||
|
||||
```nginx
|
||||
proxy_intercept_errors on;
|
||||
error_page <expanded codes...> = /__openflare_origin_error;
|
||||
error_page <expanded codes...> @__openflare_origin_error;
|
||||
|
||||
location = /__openflare_origin_error {
|
||||
internal;
|
||||
location @__openflare_origin_error {
|
||||
default_type text/html;
|
||||
charset utf-8;
|
||||
# 保持 ngx.status 为原错误码
|
||||
# 读取模板,替换 {{status}} / {{host}} 后输出 body
|
||||
content_by_lua_block {
|
||||
# 读取模板,替换 {{status}} / {{host}} 后输出 body
|
||||
# ngx.status 保持原错误码
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 运行时替换
|
||||
|
||||
采用 **internal location 内轻量 Lua(或现有 resty 能力)** 读模板并 `string.gsub` 替换占位符,**不**把 status 固化进静态文件(请求间状态码不同)。
|
||||
采用 **命名 location 内 `content_by_lua_block`** 读模板并替换占位符,**不**把 status 固化进静态文件(请求间状态码不同)。GET-only 模式在反代 location 内用 `header_filter_by_lua_block` + `body_filter_by_lua_block` 仅替换 GET 响应正文,非 GET 请求透传。
|
||||
|
||||
禁止将错误页统一改为 HTTP 200。
|
||||
|
||||
### 4.3 关闭时
|
||||
|
||||
不输出 `proxy_intercept_errors`、`error_page`、内部 location 与对应 SupportFile(或文件可写但不被引用)。
|
||||
不输出 `proxy_intercept_errors`、`error_page`、内部 location 与对应 SupportFile(或文件可写但不被引用)。GET-only 模式同时不输出 Lua 过滤器。
|
||||
|
||||
### 4.4 与缓存 / stale
|
||||
|
||||
@@ -152,8 +156,7 @@ location = /__openflare_origin_error {
|
||||
|
||||
### 5.1 入口
|
||||
|
||||
* 侧栏「网站管理」新增:**错误页** → `/error-pages`
|
||||
* 更新 `openflareWebsiteNavGroup`、`openflareWebsiteSubNav`(若使用)、全局搜索关键词
|
||||
* 侧栏「网站管理 → 响应页面」:错误页 Tab(`/responses`),编辑页 `/responses/error-page/edit`、预览页 `/responses/error-page/preview`。
|
||||
|
||||
### 5.2 页面结构
|
||||
|
||||
@@ -165,14 +168,14 @@ location = /__openflare_origin_error {
|
||||
|
||||
### 5.3 组件依赖
|
||||
|
||||
若仓库尚无 Tags Input,按项目 shadcn 流程添加;样式与现有 UI 一致。
|
||||
Tags Input 与 HTML 编辑器复用现有 shadcn/ui 组件,样式与现有 UI 一致。
|
||||
|
||||
---
|
||||
|
||||
## 6. 数据流
|
||||
|
||||
```text
|
||||
管理员 /error-pages
|
||||
管理员 /responses(错误页 Tab)
|
||||
→ Option update-batch(校验标签与 HTML)
|
||||
→ w_system_configs
|
||||
|
||||
@@ -183,50 +186,13 @@ location = /__openflare_origin_error {
|
||||
|
||||
访客请求反代域名
|
||||
→ 源站/网关产生匹配状态码
|
||||
→ error_page → internal location
|
||||
→ error_page → 命名 location
|
||||
→ 替换占位符,status 保持原码,返回 HTML
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试与验收
|
||||
|
||||
### 7.1 自动化
|
||||
|
||||
* 状态码解析:单码、区间、去重、越界、反序、默认 `500-599`
|
||||
* 渲染:enabled/disabled conf 片段;空 HTML 用默认;自定义进 SupportFile
|
||||
* Option 校验:非法标签 / 超大 HTML → 4xx
|
||||
|
||||
### 7.2 手动
|
||||
|
||||
1. 默认配置:源站不可达 → CF 风格页,真实 502/504,页内数字一致
|
||||
2. 源站返回 503 → 替换页,status 503
|
||||
3. 仅标签 `522` → 仅 522 替换
|
||||
4. 关闭开关并发布 → 透传恢复
|
||||
5. 自定义 HTML 占位符预览与线上一致
|
||||
6. Pages 路由不受影响
|
||||
|
||||
### 7.3 文档
|
||||
|
||||
* 本设计文档;`docs/design/index.md` 能力表;`docs/config.ts` 侧栏
|
||||
* changelog `[Unreleased]` 用户可读改进条
|
||||
|
||||
---
|
||||
|
||||
## 8. 实现要点清单(供计划拆分)
|
||||
|
||||
1. goose seed 三个 Option key + model 常量
|
||||
2. 状态码解析/校验纯函数 + 单测
|
||||
3. Option update 路径挂接校验
|
||||
4. 快照填充 `ConfigSnapshot` 新字段
|
||||
5. `pkg/render/openresty`:error_page 块、SupportFile、默认 HTML、单测
|
||||
6. Agent 侧若需 Lua 辅助文件,随现有 nginx lua 目录同步
|
||||
7. 前端 Tags Input + `/error-pages` 页 + 导航
|
||||
8. changelog 与设计索引
|
||||
|
||||
---
|
||||
|
||||
## 9. 决策记录
|
||||
## 7. 决策记录
|
||||
|
||||
| 决策 | 选择 | 原因 |
|
||||
| --- | --- | --- |
|
||||
@@ -236,4 +202,3 @@ location = /__openflare_origin_error {
|
||||
| 响应 status | 保持原码 | 监控/SEO/客户端语义正确 |
|
||||
| 运行时替换 | internal + 轻量模板替换 | 每请求 status 不同 |
|
||||
| 自定义方式 | 在线 HTML | 灵活且无需文件上传链路 |
|
||||
`}
|
||||
@@ -27,15 +27,13 @@ Pages 静态托管子系统包含以下核心能力:
|
||||
* **安全包校验与解压缩**:内置路径逃逸防御、防软链接劫持、文件大小/数量上限与可配置上传包体积控制,保障节点物理安全。
|
||||
* **可配置限额**:管理员可在运维设置中调整「部署包大小上限」与「历史部署保留数」。
|
||||
|
||||
### 部署源与未来构建边界
|
||||
### 部署源
|
||||
|
||||
项目当前支持 manual、Remote URL、GitHub Release 三种来源视图。无 source 记录即 manual;切换或删除 source 不删除历史 deployment,也不改变当前 active deployment。Remote URL 只允许手动“同步并发布”;GitHub Release 支持 latest/tag 手动检查与同步,只有 latest 可选择定时检查和自动更新。
|
||||
|
||||
source 是可变配置,deployment 是不可变事实。source 配置与运行态游标、状态、租约分别存储;deployment 只保存创建时的安全 provenance 快照。所有产物都复用“下载或接收产物 → 真实字节与入口校验 → `upload.Ingest` → deployment”的 artifact pipeline:manual 上传停在 candidate,等待管理员显式激活;持久来源 sync 才在同一业务事务中 create-or-load 并原子激活。Agent 只消费 active deployment,不感知来源类型。
|
||||
source 是可变配置,deployment 是不可变事实。source 配置与运行态游标、状态、租约分别存储;deployment 只保存创建时的安全 provenance 快照。所有产物都复用“下载或接收产物 → 真实字节与入口校验 → `upload.Ingest` → deployment”的 artifact pipeline:manual 上传停在 candidate,等待管理员显式激活;持久来源 sync 才在同一业务事务中 create-or-load 并原子激活。
|
||||
|
||||
后续从 Git 仓库拉取源码并自动构建时,将新增独立 `git_repository` provider 与隔离的 build executor。它输出受限的预构建产物后继续复用上述导入管线;不得把 clone、依赖安装或任意构建命令下发给 Agent,也不得把 branch/build/env 字段塞入现有 `github_release` source。当前 V2 不增加这些未来字段或空任务,只稳定 provider 输出、source discriminated view 与 deployment provenance 三个扩展边界。
|
||||
|
||||
管理端信息架构参考 Cloudflare Pages 当前把 [Git integration](https://developers.cloudflare.com/pages/configuration/git-integration/) 与 [Direct Upload](https://developers.cloudflare.com/pages/get-started/direct-upload/) 分离、并统一展示生产状态与历史部署的方式:OpenFlare 项目详情按“当前生产部署 → 部署源 → 部署历史”组织。OpenFlare 仍允许切换来源并保留历史部署,不采用 Cloudflare 项目创建后来源不可切换的限制。
|
||||
管理端项目详情按“当前生产部署 → 部署源 → 部署历史”组织。
|
||||
|
||||
---
|
||||
|
||||
@@ -67,7 +65,7 @@ graph TD
|
||||
```
|
||||
|
||||
* **控制面(Control Plane)**:Server 接收本地上传,或通过受限 Provider 获取 Remote/GitHub 预构建产物;action task 与内部 scanner 负责检查、同步和自动更新。所有产物经统一 inspect 与 `upload.Ingest` 写入平台存储后端;manual 上传创建新的 candidate,持久来源 sync 则 create-or-load deployment 并原子激活。配置发布时只编译稳定的项目锚点与静态服务元数据。
|
||||
* **数据面(Data Plane)**:Agent 在心跳/WS 对账中发现配置引用的 Pages 项目,通过专属 API 拉取该项目当前激活包并执行校验解压缩。OpenResty 在本地提供静态文件服务;Agent 不感知产物来自上传、Remote、GitHub 或未来 build executor。
|
||||
* **数据面(Data Plane)**:Agent 在心跳/WS 对账中发现配置引用的 Pages 项目,通过专属 API 拉取该项目当前激活包并执行校验解压缩。OpenResty 在本地提供静态文件服务;Agent 不感知产物来自上传、Remote 或 GitHub。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -116,14 +116,14 @@ Kill 并重新拉起 frps 进程
|
||||
server_name intranet.example.com;
|
||||
# ... TLS 证书与 WAF 过滤逻辑 ...
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18080; # 指向本地 frps 的虚拟主机端口
|
||||
proxy_pass http://127.0.0.1:8080; # 指向本地 frps 的虚拟主机端口
|
||||
proxy_set_header Host $host; # 必须保留原 Host,因为 frps 依靠 Host 进行内部路由分发
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
2. **中继节点 (frps)**:
|
||||
`frps` 监听到 `18080` 端口有 HTTP 请求进来,读取 HTTP 请求头中的 `Host: intranet.example.com`,在其已注册的活跃隧道表中检索该域名对应的加密 TCP 连接(由内网 frpc 建立)。
|
||||
`frps` 在虚拟主机端口(默认 `8080`)收到 HTTP 请求,读取 HTTP 请求头中的 `Host: intranet.example.com`,在其已注册的活跃隧道表中检索该域名对应的加密 TCP 连接(由内网 frpc 建立)。
|
||||
3. **加密隧道传输 (TCP)**:
|
||||
`frps` 将 HTTP 请求封装进内部 TCP 隧道协议,发送给内网的 `frpc` 客户端。
|
||||
4. **内网客户端分发 (frpc)**:
|
||||
|
||||
@@ -8,7 +8,7 @@ Server 保存带坐标和修订号的编辑图,发布时再次校验并编译
|
||||
|
||||
IP 组独立于规则拓扑更新。手动、订阅和自动 IP 组由控制面维护,Agent 先原子替换 JSON、最后更新 checksum。协调 Worker 每 5 秒检查 checksum,仅变化时读取完整快照并分发给其它 Worker;失败时保留上一份有效数据。完整运行时快照上限为 20 MiB,Server 发布/同步与 Agent 落盘使用同一序列化校验;OpenResty 使用独立的 64 MiB 共享字典和非淘汰写入,容量不足时拒绝新版本而不破坏已提交快照。
|
||||
|
||||
地域节点使用 Country 与 City MMDB。Agent 首次启动时从程序内嵌数据库初始化缺失文件,后续按配置周期下载更新,请求处理始终读取 OpenResty 已加载的数据库。数据库不可用时地域匹配返回 `false` 并限频告警,不允许因数据损坏意外放行其它执行错误。
|
||||
地域节点使用 Country 与 City MMDB。Docker 镜像内置数据库文件,裸二进制安装由 Agent 首次启动时下载缺失文件,后续按配置周期更新,请求处理始终读取 OpenResty 已加载的数据库。数据库不可用时地域匹配返回 `false` 并限频告警,不允许因数据损坏意外放行其它执行错误。
|
||||
|
||||
## 安全顺序
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
用户新增 WAF 规则时只输入名称。Server 随即创建一张合法的默认图 `开始 → 通过`,前端进入基于 React Flow 的独立编排页面。用户通过添加处理单元、配置节点并连接分支构建策略,不再填写固定顺序的黑白名单与 PoW 表单。
|
||||
|
||||
第一阶段支持以下节点:
|
||||
支持的节点:
|
||||
|
||||
| 节点 | 数量约束 | 输入 | 输出 | 配置 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
IP 匹配、地域匹配、UA 检查与安全防护不区分黑名单或白名单。`true` 只表示请求通过该节点判定,`false` 只表示未通过;放行或阻止的业务含义完全由连线决定。UA 检查的求值顺序为:要求携带 UA → 屏蔽爬虫/非正常 UA → 白名单匹配。安全防护在请求 Path/Query/Header/Cookie/Body(有限)上做特征匹配。PoW 验证完成后沿 `next` 继续,未完成时由挑战页面接管当前请求,不产生 `false` 分支。
|
||||
|
||||
不在第一阶段实现循环、脚本节点、任意表达式节点、子图调用和跨规则跳转。
|
||||
不实现循环、脚本节点、任意表达式节点、子图调用和跨规则跳转。
|
||||
|
||||
## 控制面架构
|
||||
|
||||
@@ -116,13 +116,3 @@ WAF 列表展示规则名称、启用状态、节点数量、应用路由数量
|
||||
新 Worker 只接受完整且可解析的规则运行态配置。旧 Worker 在 OpenResty 优雅 reload 期间继续使用旧内存图,新 Worker 使用新图,因此请求不会观察到半更新状态。
|
||||
|
||||
地域数据库不可用时,地域匹配返回 `false` 并限频告警,保持现有行为。IP 组刷新失败时保留旧内存快照。PoW 未完成由挑战模块接管请求,不视为执行错误;PoW 节点配置先以短期键写入 OpenResty 共享内存,再通过 `ngx.exec` 的显式参数传给内部挑战处理器,不能依赖内部重定向保留 `ngx.ctx` 或隐式继承请求参数。发布快照中的空规则绑定必须编码为 JSON 空数组;运行时将旧快照中的 `null` 可选数组按空数组处理,禁止因 `cjson` 的 `ngx.null` userdata 中断请求。
|
||||
|
||||
## 测试与验收
|
||||
|
||||
* Go 单元测试覆盖图结构、端口、可达性、终止性、节点配置、体积限制、编译结果、修订冲突和绑定顺序。
|
||||
* 数据库测试覆盖 PostgreSQL/SQLite 迁移、默认图、旧绑定稳定排序和回滚。
|
||||
* Lua 测试覆盖所有节点出口、多规则顺序、全局规则前置、多个阻止响应、PoW 接管和损坏运行时图保护。
|
||||
* Agent/OpenResty 测试覆盖发布 reload、加载一次、失败回滚、IP 组五秒 checksum 刷新和旧快照保留。
|
||||
* 前端测试覆盖创建后导航、特殊节点唯一性、连线限制、属性编辑、即时校验、未保存提示和并发冲突。
|
||||
* 集成测试从控制面创建并编排规则,发布后用真实请求验证放行、阻止、PoW 和 IP 组热刷新。
|
||||
* API 变更后运行 `make swagger`;完成实现后运行前端检查与构建以及 `make code-check`。
|
||||
|
||||
@@ -54,7 +54,7 @@ erDiagram
|
||||
* `GET/POST /api/v1/d/zones`
|
||||
* `GET/POST /api/v1/d/zones/:id/update`
|
||||
* `POST /api/v1/d/zones/:id/delete`
|
||||
* `GET/POST /api/v1/d/zones/:id/domains`
|
||||
* `POST /api/v1/d/zones/:id/domains`(列表经 overview 返回)
|
||||
* `POST /api/v1/d/zones/:id/domains/:domainID/update`
|
||||
* `POST /api/v1/d/zones/:id/domains/:domainID/delete`
|
||||
* `GET /api/v1/d/zones/:id/overview`
|
||||
@@ -91,10 +91,3 @@ WAF、Pages、上游与发布版本仍属于 `proxy_routes`。Zone 概览只聚
|
||||
* 持久化:域名与证书只存在于 `of_zone_domains`;`of_proxy_routes` 仅保存路由策略(上游、缓存、限流、WAF 绑定键等)。
|
||||
* 渲染:配置快照在内存中组装临时 `Domains` / `DomainCertIDs` 供 OpenResty 渲染,不写回数据库。
|
||||
* 结构迁移仅使用 `internal/infra/persistence/migrator/goose/{postgres,sqlite}/*.sql`;启动时自动导入历史域名,第二阶段后旧列不存在则为空操作。
|
||||
|
||||
## 验证
|
||||
|
||||
* 单元测试:Public Suffix List 分组、FQDN / 通配符拒绝、跨 Zone 路由、证书 SAN 覆盖、删除保护及迁移幂等性;清理后断言旧列/旧表不存在。
|
||||
* 集成测试:Zone、Zone 域名与路由 API 的成功与失败响应;现有路由迁移后生成相同 OpenResty 域名与证书配置。
|
||||
* 前端测试:Zone 列表、ID 路由、详情加载 / 错误 / 空状态、域名选择器与 API 负载。
|
||||
* 手动验证:迁移前后比较激活配置快照中的 `server_name` 和证书路径,发布后使用根域及各子域请求验证路由。
|
||||
|
||||
Reference in New Issue
Block a user