Compare commits

..

22 Commits

Author SHA1 Message Date
ryan 123140b762 [优化] POW 系统接口方案 2026-06-06 12:21:55 +08:00
ryan 6bf5023af1 [优化] POW 系统接口方案 2026-06-06 12:13:55 +08:00
ryan 4be7c19798 [优化] POW 系统接口方案 2026-06-06 12:07:39 +08:00
ryan 32e1a07157 [优化] 重构数据库历史迁移校验架构 2026-06-06 11:50:57 +08:00
ryan 2662c65f57 [优化] 部署脚本优化 2026-06-06 11:06:45 +08:00
ryan 3cfefb4367 [优化] go 引用调整 2026-06-06 10:46:40 +08:00
ryan ee1110b752 [优化] 文档优化 2026-06-05 12:17:07 +08:00
ryan 9959397934 [优化] 文档优化 2026-06-05 11:49:50 +08:00
ryan 5f82f72a6f [优化] 文档优化 2026-06-05 11:44:25 +08:00
ryan 43e8ef154f [优化] response 结构调整 2026-06-05 11:42:25 +08:00
ryan 4dc4c745c8 [优化] response 结构调整 2026-06-05 11:32:10 +08:00
ryan a5257be319 [优化] 文档更新 2026-06-05 11:28:02 +08:00
ryan 30f36efe5a [优化] 文档更新 2026-06-05 11:27:49 +08:00
ryan 5a6c5cf72c [优化] 文档更新 2026-06-05 11:13:07 +08:00
ryan 189916d1db [优化] 文档更新 2026-06-05 11:12:06 +08:00
ryan 546856594e [优化] 文档更新 2026-06-05 10:42:06 +08:00
ryan 457b3397fd [优化] 文档更新 2026-06-05 10:33:18 +08:00
ryan 47ff33c653 [优化] 白名单优先级高于黑名单 2026-06-04 12:35:52 +08:00
ryan 1d41b108fc [优化] 界面优化 2026-06-04 12:19:04 +08:00
ryan b7a101fc60 [优化] CI 2026-06-04 12:11:55 +08:00
ryan c85d9104ec [优化] 每日定时清理预发布版本 2026-06-04 12:08:33 +08:00
ryan f3cdbdd7d5 [优化] 修复文档 2026-06-04 12:05:36 +08:00
598 changed files with 5749 additions and 5243 deletions
+37
View File
@@ -0,0 +1,37 @@
---
name: plan
description: 项目级技能:规定在开启新方案、新计划或进行任务交接时,必须将计划落库到 docs/plan 文件夹中并使用对应模板。
---
# Plan & Handover Skill
当你在当前项目中被要求“开启一个新的方案”、“制定开发计划”或者准备“任务交接(Handover)”时,你**必须**遵循本技能的工作流,将计划或方案落库到 `docs/plan/` 目录下。
> [!IMPORTANT]
> **什么时候应当创建实现计划?**
> * **必须创建的场景**:新功能开发、涉及多组件的重大架构重构、引入新基础设施依赖,以及存在显著设计决策冲突的**中大型、复杂**需求。
> * **绝对不要创建的场景**:改个包名、挪个文件、重命名函数、小修小改修复 Bug 等**轻量级、简单的局部重构**。对于此类改动,应当直接完成并运行单元测试通过后交付,禁止制造冗余的计划文档。
## 执行工作流 (Workflow)
### 1. 确定计划类型
* **新特性/技术实现计划**:如果你要开发新功能或进行重大重构,你需要创建**实现计划**。
* **AI 任务交接计划**:如果当前任务尚未完成但需要记录进度留作以后或其他 AI 代理接手,你需要创建**交接计划**。
### 2. 读取对应模板
在创建计划文档前,必须读取对应的模板内容,并严格按照模板的骨架进行填充:
* **实现计划模板**:`docs/plan/implementation-plan-template.md`
* **接手计划模板**:`docs/plan/handover-plan-template.md`
### 3. 落库与命名规范
在 `docs/plan/` 目录下创建新的 Markdown 文件进行保存:
* **实现计划**命名格式:`docs/plan/YYYYMMDD-[feature-name].md` (例如:`20260605-uptime-kuma-sync.md`)
* **接手计划**命名格式:`docs/plan/handover-[task-name].md` (例如:`handover-waf-ip-group.md`)
### 4. 隔离约束 (极其重要)
`docs/plan/` 目录下的文档**仅限内部开发和 AI 代理同步使用**。
* **绝对禁止**将新创建的 plan 文档加入到项目的官方导航配置(如 `docs/config.ts` 的 `nav` 或 `sidebar` 导航条中)。
* **绝对禁止**通过任何方式将其暴露给文档渲染框架(如 VitePress)对外渲染。
## 后续动作
落库完成后,向用户报告计划已生成在 `docs/plan/` 目录下,并列出文档的核心要点或待决策项(如有),等待用户 Review 或批准后即可推进下一步。
+37
View File
@@ -0,0 +1,37 @@
---
name: plan
description: 项目级技能:规定在开启新方案、新计划或进行任务交接时,必须将计划落库到 docs/plan 文件夹中并使用对应模板。
---
# Plan & Handover Skill
当你在当前项目中被要求“开启一个新的方案”、“制定开发计划”或者准备“任务交接(Handover)”时,你**必须**遵循本技能的工作流,将计划或方案落库到 `docs/plan/` 目录下。
> [!IMPORTANT]
> **什么时候应当创建实现计划?**
> * **必须创建的场景**:新功能开发、涉及多组件的重大架构重构、引入新基础设施依赖,以及存在显著设计决策冲突的**中大型、复杂**需求。
> * **绝对不要创建的场景**:改个包名、挪个文件、重命名函数、小修小改修复 Bug 等**轻量级、简单的局部重构**。对于此类改动,应当直接完成并运行单元测试通过后交付,禁止制造冗余的计划文档。
## 执行工作流 (Workflow)
### 1. 确定计划类型
* **新特性/技术实现计划**:如果你要开发新功能或进行重大重构,你需要创建**实现计划**。
* **AI 任务交接计划**:如果当前任务尚未完成但需要记录进度留作以后或其他 AI 代理接手,你需要创建**交接计划**。
### 2. 读取对应模板
在创建计划文档前,必须读取对应的模板内容,并严格按照模板的骨架进行填充:
* **实现计划模板**:`docs/plan/implementation-plan-template.md`
* **接手计划模板**:`docs/plan/handover-plan-template.md`
### 3. 落库与命名规范
在 `docs/plan/` 目录下创建新的 Markdown 文件进行保存:
* **实现计划**命名格式:`docs/plan/YYYYMMDD-[feature-name].md` (例如:`20260605-uptime-kuma-sync.md`)
* **接手计划**命名格式:`docs/plan/handover-[task-name].md` (例如:`handover-waf-ip-group.md`)
### 4. 隔离约束 (极其重要)
`docs/plan/` 目录下的文档**仅限内部开发和 AI 代理同步使用**。
* **绝对禁止**将新创建的 plan 文档加入到项目的官方导航配置(如 `docs/config.ts` 的 `nav` 或 `sidebar` 导航条中)。
* **绝对禁止**通过任何方式将其暴露给文档渲染框架(如 VitePress)对外渲染。
## 后续动作
落库完成后,向用户报告计划已生成在 `docs/plan/` 目录下,并列出文档的核心要点或待决策项(如有),等待用户 Review 或批准后即可推进下一步。
-122
View File
@@ -1,122 +0,0 @@
name: Release
on:
workflow_dispatch:
push:
tags: ["v*"]
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve version metadata
id: version
shell: bash
run: |
SHOULD_RUN=true
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
if [[ "${GITHUB_REF}" == refs/heads/main ]] && [[ -n "$POINTED_TAG" ]]; then
SHOULD_RUN=false
VERSION="$POINTED_TAG"
elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
VERSION="${GITHUB_REF_NAME}"
else
VERSION="$(git describe --tags)"
fi
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
else
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
fi
- name: Set up Node.js
if: steps.version.outputs.should_run == 'true'
uses: https://github.com/actions/setup-node@v4
with:
node-version: 20
- name: Build Frontend
if: steps.version.outputs.should_run == 'true'
env:
CI: ""
VERSION: ${{ steps.version.outputs.version }}
run: |
cd openflare_server/web
corepack enable
pnpm install --frozen-lockfile
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build
- name: Set up Go
if: steps.version.outputs.should_run == 'true'
uses: https://github.com/actions/setup-go@v5
with:
go-version-file: openflare_server/go.mod
- name: Build Server Binaries
if: steps.version.outputs.should_run == 'true'
shell: bash
env:
CGO_ENABLED: 0
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
mkdir -p dist
cd openflare_server
go mod download
while read -r GOOS GOARCH ASSET_NAME; do
GOOS="$GOOS" GOARCH="$GOARCH" \
go build -trimpath -ldflags "-s -w -X 'openflare/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
done <<'EOF'
linux amd64 openflare-server-linux-amd64
linux arm64 openflare-server-linux-arm64
darwin amd64 openflare-server-darwin-amd64
darwin arm64 openflare-server-darwin-arm64
windows amd64 openflare-server-windows-amd64.exe
EOF
- name: Build Agent Binaries
if: steps.version.outputs.should_run == 'true'
shell: bash
env:
CGO_ENABLED: 0
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
cd openflare_agent
go mod download
while read -r GOOS GOARCH ASSET_NAME; do
GOOS="$GOOS" GOARCH="$GOARCH" \
go build -trimpath -ldflags "-s -w -X 'openflare-agent/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
done <<'EOF'
linux amd64 openflare-agent-linux-amd64
linux arm64 openflare-agent-linux-arm64
darwin amd64 openflare-agent-darwin-amd64
darwin arm64 openflare-agent-darwin-arm64
windows amd64 openflare-agent-windows-amd64.exe
EOF
- name: Publish Release
if: steps.version.outputs.should_run == 'true'
uses: https://gitea.com/actions/gitea-release-action@v1
with:
tag_name: ${{ steps.version.outputs.version }}
name: ${{ steps.version.outputs.version }}
target_commitish: ${{ github.sha }}
files: |
dist/*
draft: false
prerelease: ${{ steps.version.outputs.is_prerelease == 'true' }}
@@ -2,6 +2,8 @@ name: Cleanup prerelease tags
on:
workflow_dispatch:
schedule:
- cron: '0 3 * * *'
permissions:
contents: write
+1 -1
View File
@@ -75,7 +75,7 @@ jobs:
uses: docker/build-push-action@v7
with:
context: .
file: ./openflare_agent/Dockerfile
file: ./openflare-agent/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
+1 -1
View File
@@ -75,7 +75,7 @@ jobs:
uses: docker/build-push-action@v7
with:
context: .
file: ./openflare_relay/Dockerfile
file: ./openflare-relay/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
+2 -2
View File
@@ -74,8 +74,8 @@ jobs:
id: build
uses: docker/build-push-action@v7
with:
context: ./openflare_server
file: ./openflare_server/Dockerfile
context: .
file: ./openflare-server/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
+30 -30
View File
@@ -1,30 +1,30 @@
name: Build GitHub Pages
on:
workflow_dispatch:
inputs:
name:
description: 'Reason'
required: false
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
with:
persist-credentials: false
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
env:
CI: ""
run: |
cd openflare_server/web
corepack enable
pnpm install --frozen-lockfile
pnpm build
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: openflare_server/web/build # The folder the action should deploy.
name: Build GitHub Pages
on:
workflow_dispatch:
inputs:
name:
description: 'Reason'
required: false
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
with:
persist-credentials: false
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
env:
CI: ""
run: |
cd openflare-server/web
corepack enable
pnpm install --frozen-lockfile
pnpm build
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: openflare-server/web/build # The folder the action should deploy.
+16 -14
View File
@@ -79,7 +79,7 @@ jobs:
CI: ""
VERSION: ${{ needs.prepare.outputs.version }}
run: |
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install --frozen-lockfile
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build
@@ -88,7 +88,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: openflare_server/web/build
path: openflare-server/web/build
retention-days: 1
build-binaries:
@@ -127,15 +127,15 @@ jobs:
uses: actions/download-artifact@v4
with:
name: frontend-build
path: openflare_server/web/build
path: openflare-server/web/build
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: openflare_server/go.mod
go-version-file: go.mod
- name: Build Server
working-directory: openflare_server
working-directory: openflare-server
env:
CGO_ENABLED: 0
GOOS: ${{ matrix.goos }}
@@ -145,7 +145,7 @@ jobs:
run: |
go mod download
mkdir -p ../dist
go build -trimpath -ldflags "-s -w -X 'openflare/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
- name: Upload Binary Artifact
uses: actions/upload-artifact@v4
@@ -184,10 +184,10 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: openflare_agent/go.mod
go-version-file: go.mod
- name: Build Agent
working-directory: openflare_agent
working-directory: openflare-agent
env:
CGO_ENABLED: 0
GOOS: ${{ matrix.goos }}
@@ -197,7 +197,7 @@ jobs:
run: |
go mod download
mkdir -p ../dist
go build -trimpath -ldflags "-s -w -X 'openflare-agent/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-agent/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
- name: Upload Agent Artifact
@@ -239,10 +239,10 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: openflare_relay/go.mod
go-version-file: go.mod
- name: Build Relay
working-directory: openflare_relay
working-directory: openflare-relay
env:
CGO_ENABLED: 0
GOOS: ${{ matrix.goos }}
@@ -252,7 +252,7 @@ jobs:
run: |
go mod download
mkdir -p ../dist
go build -trimpath -ldflags "-s -w -X 'openflare-relay/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/relay
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-relay/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/relay
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
- name: Upload Relay Artifact
@@ -294,7 +294,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: openflared/go.mod
go-version-file: go.mod
- name: Build Flared
working-directory: openflared
@@ -307,7 +307,7 @@ jobs:
run: |
go mod download
mkdir -p ../dist
go build -trimpath -ldflags "-s -w -X 'openflare-flared/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/flared
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflared/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/flared
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
- name: Upload Flared Artifact
@@ -366,6 +366,8 @@ jobs:
files: dist/*
draft: false
prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }}
body: |
查看完整更新日志: https://open-flare.pages.dev/changelog/
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -1
View File
@@ -50,4 +50,6 @@ go.work.sum
*-source
*-source.*
.codex*
.codex*
/bin/
+27 -57
View File
@@ -2,69 +2,39 @@
本文件是 OpenFlare 的 AI 接手入口,不承载详细设计、规范和计划。接手项目时,请根据以下分层文档指引进行阅读与开发:
### 1. 开发指导规范 (AI & Developer Guidelines)
### 面向 AI 的开发指导规范 (AI Guidelines) 必须阅读
* **必须阅读**:
* **[docs/guideline/development-constraints.md](docs/guideline/Constraints.md)**:掌握核心后端/Agent/前端分层约束、数据模型规范、数据库迁移升级协议、API 与鉴权设计准则及变更准入与验收标准。
* **[docs/guideline/Role.md](./docs/guideline/Role.md)**:通用的 Go 后端开发与高质量编码准则,包括架构、并发、错误处理、安全及工作流程。
* **正在进行的开发计划与接手 (Handover & Plans)**:
* **[docs/plan/index.md](./docs/plan/index.md)**:查看正在进行的开发实现计划(Implementation Plan)与 AI 代理交接文档(Handover),接手项目时优先检查。
为了理解 OpenFlare 的设计理念、产品边界、核心机制以及代码编写的工程约束,**AI 在接手项目时必须首先且完整阅读以下文档**:
### 2. 系统设计与架构 (Design Docs)
* **[docs/guildline/development-constraints.md](./docs/guildline/development-constraints.md)**
*作用:掌握核心后端/Agent/前端分层约束、数据模型规范、数据库迁移升级协议、API 与鉴权设计准则。*
* **[docs/guildline/Guidelines.md](./docs/guildline/Guidelines.md)**
*作用:通用的 Go 后端开发与高质量编码准则,包括架构、并发、错误处理、安全及工作流程。*
* **[docs/guildline/Project.md](./docs/guildline/Project.md)**
*作用:针对 OpenFlare 后端特定的控制器参数解析、响应处理、纯净工具类与数据库逻辑完全隔离、Go 泛型切片去重及 JSON 序列化避坑细则。*
* **[docs/design/index.md](./docs/design/index.md)**:理解产品范围、系统边界、核心对象及长期约束,以及[仓库结构](./docs/design/index.md#仓库结构)。
* **[docs/design/architecture.md](./docs/design/architecture.md)**:理解 Server、Agent、OpenResty 与前端的职责边界与网络拓扑。
* **[docs/design/agent-design.md](./docs/design/agent-design.md)**:理解 Agent 设计原则、与 Server 交互时序、OpenResty 管控与配置发布回滚模型。
### 系统参阅文档 按需查阅
* **[docs/reference/configuration.md](./docs/reference/configuration.md)**
*作用:系统启动时支持的所有环境变量、命令行参数、运行时 Option 选项和 Agent 配置文件字段。*
* **[docs/reference/cli.md](./docs/reference/cli.md)**
*作用:Server 与 Agent 可用的命令行参数、安装/卸载脚本参数等参考。*
* **[docs/reference/api.md](./docs/reference/api.md)**
*作用:管理端 API 与 Agent API 的响应结构、路径和详细鉴权约定。*
### 3. 部署与参考手册 (Deployment & References)
### 面向开发者的文档 按需查阅
* **[docs/design/index.md](./docs/design/index.md)**
*作用:理解当前 MVP 的产品范围、系统边界、核心对象和长期约束。*
* **[docs/design/architecture.md](./docs/design/architecture.md)**
*作用:理解 Server、Agent、OpenResty 与前端的职责边界与网络拓扑。*
* **[docs/design/agent-design.md](./docs/design/agent-design.md)**
*作用:理解 Agent 设计原则、与 Server 交互时序、OpenResty 管控、配置版本发布与三阶段异常回滚模型。*
* **[docs/design/development.md](./docs/design/development.md)**
*作用:了解如何搭建本地开发环境,运行后端 Server、Agent 和前端开发服务器,以及运行测试与构建的命令。*
* **[docs/design/repository.md](./docs/design/repository.md)**
*作用:熟悉仓库的整体物理结构和各子目录的职责。*
### 部署与升级指南
* **[docs/deployment/deployment.md](./docs/deployment/deployment.md)**
*作用:理解 Server 和 Agent 的单机、Docker 部署配置,以及 Agent 接入、升级、卸载和联调步骤。*
* **[docs/deployment/server.md](./docs/deployment/server.md)**
*作用:如何配置系统配置、服务环境变量并正确启动 Server 服务。*
* **[docs/deployment/agent.md](./docs/deployment/agent.md)**
*作用:理解 Agent 接入的 discovery/agent 令牌鉴权机制、本地配置文件及 Docker 部署参数。*
* **[docs/deployment/upgrade.md](./docs/deployment/upgrade.md)**
*作用:Server 及各代理节点 Agent 的升级步骤与维护策略。*
* **[docs/deployment/deployment.md](./docs/deployment/deployment.md)** / **[server.md](./docs/deployment/server.md)** / **[agent.md](./docs/deployment/agent.md)** / **[upgrade.md](./docs/deployment/upgrade.md)**:Server 和 Agent 的单机、Docker 部署配置,接入、升级与维护策略。
* **[docs/reference/configuration.md](./docs/reference/configuration.md)** / **[cli.md](./docs/reference/cli.md)**:支持的环境变量、参数、命令行与配置文件参考。
---
## 执行要求
## 开发与执行要求
* 如果实现内容超出 [产品边界](./docs/design/index.md),先修改设计文档,再继续编码。
* 如果实现方式违反 [开发约束](./docs/guildline/development-constraints.md),应优先调整方案,而不是绕过规范。
* 如果实现方式涉及后端代码逻辑,必须严格遵循 [docs/guildline/](./docs/guildline/) 下的所有开发准则。
* 如果需求与当前阶段原则冲突,优先遵守 [开发约束](./docs/guildline/development-constraints.md) 中的变更准入与验收标准。
* 如果任务涉及前端改造或管理端 UI,必须同时遵守 [开发约束](./docs/guildline/development-constraints.md) 中的前端规范。
## 文档维护要求
当以下内容发生变化时,应同步更新对应中文文档,不要同步英文文档:
* 产品范围或系统边界变化:更新 `docs/design/index.md`
* 系统结构、模块职责变化:更新 `docs/design/architecture.md`
* 发布、同步、回滚与 Agent 模型变化:更新 `docs/design/agent-design.md`
* 业务分层、数据模型边界、接口约定、阶段原则、测试基线变化:更新 `docs/guildline/development-constraints.md`
* 后端开发规范、代码质量要求、重构模式、去重逻辑与避坑指南变化:更新 `docs/guildline/` 下的对应开发准则文件
* 产品启动、部署、升级、联调方式变化:更新 `docs/guide/quick-start.md`、`docs/deployment/deployment.md` 和 `README.md`
* 用户操作路径、常见场景变化:更新 `docs/guide/usage.md`
* 本地开发、测试、构建方式变化:更新 `docs/design/development.md`
* 环境变量、命令行参数、运行时配置、Agent 配置变化:更新 `docs/reference/configuration.md`
* **任何代码、配置或文档变更完成后:必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应条目(新增 / 变更 / 修复),格式遵循文件内已有模板。**
1. **设计先行**:
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
2. **遵守约束**:
* 必须严格遵循 `docs/guideline/` 下的所有开发准则与开发约束规范,不得绕过任何规范。
* 涉及前端改造或管理端 UI 时,必须遵守 `docs/guideline/development-constraints.md` 中的前端规范。
3. **开发计划与交接**:
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
4. **文档与变更日志**:
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
* **纯文档变更(如 `docs/` 下的 Markdown 文档、README 等)不需要写入 changelog。**
+209
View File
@@ -0,0 +1,209 @@
<div align="center">
# OpenFlare
**[English](./README.en.md) | [📖 中文](./README.md)**
OpenFlare is an open-source CDN orchestration and edge security platform. It supports reverse proxies, centralized configuration synchronization, secure intranet penetration (Tunnels), dynamic WAF protection, and anti-CC challenges.
</div>
<p align="center">
<a href="https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/LICENSE">
<img src="https://img.shields.io/github/license/Rain-kl/OpenFlare?color=brightgreen" alt="license">
</a>
<a href="https://github.com/Rain-kl/OpenFlare/releases/latest">
<img src="https://img.shields.io/github/v/release/Rain-kl/OpenFlare?color=brightgreen&include_prereleases" alt="release">
</a>
<a href="https://github.com/Rain-kl/OpenFlare/pkgs/container/openflare">
<img src="https://img.shields.io/badge/GHCR-ghcr.io%2Frain--kl%2Fopenflare-brightgreen" alt="ghcr">
</a>
</p>
> [!WARNING]
> After logging in for the first time with the `root` user, make sure to change the default password `123456`.
>
> The BETA version is a temporary product for the development and testing phase. It may contain unknown issues and should not be used in production environments.
## Documentation
**https://open-flare.pages.dev**
Quick links:
* [Quick Start](https://open-flare.pages.dev/en/guide/quick-start)
* [Deployment Guide](https://open-flare.pages.dev/en/deployment/deployment)
* [Configuration Reference](https://open-flare.pages.dev/reference/configuration)
* [System Design](https://open-flare.pages.dev/design/)
## Core Features
* **Reverse Proxy Management**: Website rules as the aggregation boundary, supporting multi-domain binding and multi-upstream load balancing with unified management of all OpenResty node configurations.
* **Immutable Config Version Control**: Full-snapshot publish model based on version numbers (`YYYYMMDD-NNN`), with pre-publish diff preview, a single globally active version, and one-click sub-second rollback.
* **Secure Intranet Penetration (Tunnels)**: An open-source alternative to Cloudflare Tunnels. Securely expose local intranet Web services to the public network via Relay and OpenFlared clients — no public IP or open inbound ports required.
* **Edge WAF Safety Protection**: Provides global and custom rule groups, supporting manual/automatic/subscription IP groups, MaxMind GeoIP country-level access control, Checksum-based differential IP group sync (no Nginx reload), and custom block responses.
* **Anti-CC & Human-Machine Challenge (PoW)**: Built-in high-performance client-side cryptographic Proof of Work challenges (similar to Turnstile) to block and intercept botnets and scrapers at the gateway edge in seconds.
* **Pages Static Hosting**: Upload pre-built ZIP packages directly; edge Agents pull and serve them via local OpenResty, with SPA Fallback and built-in API reverse proxy configuration.
* **Automated TLS Certificate Management**: Supports dynamic certificate upload, automatic multi-domain certificate matching and binding, and ACME-based automatic issuance and renewal via Let's Encrypt.
* **Uptime Kuma Monitoring Sync**: Integrates with Uptime Kuma to automatically sync the monitoring site list using differential updates, providing real-time awareness of node availability and service health.
* **SSO Single Sign-On**: Supports GitHub OAuth and standard OIDC protocol for seamless integration with enterprise identity providers.
* **Unified Observability**: Aggregates node request metrics, real-time access log details, host/Nginx resource snapshots, health events, and a re-upload buffer for network fluctuations.
## Quick Start
### 1. Launch Server
```yaml
services:
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: openflare
POSTGRES_USER: openflare
POSTGRES_PASSWORD: replace-with-strong-password
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U openflare -d openflare"]
interval: 10s
timeout: 5s
retries: 5
openflare:
image: ghcr.io/rain-kl/openflare:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "3000:3000"
environment:
SESSION_SECRET: replace-with-random-string
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
GIN_MODE: release
LOG_LEVEL: info
volumes:
postgres-data:
```
```bash
docker compose up -d
```
Access at: `http://localhost:3000`
Default credentials:
* Username: `root`
* Password: `123456`
### 2. Install Agent
Before installing an Agent, please install OpenResty on the target node first, or use the Agent Docker image with OpenResty built-in.
You can copy the installation command from **Node Management -> Details -> Node Info -> Node Token & Deployment** in the control panel, or directly use the scripts below:
#### Docker Deployment
For Docker deployment, you can directly run the Agent image:
```bash
docker pull ghcr.io/rain-kl/openflare-agent:latest
docker rm -f openflare-agent 2>/dev/null || true
docker run -d --name openflare-agent --restart unless-stopped \
-p 80:80 -p 443:443/tcp -p 443:443/udp \
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
ghcr.io/rain-kl/openflare-agent:latest
```
#### Local Installation
Using `discovery_token` to register:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
--server-url http://your-server:3000 \
--discovery-token YOUR_DISCOVERY_TOKEN
```
Using node-specific `agent_token`:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
--server-url http://your-server:3000 \
--agent-token YOUR_AGENT_TOKEN
```
The installation script defaults to `/opt/openflare-agent`, creates a `openflare-agent.service`, automatically searches for `openresty`, and can be executed repeatedly to reinstall or upgrade the Agent.
### 3. Uninstall Agent
To completely uninstall the Agent and clear local data, run:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
```
The uninstallation script will stop and remove the `openflare-agent.service`, and delete the entire `/opt/openflare-agent` directory. It will not delete the local OpenResty installation.
### 4. Publish Your First Configuration
1. Log in to the management panel and add a reverse proxy rule.
2. View the preview or change summary before publishing.
3. Activate the new version.
4. Agents will receive the configuration and apply it via WebSocket notification or subsequent heartbeats.
The version number format is fixed as `YYYYMMDD-NNN`. Historical versions are immutable, and rollback is achieved by reactivating an older version.
## UI Preview
### Dashboard Overview
![OpenFlare dashboard overview](./docs/assets/readme/dashboard-overview.png)
### Node Details
![OpenFlare node detail](./docs/assets/readme/node-detail.png)
### Proxy Configuration
![OpenFlare version release](./docs/assets/readme/proxy-route-detail.png)
## Management Panel & API
The management panel includes:
* Reverse Proxy Rules
* Configuration Versions
* Node Management
* Application Records
* TLS Certificates
* Domain Management
* Pages Static Hosting
* WAF Rule Groups
* Intranet Tunnels
* Uptime Kuma Monitoring Sync
* SSO Login Configuration
* User Management
* Settings
* Version Updates
* PoW Rules
After logging in to the dashboard, access Swagger UI at: `/swagger/index.html`
## License
This project is licensed under [Apache License 2.0](./LICENSE).
## Star History
<a href="https://www.star-history.com/?repos=Rain-kl%2FOpenFlare&type=date&legend=bottom-right">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
</picture>
</a>
+71 -66
View File
@@ -2,9 +2,9 @@
# OpenFlare
**[📖 English](./README.md) | [中文](./README.zh-CN.md)**
**[📖 中文](./README.md) | [English](./README.en.md)**
OpenFlare is an open-source CDN orchestration and edge security platform. It supports reverse proxies, centralized configuration synchronization, secure intranet penetration (Tunnels), dynamic WAF protection, and anti-CC challenges.
OpenFlare 是开源 CDN 编排与边缘安全平台。它支持反向代理、集中式配置同步、内网穿透(Tunnels)、动态 WAF 防护以及防 CC 挑战。
</div>
@@ -21,36 +21,36 @@ OpenFlare is an open-source CDN orchestration and edge security platform. It sup
</p>
> [!WARNING]
> After logging in for the first time with the `root` user, make sure to change the default password `123456`.
>
> The BETA version is a temporary product for the development and testing phase. It may contain unknown issues and should not be used in production environments.
> 使用 `root` 用户初次登录系统后,务必修改默认密码 `123456`。
>
> BETA 版本为开发测试阶段的临时产物,可能存在未知问题,请勿在生产环境使用。
## Documentation
## 文档
**https://open-flare.pages.dev**
Quick links:
常用入口:
* [Quick Start](https://open-flare.pages.dev/en/guide/quick-start)
* [Deployment Guide](https://open-flare.pages.dev/en/guide/deployment)
* [Configuration Reference](https://open-flare.pages.dev/reference/configuration)
* [System Design](https://open-flare.pages.dev/design/)
* [快速开始](https://open-flare.pages.dev/guide/quick-start)
* [部署说明](https://open-flare.pages.dev/deployment/deployment)
* [配置项参考](https://open-flare.pages.dev/reference/configuration)
* [系统设计](https://open-flare.pages.dev/design/)
## Core Features
## 核心能力
* **Centralized Real-Time Config Sync**: Sync configurations across all nodes in real time via WebSockets and heartbeats with sub-second hot reload. Instantly retrieve alerts and statuses. No manual SSH login or online patching required.
* **Distributed CDN Orchestration**: Orchestrate scattered and independent OpenResty nodes into a highly collaborative distributed Content Delivery Network (CDN) fleet, supporting website-level multi-domain aggregation, upstream Keepalive, and multi-origin load balancing.
* **Secure Intranet Penetration (Tunnels)**: An open-source alternative to Cloudflare Tunnels. Expose local intranet services securely to the public network without a public IP or exposing inbound ports.
* **Edge WAF Safety Protection**: Provides global and custom rule groups, supporting IP/CIDR filtering, MaxMind GeoIP country-level regional access control, asynchronous differential synchronization of IP group members without Nginx reloads, and custom block responses.
* **Anti-CC & Human-Machine Challenge (PoW)**: Built-in high-performance client-side cryptographic Proof of Work challenges (similar to Turnstile) to block and intercept botnets and scrapers at the gateway edge in seconds.
* **Publish & Sync Model**: Based on immutable configuration versions (`YYYYMMDD-NNN`), preview and compare differences before publishing, a single globally active version, and one-click sub-second rollbacks.
* **Three-Stage Disaster Recovery & Rollback**: Supports automatic node backup rollback, a built-in safety fallback page (Port 80/503 keeping status monitoring and security interception active), and an abnormal configuration blocklist.
* **Automated Certificate Hosting**: Supports dynamic certificate upload, automatic multi-domain certificate matching and binding, ACME automatic renewal, and full lifecycle status tracking.
* **Unified Observability**: Aggregates node request counts, provides real-time access analysis, host/Nginx resource snapshots, health logs, and a re-upload buffer for network fluctuations.
* **反代配置管理**:以网站规则为聚合边界,支持多域名绑定与多上游负载均衡,统一管理所有 OpenResty 节点的反代配置。
* **安全内网穿透(Tunnels)**:开源版的 Cloudflare Tunnels。无须公网 IP 或暴露入向端口,通过 Relay 中继节点与 OpenFlared 客户端安全反向穿透内网 Web 服务至公网。
* **边缘 WAF 安全防护**:提供全局与自定义规则组,支持手动/自动/订阅型 IP 组、MaxMind GeoIP 国家级地域准入、IP 组成员 Checksum 差分同步(无需 Nginx 重载)以及自定义拦截响应。
* **防 CC 与人机挑战(PoW)**:内置高性能客户端密码学 Proof of Work 挑战(类似 Turnstile),在网关边缘秒级拦截并阻断僵尸网络与爬虫。
* **Pages 静态托管**:直接上传预构建 ZIP 包,由边缘 Agent 拉取并通过 OpenResty 本地提供服务,支持 SPA Fallback 与内置 API 反向代理配置。
* **TLS 证书自动化**:支持证书动态上传、多域名证书自动匹配绑定,以及通过 ACME 协议向 Let's Encrypt 自动申请与续期证书。
* **Uptime Kuma 监控同步**:与 Uptime Kuma 集成,自动差分同步监控站点列表,实时感知节点存活与服务可用状态。
* **SSO 单点登录**:支持 GitHub OAuth 与标准 OIDC 协议,无缝接入企业身份提供商实现统一登录。
* **统一观测**:聚合节点请求指标、实时访问日志明细、宿主机与 Nginx 资源快照、健康事件以及网络波动补传缓冲。
## Quick Start
## 快速开始
### 1. Launch Server
### 1. 启动 Server
```yaml
services:
@@ -91,36 +91,36 @@ volumes:
docker compose up -d
```
Access at: `http://localhost:3000`
访问地址:`http://localhost:3000`
Default credentials:
默认账号:
* Username: `root`
* Password: `123456`
* 用户名:`root`
* 密码:`123456`
### 2. Install Agent
### 2. 安装 Agent
Before installing an Agent, please install OpenResty on the target node first, or use the Agent Docker image with OpenResty built-in.
安装 Agent 前请先在节点上安装 OpenResty,或改用内置 OpenResty 的 Agent Docker 镜像。
You can copy the installation command from **Node Management -> Details -> Node Info -> Node Token & Deployment** in the control panel, or directly use the scripts below:
你可以在控制面板的节点管理->详情->节点信息->节点标识与部署复制安装命令,或直接使用下面的脚本:
#### Docker Deployment
#### Docker 部署
For Docker deployment, you can directly run the Agent image:
Docker 部署可直接运行 Agent 镜像:
```bash
docker pull ghcr.io/rain-kl/openflare-agent:latest
docker rm -f openflare-agent 2>/dev/null || true
docker run -d --name openflare-agent --restart unless-stopped \
-p 80:80 -p 443:443 \
-p 80:80 -p 443:443/tcp -p 443:443/udp \
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
ghcr.io/rain-kl/openflare-agent:latest
```
#### Local Installation
#### 本地部署
Using `discovery_token` to register:
使用 `discovery_token` 接入:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -128,7 +128,7 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--discovery-token YOUR_DISCOVERY_TOKEN
```
Using node-specific `agent_token`:
使用节点专属 `agent_token`:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -136,62 +136,67 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--agent-token YOUR_AGENT_TOKEN
```
The installation script defaults to `/opt/openflare-agent`, creates a `openflare-agent.service`, automatically searches for `openresty`, and can be executed repeatedly to reinstall or upgrade the Agent.
安装脚本默认写入 `/opt/openflare-agent`,创建 `openflare-agent.service`,自动查找 `openresty`,并可重复执行以重装或升级 Agent。
### 3. Uninstall Agent
### 3. 卸载 Agent
To completely uninstall the Agent and clear local data, run:
如需彻底卸载 Agent 并清空本地数据,可执行:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
```
The uninstallation script will stop and remove the `openflare-agent.service`, and delete the entire `/opt/openflare-agent` directory. It will not delete the local OpenResty installation.
卸载脚本会先停止并移除 `openflare-agent.service`、删除整个 `/opt/openflare-agent` 目录,不会删除本机 OpenResty。
### 4. Publish Your First Configuration
### 4. 发布第一份配置
1. Log in to the management panel and add a reverse proxy rule.
2. View the preview or change summary before publishing.
3. Activate the new version.
4. Agents will receive the configuration and apply it via WebSocket notification or subsequent heartbeats.
1. 登录管理端并新增反代规则
2. 在发布前查看预览或变更摘要
3. 激活新版本
4. Agent 通过 WebSocket 通知或后续 heartbeat 拉取并应用配置
The version number format is fixed as `YYYYMMDD-NNN`. Historical versions are immutable, and rollback is achieved by reactivating an older version.
版本号格式固定为 `YYYYMMDD-NNN`,历史版本不可变,回滚通过重新激活旧版本完成。
## UI Preview
### Dashboard Overview
## 界面预览
### 仪表盘总览
![OpenFlare dashboard overview](./docs/assets/readme/dashboard-overview.png)
### Node Details
### 节点详情
![OpenFlare node detail](./docs/assets/readme/node-detail.png)
### Proxy Configuration
### 配置新增
![OpenFlare version release](./docs/assets/readme/proxy-route-detail.png)
## Management Panel & API
## 管理端与接口
The management panel includes:
管理端当前覆盖:
* Reverse Proxy Rules
* Configuration Versions
* Node Management
* Application Records
* TLS Certificates
* Domain Management
* WAF Rule Groups
* User Management
* Settings
* Version Updates
* POW Rules
* 反代规则
* 配置版本
* 节点管理
* 应用记录
* TLS 证书
* 域名管理
* Pages 静态托管
* WAF 规则组
* 内网穿透(Tunnels)
* Uptime Kuma 监控同步
* SSO 登录配置
* 用户管理
* 设置
* 版本更新
* PoW 规则
After logging in to the dashboard, access Swagger UI at: `/swagger/index.html`
登录管理端后,可访问 Swagger UI:`/swagger/index.html`
## License
## 开源协议
This project is licensed under [Apache License 2.0](./LICENSE).
本项目采用 [Apache License 2.0](./LICENSE) 开源。
## Star History
-205
View File
@@ -1,205 +0,0 @@
<div align="center">
# OpenFlare
**[English](./README.md) | [📖 中文](./README.zh-CN.md)**
OpenFlare 是开源 CDN 编排与边缘安全平台。它支持反向代理、集中式配置同步、内网穿透(Tunnels)、动态 WAF 防护以及防 CC 挑战。
</div>
<p align="center">
<a href="https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/LICENSE">
<img src="https://img.shields.io/github/license/Rain-kl/OpenFlare?color=brightgreen" alt="license">
</a>
<a href="https://github.com/Rain-kl/OpenFlare/releases/latest">
<img src="https://img.shields.io/github/v/release/Rain-kl/OpenFlare?color=brightgreen&include_prereleases" alt="release">
</a>
<a href="https://github.com/Rain-kl/OpenFlare/pkgs/container/openflare">
<img src="https://img.shields.io/badge/GHCR-ghcr.io%2Frain--kl%2Fopenflare-brightgreen" alt="ghcr">
</a>
</p>
> [!WARNING]
> 使用 `root` 用户初次登录系统后,务必修改默认密码 `123456`。
>
> BETA 版本为开发测试阶段的临时产物,可能存在未知问题,请勿在生产环境使用。
## 文档
**https://open-flare.pages.dev**
常用入口:
* [快速开始](https://open-flare.pages.dev/guide/quick-start)
* [部署说明](https://open-flare.pages.dev/guide/deployment)
* [配置项参考](https://open-flare.pages.dev/reference/configuration)
* [系统设计](https://open-flare.pages.dev/design/)
## 核心能力
* **中心化实时配置同步**:通过 WebSocket 与心跳实现全网节点配置秒级同步下发与热生效,告警与状态即时回收,无须手动 SSH 登录或在线打补丁。
* **分布式 CDN 编排集群**:将分散独立的 OpenResty 节点编排为高度协同的分布式内容分发网络(CDN)舰队,支持网站级多域名聚合、上游 Keepalive 与多源站负载均衡。
* **安全内网穿透(Tunnels)**:开源版的 Cloudflare Tunnels。无须公网 IP 或暴露入向端口,安全反向穿透本地内网服务至公网。
* **边缘 WAF 安全防护**:提供全局及自定义规则组,支持 IP/CIDR 过滤、MaxMind GeoIP 国家级地域准入、IP 组成员异步差分同步免 Nginx 重载以及自定义拦截响应。
* **防 CC 与人机挑战(PoW)**:内置高性能客户端密码学 Proof of Work 挑战(类似 Turnstile),在网关边缘秒级拦截并阻断僵尸网络与爬虫。
* **发布与同步模型**:基于不可变配置版本(`YYYYMMDD-NNN`)、发布前预览对比差异、全局单激活版本与一键秒级回滚。
* **三阶段容灾回滚**:支持节点备份自动回滚、内置安全兜底页面(Port 80/503 保持状态监控与安全拦截)及异常配置阻断名单。
* **证书托管自动化**:支持证书动态上传、多域名证书自动匹配绑定、ACME 自动续期与全生命周期状态追踪。
* **统一观测与可观测性**:聚合节点请求数、实时访问分析、宿主机与 Nginx 资源快照、健康日志及网络波动补传缓冲。
## 快速开始
### 1. 启动 Server
```yaml
services:
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: openflare
POSTGRES_USER: openflare
POSTGRES_PASSWORD: replace-with-strong-password
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U openflare -d openflare"]
interval: 10s
timeout: 5s
retries: 5
openflare:
image: ghcr.io/rain-kl/openflare:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "3000:3000"
environment:
SESSION_SECRET: replace-with-random-string
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
GIN_MODE: release
LOG_LEVEL: info
volumes:
postgres-data:
```
```bash
docker compose up -d
```
访问地址:`http://localhost:3000`
默认账号:
* 用户名:`root`
* 密码:`123456`
### 2. 安装 Agent
安装 Agent 前请先在节点上安装 OpenResty,或改用内置 OpenResty 的 Agent Docker 镜像。
你可以在控制面板的节点管理->详情->节点信息->节点标识与部署复制安装命令,或直接使用下面的脚本:
#### Docker 部署
Docker 部署可直接运行 Agent 镜像:
```bash
docker pull ghcr.io/rain-kl/openflare-agent:latest
docker rm -f openflare-agent 2>/dev/null || true
docker run -d --name openflare-agent --restart unless-stopped \
-p 80:80 -p 443:443/tcp -p 443:443/udp \
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
ghcr.io/rain-kl/openflare-agent:latest
```
#### 本地部署
使用 `discovery_token` 接入:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
--server-url http://your-server:3000 \
--discovery-token YOUR_DISCOVERY_TOKEN
```
使用节点专属 `agent_token`:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
--server-url http://your-server:3000 \
--agent-token YOUR_AGENT_TOKEN
```
安装脚本默认写入 `/opt/openflare-agent`,创建 `openflare-agent.service`,自动查找 `openresty`,并可重复执行以重装或升级 Agent。
### 3. 卸载 Agent
如需彻底卸载 Agent 并清空本地数据,可执行:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
```
卸载脚本会先停止并移除 `openflare-agent.service`、删除整个 `/opt/openflare-agent` 目录,不会删除本机 OpenResty。
### 4. 发布第一份配置
1. 登录管理端并新增反代规则
2. 在发布前查看预览或变更摘要
3. 激活新版本
4. Agent 通过 WebSocket 通知或后续 heartbeat 拉取并应用配置
版本号格式固定为 `YYYYMMDD-NNN`,历史版本不可变,回滚通过重新激活旧版本完成。
## 界面预览
### 仪表盘总览
![OpenFlare dashboard overview](./docs/assets/readme/dashboard-overview.png)
### 节点详情
![OpenFlare node detail](./docs/assets/readme/node-detail.png)
### 配置新增
![OpenFlare version release](./docs/assets/readme/proxy-route-detail.png)
## 管理端与接口
管理端当前覆盖:
* 反代规则
* 配置版本
* 节点管理
* 应用记录
* TLS 证书
* 域名管理
* WAF 规则组
* 用户管理
* 设置
* 版本更新
* POW 规则
登录管理端后,可访问 Swagger UI:`/swagger/index.html`
## 开源协议
本项目采用 [Apache License 2.0](./LICENSE) 开源。
## Star History
<a href="https://www.star-history.com/?repos=Rain-kl%2FOpenFlare&type=date&legend=bottom-right">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
</picture>
</a>
+5 -3
View File
@@ -2,7 +2,7 @@ services:
agent:
build:
context: .
dockerfile: openflare_agent/Dockerfile
dockerfile: openflare-agent/Dockerfile
container_name: openflare-agent
restart: unless-stopped
@@ -12,7 +12,7 @@ services:
- "127.0.0.1:18081:18081"
volumes:
- ./openflare_agent/data/:/data
- ./openflare-agent/data/:/data
environment:
OPENFLARE_SERVER_URL: "http://host.docker.internal:3000"
@@ -25,10 +25,12 @@ services:
relay:
build:
context: .
dockerfile: openflare_relay/Dockerfile
dockerfile: openflare-relay/Dockerfile
container_name: openflare-relay
network_mode: host
restart: unless-stopped
volumes:
- ./openflare-relay/data/:/app/data
environment:
OPENFLARE_SERVER_URL: http://host.docker.internal:3000
OPENFLARE_DISCOVERY_TOKEN: 85464eeb72c49abc430569d6b9c77f78
+3 -1
View File
@@ -12,7 +12,9 @@ export default defineConfig({
srcExclude: [
'zh/**',
'components/**',
'snippets/**'
'snippets/**',
'plan/**',
'guideline/**'
],
markdown: {
+26
View File
@@ -20,8 +20,34 @@ sidebar: false
### 新增
- 新增密码登录人机验证(基于 Proof-of-Work 和无感浏览器检测的 Cap 验证码防护)
- 新增后端 PoW 校验服务,实现 FNV-1a/XORShift PRNG 难题生成、验证及 JWT 难题校验算法
- 新增线程安全的内存 TTL 核销缓存,支持高并发与 Single-use 难题令牌防重放
- 新增 Gin 拦截中间件与登录路由 `X-Cap-Token` 自动校验,支持从 HTTP 请求头验证并放行
- 前端登录页集成 cap-widget 组件,按需加载 CDN 脚本,实现静默 PoW 求解与令牌提交
- 管理后台系统设置页“登录与注册开关”中新增“启用登录人机验证”开关,支持热更新全局防护状态
- 新增 Agent 交互式安装向导,支持选择本地安装和 Docker 运行模式;未传参数时自动进入交互菜单
- 新增 Docker 运行模式的智能环境检查,检测到未安装 Docker 时支持一键在线安装,中国大陆环境支持多镜像源自动测速优选与加速器配置
- 新增 Agent 交互式卸载向导,支持选择本地卸载和 Docker 容器卸载模式;未传参数时自动进入交互菜单
- 新增 Pages 静态托管使用指南(`pages-usage.md`),讲解 ZIP 上传、SPA Fallback 与 API 代理配置
- 新增 Uptime Kuma 监控同步集成指南(`uptime-kuma.md`),说明同步参数与专属标签隔离机制
- 完善 WAF IP 组订阅模式使用指南(`waf-usage.md`),补充 JSON 路径提取映射规则与同步参数说明
### 修复
- 修复由于前端验证码组件未携带 `scope` 导致的登录验证码校验失败或过期的问题;通过引入路径参数化路由 `/api/cap/:scope/...` 支持在不同流程中隔离与核销特定 scope 的人机验证难题
### 变更
- 重构 `install-agent.sh` 安装脚本与 `uninstall-agent.sh` 卸载脚本以兼容交互式导引、非交互式命令行参数及 Docker 部署/卸载参数(`--docker`/`--method docker`)
- 重构 Go 包依赖结构为统一模块(Monorepo),模块命名为 `github.com/rain-kl/openflare`
- 移除各子目录下独立的 `go.mod`/`go.sum` 文件,统一由根目录 `go.mod` 进行全局依赖管理与依赖版本锁定
- 替换全仓库 Go源文件中的内部引用路径,由本地相对路径迁移为标准 GitHub 绝对导入路径
- 适配 Docker 镜像构建,所有组件镜像的 Dockerfile 调整为基于根目录的上下文编译
- 更新 GitHub release 自动化发布流水线,适配全新 monorepo 包结构与符号信息注入路径
- 简化并重构数据库历史迁移校验逻辑,将版本 2 至 6 的中间校验函数合并到基线校验函数 `validateDatabaseSchemaV7` 中,消除冗余代码
- 重构数据库历史迁移校验架构,引入基于 GORM 反射解析(`schema.Parse`)的通用自动表结构校验,彻底废弃老版本中大量手动编写的 `HasTable`/`HasColumn` 结构字段存在性检测代码
---
## [v2.3.2] - 2026-06-04
+28 -11
View File
@@ -1,4 +1,4 @@
import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
import {type DefaultTheme, defineAdditionalConfig} from 'vitepress'
export default defineAdditionalConfig({
description:
@@ -10,8 +10,9 @@ export default defineAdditionalConfig({
sidebar: {
'/guide/': { base: '/guide/', items: sidebarGuide() },
'/reference/': { base: '/reference/', items: sidebarReference() },
'/deployment/': { base: '/deployment/', items: sidebarDeployment() },
'/design/': { base: '/design/', items: sidebarDesign() },
'/changelog/': false
'/changelog/': { base: '/changelog/', items: [] }
},
editLink: {
@@ -57,6 +58,7 @@ export default defineAdditionalConfig({
function nav(): DefaultTheme.NavItem[] {
return [
{ text: '指南', link: '/guide/', activeMatch: '/guide/' },
{ text: '部署', link: '/deployment/', activeMatch: '/deployment/' },
{ text: '参考', link: '/reference/', activeMatch: '/reference/' },
{ text: '设计', link: '/design/', activeMatch: '/design/' },
{ text: '更新日志', link: '/changelog/', activeMatch: '/changelog/' }
@@ -70,10 +72,12 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
items: [
{ text: '概览', link: '' },
{ text: '快速开始', link: 'quick-start' },
{ text: '基础使用', link: 'usage' },
{ text: '新建反代配置', link: 'proxy-config' },
{ text: 'Pages 静态托管使用', link: 'pages-usage' },
{ text: '内网穿透与隧道使用', link: 'tunnel-usage' },
{ text: 'WAF 安全防护使用', link: 'waf-usage' },
{ text: 'WAF 自动 IP 组语法', link: 'waf-ip-group-expr' },
{ text: 'Uptime Kuma 监控同步', link: 'uptime-kuma' },
{ text: 'SSO 登录配置', link: 'sso' },
{ text: '发布第一份配置', link: 'first-site' },
{ text: '故障排查', link: 'troubleshooting' },
@@ -89,13 +93,6 @@ function sidebarReference(): DefaultTheme.SidebarItem[] {
text: '参考',
items: [
{ text: '概览', link: '' },
{ text: '系统架构', link: '../design/architecture' },
{ text: '启动 Server', link: '../deployment/server' },
{ text: '接入 Agent', link: '../deployment/agent' },
{ text: '部署说明', link: '../deployment/deployment' },
{ text: '部署 Relay (Tunnel)', link: '../deployment/relay' },
{ text: '部署 OpenFlared', link: '../deployment/openflared' },
{ text: '升级与维护', link: '../deployment/upgrade' },
{ text: '配置项', link: 'configuration' },
{ text: '命令与脚本', link: 'cli' },
{ text: 'API 约定', link: 'api' }
@@ -104,6 +101,23 @@ function sidebarReference(): DefaultTheme.SidebarItem[] {
]
}
function sidebarDeployment(): DefaultTheme.SidebarItem[] {
return [
{
text: '部署',
items: [
{ text: '概览', link: '' },
{ text: '部署说明', link: 'deployment' },
{ text: '启动 Server', link: 'server' },
{ text: '接入 Agent', link: 'agent' },
{ text: '部署 Relay (Tunnel)', link: 'relay' },
{ text: '部署 OpenFlared', link: 'openflared' },
{ text: '升级与维护', link: 'upgrade' }
]
}
]
}
function sidebarDesign(): DefaultTheme.SidebarItem[] {
return [
{
@@ -114,8 +128,11 @@ function sidebarDesign(): DefaultTheme.SidebarItem[] {
{ text: 'Agent 与发布模型', link: 'agent-design' },
{ text: '内网穿透隧道设计', link: 'tunnel-design' },
{ text: 'WAF 设计', link: 'waf-design' },
{ text: '仓库结构', link: 'repository' }
{ text: 'Pages 静态托管设计', link: 'pages-design' },
{ text: 'Uptime Kuma 监控同步设计', link: 'kuma-design' },
{ text: '登录验证码设计', link: 'login-captcha' }
]
}
]
}
+56 -13
View File
@@ -20,7 +20,19 @@ OpenFlare Agent 运行在代理节点侧。它不会接收远程 shell 指令,
## 一键安装
使用 `discovery_token`:
### 交互式安装 (推荐)
如果在不传递任何参数的情况下运行安装脚本,脚本将进入交互模式。您将可以通过向导选择安装方式(本地运行 / Docker 容器运行),并配置 Server 地址与认证 Token(若选择 Docker 方式且本地没有 Docker,脚本还会询问并智能安装 Docker):
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash
```
### 自动化 (非交互式) 安装
如果在执行脚本时附加了任何参数,脚本将进入自动化安装模式,不需要任何交互。
使用 `discovery_token` 进行本地安装:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -28,7 +40,7 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--discovery-token YOUR_DISCOVERY_TOKEN
```
使用节点专属 `agent_token`:
使用节点专属 `agent_token` 进行本地安装:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -36,19 +48,30 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--agent-token YOUR_AGENT_TOKEN
```
安装脚本会下载最新 Agent,默认写入 `/opt/openflare-agent`,生成 `agent.json`,并在 Linux + systemd 环境创建 `openflare-agent.service`。
使用 Docker 容器自动化安装:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
--server-url http://your-server:3000 \
--discovery-token YOUR_DISCOVERY_TOKEN \
--docker
```
安装脚本在本地安装模式下会下载最新 Agent,默认写入 `/opt/openflare-agent`,生成 `agent.json`,并在 Linux + systemd 环境创建 `openflare-agent.service`。
支持参数:
| 参数 | 说明 |
| --- | --- |
| `--server-url` | Server 地址,必填 |
| `--server-url` | Server 地址 |
| `--discovery-token` | 首次自动注册 Token |
| `--agent-token` | 节点专属 Token |
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent` |
| `--openresty-path` | OpenResty 二进制路径,未传时自动查找 `openresty` |
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent`(仅本地安装生效) |
| `--openresty-path` | OpenResty 二进制路径,未传时自动查找 `openresty`(仅本地安装生效) |
| `--repo` | 下载 Agent 的 GitHub 仓库,默认 `Rain-kl/OpenFlare` |
| `--no-service` | 不创建 systemd 服务 |
| `--no-service` | 不创建 systemd 服务(仅本地安装生效) |
| `--docker` | 使用 Docker 容器方式安装 |
| `--method` | 安装方式,可选 `local` 或 `docker`(默认 `local`) |
## 配置文件
@@ -126,7 +149,7 @@ journalctl -u openflare-agent -f
源码运行:
```bash
cd openflare_agent
cd openflare-agent
export LOG_LEVEL='info'
go run ./cmd/agent -config /path/to/agent.json
```
@@ -134,7 +157,7 @@ go run ./cmd/agent -config /path/to/agent.json
编译后二进制运行:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
export LOG_LEVEL='info'
./openflare-agent -config /path/to/agent.json
@@ -150,20 +173,40 @@ export LOG_LEVEL='info'
## 卸载
如需彻底卸载 Agent 并清空本地数据:
### 交互式卸载 (推荐)
如果在不传递任何参数的情况下运行卸载脚本,脚本将进入交互模式。您可以通过提示菜单选择卸载方式(本地卸载 / Docker 容器卸载):
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
```
### 自动化 (非交互式) 卸载
使用命令行传参进行无人值守卸载。
本地卸载(默认):
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash -s -- --install-dir /opt/openflare-agent
```
Docker 容器卸载:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash -s -- --docker
```
支持参数:
| 参数 | 说明 |
| --- | --- |
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent` |
| `--service-name` | systemd 服务名,默认 `openflare-agent` |
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent`(仅本地卸载生效) |
| `--service-name` | systemd 服务名,默认 `openflare-agent`(仅本地卸载生效) |
| `--docker` | 使用 Docker 容器方式卸载 |
| `--method` | 卸载方式,可选 `local` 或 `docker`(默认 `local`) |
卸载脚本只移除 Agent 服务、进程和安装目录,不会删除本机 OpenResty。
本地卸载只会移除 Agent 服务、进程和安装目录,不会删除本机 OpenResty。Docker 卸载会停止并删除 `openflare-agent` 容器,交互模式下还可以选择是否清理对应的 Docker 镜像。
## 常见问题
+4 -4
View File
@@ -134,7 +134,7 @@ docker compose logs -f openflare
先构建管理端前端:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm build
@@ -143,7 +143,7 @@ pnpm build
再启动 Server:
```bash
cd openflare_server
cd openflare-server
export JWT_SECRET='replace-with-a-long-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -230,7 +230,7 @@ journalctl -u openflare-agent -f
源码运行:
```bash
cd openflare_agent
cd openflare-agent
export LOG_LEVEL='info'
go run ./cmd/agent -config /path/to/agent.json
```
@@ -238,7 +238,7 @@ go run ./cmd/agent -config /path/to/agent.json
编译后二进制运行:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
export LOG_LEVEL='info'
./openflare-agent -config /path/to/agent.json
+3 -3
View File
@@ -1,6 +1,6 @@
# 部署 Relay (Tunnel 中继)
你会学到:TunnelRelay 节点的职责、`openflare_relay` 的配置项与环境变量、使用 Docker 运行 Relay 的方法,以及如何通过源码手动构建并部署 Relay。
你会学到:TunnelRelay 节点的职责、`openflare-relay` 的配置项与环境变量、使用 Docker 运行 Relay 的方法,以及如何通过源码手动构建并部署 Relay。
在 OpenFlare 的内网穿透体系中,**TunnelRelay 节点** 扮演着关键的角色。它与普通的边缘节点(Edge Node)不同,除了运行传统的 Agent(托管 OpenResty 进行 HTTPS/WAF 处理)外,还同机运行了 **Relay (frps 隧道管理器)** 服务,负责监听内网客户端(OpenFlared)的隧道连接并进行流量中继。
@@ -21,7 +21,7 @@
## 配置文件与环境变量
`openflare_relay` 启动时默认会读取当前目录下的 `relay.json`。同时也完全支持通过环境变量进行覆盖。
`openflare-relay` 启动时默认会读取当前目录下的 `relay.json`。同时也完全支持通过环境变量进行覆盖。
### 配置字段详情
@@ -68,7 +68,7 @@ docker run -d --name openflare-relay --restart unless-stopped \
### 1. 编译二进制
```bash
cd openflare_relay
cd openflare-relay
go build -o openflare-relay ./cmd/relay
```
+4 -4
View File
@@ -17,10 +17,10 @@ OpenFlare Server 是 Gin + GORM 单体控制面,负责管理端 UI、管理 AP
## 构建管理端前端
Go Server 会托管 `openflare_server/web/build` 中的静态产物。源码启动前先构建前端:
Go Server 会托管 `openflare-server/web/build` 中的静态产物。源码启动前先构建前端:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm build
@@ -37,7 +37,7 @@ pnpm test
## 使用 SQLite 启动
```bash
cd openflare_server
cd openflare-server
export JWT_SECRET='replace-with-a-long-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -53,7 +53,7 @@ http://localhost:3000
## 使用 PostgreSQL 启动
```bash
cd openflare_server
cd openflare-server
export JWT_SECRET='replace-with-a-long-random-string'
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
export LOG_LEVEL='info'
+109 -179
View File
@@ -1,244 +1,174 @@
# 系统架构
你会学到:OpenFlare 的整体架构、Server、Agent、OpenResty 与管理端前端的职责边界,以及一次配置发布从管理端到节点生效的请求流。
你会学到:OpenFlare 的整体架构、各核心组件(Server, Agent, OpenResty, Relay, Client)的职责分工,以及主要数据与请求流的宏观流向。
OpenFlare 由 Server、Agent、节点本地 OpenResty 和管理端前端组成。Server 是控制面,Agent 是节点侧唯一受控落地入口,OpenResty 是实际数据面。内网穿透场景中,Relay(frps 管理器)和 OpenFlared(frpc 管理器)扩展了数据面流量路径。
OpenFlare 是一套自托管的 OpenResty 控制面。它在物理上由 Server(控制面)、Agent(配置落地端)、节点本地 OpenResty(数据面)、内网穿透组件(Relay 与 OpenFlared,数据面扩展)以及管理端前端组成。
### 标准反代流量路径
---
## 流量路径概览
根据不同的网站上游类型,OpenFlare 支持三种不同的数据面流量路径:
### 1. 标准反代流量路径
```text
Browser
|
| Management UI / API
| HTTPS/HTTP request
v
OpenFlare Server (Gin + GORM + SQLite/PostgreSQL)
OpenResty (WAF, TLS, Rate Limit)
|
| Agent API / heartbeat / config pull
| reverse proxy (proxy_pass)
v
OpenFlare Agent
|
| write config / openresty -t / reload / rollback
v
OpenResty binary
|
| reverse proxy
v
Origin
Origin Server (直连公网/局域网上游)
```
### 内网穿透流量路径
### 2. 内网穿透流量路径
适用于内网受限服务器上的源站服务接入:
```text
Browser
|
| HTTPS request
| HTTPS/HTTP request
v
OpenResty (Agent, TLS/WAF) <-- TunnelRelay 节点
OpenResty (Agent 宿主机, TLS/WAF)
|
| proxy_pass http://localhost:vhost_port (Host header preserved)
v
OpenFlareRelay (frps) <-- TunnelRelay 节点,与 Agent 同机部署
OpenFlareRelay (frps) <-- 与 Agent 同机部署,提供中继
|
| frp tunnel protocol (HTTP Vhost routing by Host header)
| frp tunnel protocol (Host header routing)
v
OpenFlared (frpc) <-- 内网服务器
OpenFlared (frpc) <-- 内网受限服务器
|
| HTTP/HTTPS forward
v
Internal Service (192.168.x.x)
```
### Pages 静态托管流量路径
### 3. Pages 静态托管流量路径
适用于预构建的单页应用(SPA)或静态网站托管:
```text
Browser
|
| HTTPS request
| HTTPS/HTTP request
v
OpenResty (Agent, TLS/WAF)
|
| root/try_files
v
Agent 本地 Pages 部署目录
+---> [静态服务] root/try_files ---> Agent 本地 Pages 部署目录
|
+---> [API 反代] proxy_pass ---> 后端 API 服务 (如果启用了 API 代理)
```
---
## 组件职责
| 组件 | 职责 |
| --------------- | ---------------------------------------------------------------------- |
| Server | 管理端 UI、管理 API、Agent/Relay/Client API、配置渲染、版本发布、Pages 部署包存储、数据存储与聚合查询 |
| Agent | 注册、心跳、同步、写入文件、Pages 部署包拉取与解压、校验、reload、失败回滚、自更新与轻量采集 |
| OpenResty | 接收真实流量,按 OpenFlare 渲染的配置执行 WAF、PoW、认证、反向代理与 Pages 静态文件服务 |
| OpenFlareRelay | 管理 frps 进程生命周期,提供隧道中继服务,通过心跳接收 frps 配置 |
| OpenFlared | 管理 frpc 进程(可多个),连接 Relay 中继,将流量转发到内网服务 |
| Frontend | 管理网站配置、WAF、源站、证书、节点、Tunnel、版本、用户、设置与观测页面 |
| 组件 | 职责 | 详细设计参考 |
| --------------- | ---------------------------------------------------------------------- | ------------ |
| **Server** | 管理端 UI/API、控制面状态持久化、配置编译渲染、发布版本控制、Pages 部署包存储、Uptime Kuma 监控同步与登录验证码防护 | [Agent 与发布模型](./agent-design.md) / [Uptime Kuma 监控同步设计](./kuma-design.md) / [登录验证码设计](./login-captcha.md) |
| **Agent** | 周期心跳与 WS 同步、静态资源包拉取与解压、OpenResty 配置写入/校验/重载与自愈 | [Agent 与发布模型](./agent-design.md) |
| **OpenResty** | 接收真实流量,执行 WAF 过滤、PoW 防护、Basic Auth 认证与静态/反代服务 | [WAF 设计](./waf-design.md) / [Pages 设计](./pages-design.md) |
| **Relay** | 部署于边缘节点,管理 `frps` 守护进程生命周期,接受心跳派发的穿透中继配置 | [内网穿透设计](./tunnel-design.md) |
| **OpenFlared** | 部署于内网,管理 `frpc` 进程组,向多个 Relay 建立反向隧道,上报连接状态 | [内网穿透设计](./tunnel-design.md) |
| **Frontend** | Next.js 管理界面,提供路由、WAF、证书、节点、穿透隧道和 Pages 项目的可视化管理 | [开发约束](../guideline/Constraints.md) |
## Server
---
`openflare_server` 是单体控制面:
## 组件架构与分工
* Gin 提供 HTTP 服务。
* GORM 访问 SQLite 或 PostgreSQL。
* 现有登录体系签发管理端用户 Token,管理端 API 通过 `OPENFLARE_TOKEN` 请求头鉴权。
* 认证源与外部账号绑定支持 GitHub OAuth 和标准 OIDC。
* Go Server 托管 `openflare_server/web` 静态构建产物。
### 1. Server (控制面)
`openflare-server` 是 Go 编写的单体控制面:
* 提供管理端 REST API,通过 `OPENFLARE_TOKEN` 请求头鉴权。
* 包含配置编译器(Compiler),将数据库中的规则、证书与全局参数统一编译为不可变的配置快照及 OpenResty 物理配置文件文本。
* 存储 Pages 部署 ZIP 包于本地 Artifacts 目录,并向 Agent 提供受控的下载接口。
* 后台集成 Uptime Kuma 监控同步服务,自动为可用站点维护 HTTP 探测任务。
* *详细设计请参阅:[Agent 与发布模型设计](./agent-design.md) 以及 [Uptime Kuma 监控同步设计](./kuma-design.md)*
Server 不直接 SSH 到节点,也不在线修改节点文件。它只保存控制面状态、生成完整配置版本,并通过 Agent API 让节点主动拉取。
### 2. Agent (配置落地端)
`openflare-agent` 是运行在节点本地的守护进程:
* 启动后维持与控制面的周期性心跳,并通过可选的 WebSocket 接收实时的配置发布广播。
* 负责拉取最新激活版本的配置文件及证书,写入本地目录,并通过 `openresty -t` 执行安全校验后平滑重载 (`reload`)。
* 在本地处理 Pages 部署包的下载、SHA-256 校验与解压缩切换。
* *详细设计请参阅:[Agent 与发布模型设计](./agent-design.md)*
Pages 静态托管场景中,Server 保存 Pages 项目、SPA fallback 回退路径、不可变部署元数据、文件清单和 zip 部署包;发布版本只记录部署引用、checksum 与静态渲染策略,不把大体积静态资源写入 `config_versions`。
### 3. OpenResty (数据面)
接收访客流量并执行最终的业务落地:
* 流量入口,支持 HTTP/2、HTTP/3(QUIC)和 TLS 证书动态绑定。
* 嵌入 Lua 逻辑,在 `access_by_lua` 阶段高效过滤 WAF 规则、验证工作量证明 (PoW) 挑战,并在此之后执行连接数/速率限制及基础缓存。
* *详细设计请参阅:[WAF 设计文档](./waf-design.md) 与 [Pages 静态托管设计文档](./pages-design.md)*
## Agent
### 4. Relay 与 OpenFlared (穿透组件)
扩展数据面反穿透能力:
* `openflare-relay` 守护本地 `frps`,接受 Server 的配置派发,自动更新中继端口。
* `openflared` 在内网守护一组 `frpc` 客户端进程,实现多中继就近建连与高可用容灾。
* *详细设计请参阅:[内网穿透隧道设计文档](./tunnel-design.md)*
`openflare_agent` 是 Go 单体程序:
---
* 单二进制运行在节点侧。
* 启动后读取或生成本地节点信息。
* 周期性 heartbeat,上报状态并获取激活版本摘要。
* 发现新版本后拉取配置、备份旧文件、写入新文件、校验并 reload。
* 当激活配置引用 Pages 部署时,先按部署 ID 下载 zip 包,校验 checksum,解压到本地 `pages_dir` 并切换当前部署目录。
* 应用失败时尝试恢复运行并回滚。
* 维护 WAF GeoIP mmdb,启动时写入内置初始库,并按配置定期更新。
Agent 通过 `openresty_path` 指向的 OpenResty 二进制统一执行校验、reload、启动与重启;未配置时默认调用 `openresty`。Docker 部署时,Agent 镜像内置 OpenResty 二进制,仍走同一套二进制控制逻辑。
节点 IP 默认由 Agent 注册和心跳上报维护;如果管理端锁定节点 IP,Server 只更新运行状态、版本、观测等运行态字段,不再接受 Agent 上报覆盖该 IP。
## Frontend
`openflare_server/web` 是正式管理端前端:
* Next.js 15 App Router。
* React 19。
* TypeScript。
* Tailwind CSS。
* TanStack Query 管理服务端状态。
前端采用静态导出模式(`output: 'export'`),导出后由 Go Server 通过 `embed.FS` 托管。所有 API 请求应统一经过 `lib/api/`,并处理 `success/message/data` 响应结构。
Server 集成以下安全特性:
* CORS 中间件:跨域请求保护。
* 速率限制:全局与关键接口限流。
* 会话管理:基于 Cookie/Redis 的会话存储。
## 数据与请求流
### 管理端请求流
## 数据与请求流概览
### 1. 配置发布与同步流
```text
Browser -> Frontend -> /api/* -> controller -> service -> model -> database
管理端修改配置 -> 发布新版本 -> 生成全局唯一 Checksum 激活版本
|
+------------------+------------------+
| (WebSocket 广播或周期 Heartbeat) |
v v
[边缘节点 Agent] [内网 OpenFlared]
拉取最新 OpenResty 配置/证书 拉取最新 Tunnel 映射配置
增量拉取/解压 Pages 静态部署包 生成/重写 frpc.toml
Nginx 校验配置并平滑重载 (reload) 平滑重载或拉起 frpc 进程
上报应用状态 (Success / Error) 上报隧道连接状态与活跃指标
```
* *同步与自愈的精细时序及回滚模型详见:[Agent 与发布模型设计](./agent-design.md)*
管理端变更类接口使用 `POST`,只读接口使用 `GET`。成功与失败都返回清晰的 `message`。
### 2. 静态托管与 API 代理流
* 静态资源解压落地于 Agent 节点的 `deployments/{id}/current` 下,OpenResty 通过 `root`/`index`/`try_files` 指令在边缘直接向访客提供极低延迟的静态资源服务。
* 当启用 API 代理时,OpenResty 自动根据站点配置的 `api_proxy_path`(如 `/api`)将 API 请求重写并转发(`proxy_pass`)给后端动态接口。
* *部署包校验、解压逃逸防御及 Nginx 规则渲染详见:[Pages 静态托管设计文档](./pages-design.md)*
### Agent 同步流
### 3. WAF 安全过滤流
* WAF 引擎嵌入在 OpenResty 请求生命周期中。
* 过滤规则直接从 Agent 落地在节点本地的 `waf_config.json` 及 `waf_ip_groups.json` 读取,判决逻辑白名单优先、黑名单层层过滤,完全在本地内存中完成,不产生数据库或网络 I/O 损耗。
* *IP组增量同步、自动 IP 组计算与拦截响应机制详见:[WAF 设计文档](./waf-design.md)*
```text
Agent HTTP heartbeat -> Server 返回激活版本摘要
Agent 发现新版本 -> 拉取配置详情
Agent 确保 Pages 部署包已下载、校验并解压 (如配置引用 Pages)
Agent 写入主配置 / 路由配置 / 证书 / Lua 资源 / WAF 运行时配置
Agent 执行 OpenResty 校验与 reload
Agent 上报应用结果
```
### Relay 同步流
Relay(OpenFlareRelay 进程)运行在 TunnelRelay 节点上,与 Agent 共享同一 `agent_token`:
```text
Relay HTTP heartbeat -> Server 返回 frps 基础配置 (bindPort, vhostHTTPPort, auth_token)
Relay 生成 frps.toml 并启动或更新 frps 进程
Relay 定期上报 frps 健康状态与连接统计
Relay 尝试升级 WebSocket 连接以支持实时配置推送
```
frps 配置相对静态(端口、认证 Token),通过心跳下发,**不纳入版本化发布流**。Relay 需要监听 frps 进程异常并自动恢复。认证方式:`X-Agent-Token` + API 路径前缀 `/api/relay/*`,Server 通过 `node_type = tunnel_relay` 区分。
### OpenFlared 同步流
OpenFlared(客户端)运行在内网服务器,使用独立的 `tunnel_token` 认证:
```text
Client HTTP heartbeat -> Server 返回 tunnel 配置版本摘要 (version, checksum)
Client 发现新版本 -> 拉取完整 tunnel 路由配置 (relay 列表 + frpc proxy 定义)
Client 为每个 Relay 生成独立的 frpc.toml 配置文件
Client 为新 Relay 启动 frpc 进程,或为已有 Relay 执行热重载 (frpc reload)
Client 上报应用结果 (成功/失败原因)
```
OpenFlared 通过 `/api/flared/*` 端点与 Server 通信,认证使用 `X-Tunnel-Token`。Tunnel 路由配置随发布流程版本化同步,所有配置变更通过单一版本号关联并一致性发布到 Agent 和 Client。
**WebSocket 升级流程**(可选,通过 `AgentWebsocketUpgradeEnabled` 选项控制):
当启用 WebSocket 升级时:
1. Agent 通过 HTTP heartbeat 获取运行配置与设置。
2. Agent 尝试升级连接到 `GET /api/agent/ws`(WebSocket)。
3. WS 连接成功后,周期性状态上报和实时消息由 WebSocket 承载,降低延迟。
4. Server 发布或激活版本后,可向已连接 Agent 立即广播激活版本摘要,使 Agent 立即进入同步流程。
5. 若 WebSocket 断开或建立失败,Agent 自动降级回 HTTP heartbeat,保证可用性。
通过 `OpenRestyWebsocketEnabled` 选项,可在 OpenResty 层面启用或禁用 WebSocket 反向代理支持。
### 反向代理流
```text
Client -> OpenResty server block -> WAF Lua -> named upstream -> Origin
```
网站配置是反向代理聚合边界。一条网站配置可绑定多个域名,并共享站点级流量限制、反向代理和缓存配置。
WAF 在 OpenResty `access_by_lua_file` 阶段执行。规则来自当前激活版本携带的 `waf_config.json`,全局规则组默认生效,网站可叠加自定义规则组。`waf_config.json` 只保存规则组直接 IP 和 IP 组引用 ID;IP 组成员由 Agent 独立同步到本地 `waf_ip_groups.json`,OpenResty Lua 按引用 ID 合并判断。
WAF IP 组由 Server 管理。手动 IP 组直接保存 IP/IP 段列表;自动 IP 组由 Server 定时任务读取请求日志、按单个 IP 聚合指标并执行 Expr 规则;订阅 IP 组由 Server 定时任务同步远程文本或 JSON 源。Agent 心跳会上报本地 IP 组 checksum,Server 只返回不一致的 IP 组;Server 侧 IP 组更新时会通过 Agent WebSocket 广播变更组。OpenResty Lua 只读取 Agent 落地的运行时 JSON,不直接访问 Server 数据库、请求日志或远程订阅源。
---
## 核心对象
当前有效实体包括:
当前系统核心实体包括:
* `proxy_routes`
* `origins`
* `config_versions`
* `pages_projects`
* `pages_deployments`
* `pages_deployment_files`
* `nodes`
* `tunnels`
* `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`
* `waf_rule_groups`
* `waf_ip_groups`
* `waf_rule_group_bindings`
* `acme_accounts`
* `dns_accounts`
* `geoip_update_configs`
* **反代与配置**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
* **Pages 静态托管**:`pages_projects` (Pages项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
* **节点与穿透**:`nodes` (节点), `tunnels` (隧道客户端), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
* **WAF 与安全**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
* **系统与账号**:`acme_accounts` (ACME账户), `dns_accounts` (DNS账户), `geoip_update_configs` (GeoIP更新配置).
---
## 关键设计决策
| 决策 | 原因 |
| ------------------------------ | --------------------------------------------------------------------------- |
| 完整配置版本,而不是在线 patch | 让预览、激活、历史和回滚有稳定边界 |
| Agent 主动拉取 | Server 不需要 SSH 权限,也不暴露远程命令入口;支持 HTTP 与 WebSocket 双协议 |
| 全局单激活版本 | 降低 MVP 复杂度,保证所有节点默认一致;支持版本预览、历史查询与一键回滚 |
| 网站配置聚合多域名 | 支持一个业务站点共享站点级策略,同时允许按域名绑定证书 |
| 观测数据服务端聚合 | 避免前端临时统计造成口径不一致 |
| 内网穿透基于 frp 整合 | 复用成熟隧道协议,避免自研隧道的稳定性风险;frps HTTP Vhost 路由天然适配 |
| Relay/Client 独立二进制 | 职责分离,Relay 管理 frps,Client 管理 frpc,各自独立升级和部署 |
| Tunnel 与 Node 体系分离 | Tunnel 客户端在内网运行,与公网节点概念不同,使用独立的注册和认证体系 |
| 完整配置版本,而不是在线 patch | 让预览、激活、历史和回滚有稳定边界,保证节点状态一致 |
| Agent 主动拉取 | Server 不需要 SSH 权限,降低安全风险;支持 HTTP 与 WebSocket 双协议灵活切换 |
| 全局单激活版本 | 降低控制面复杂度,保证所有节点默认一致;提供一键秒级回滚的稳定机制 |
| 网站配置聚合多域名 | 支持单个业务站点共享站点级策略,同时支持按域名灵活绑定不同的 TLS 证书 |
| 内网穿透基于 frp 整合 | 复用成熟隧道协议,避免自研隧道引起稳定性风险;其 Vhost 机制天然适配反代路由 |
| 运行时配置与控制库解耦 | 如 WAF 运行时只读取本地 JSON 规则包,配置变更通过差分广播或快速重载热生效 |
---
## 贡献者阅读建议
如果要修改架构相关代码,先阅读:
修改系统架构或开发新功能前,请按以下顺序阅读:
1. [产品边界](./index.md)
2. [Agent 与发布模型](./agent-design.md)
3. [开发约束](../guildline/development-constraints.md)
4. [仓库结构](./repository.md)
1. **[产品边界](./index.md)**:了解 OpenFlare 核心定位与不允许逾越的设计边界。
2. **[开发约束](../guideline/Constraints.md)**:掌握数据模型、API 约定、数据库迁移(Goose)与前端规范。
3. **[Agent 与发布模型](./agent-design.md)**:理解版本快照同步及失败回滚的安全兜底逻辑。
4. **细分领域设计**:
* 穿透相关开发:阅读 [内网穿透隧道设计](./tunnel-design.md)。
* WAF 相关开发:阅读 [WAF 设计](./waf-design.md)。
* Pages 托管开发:阅读 [Pages 静态托管设计](./pages-design.md)。
* 监控同步开发:阅读 [Uptime Kuma 监控同步设计](./kuma-design.md)。
5. **[仓库结构](./index.md#仓库结构)**:明确各个物理目录分层职责,避免堆砌和重复开发。
-182
View File
@@ -1,182 +0,0 @@
# 本地开发
你会学到:如何搭建 OpenFlare 的本地开发环境、启动 Server、Agent 和管理端前端,运行测试与构建命令,并理解贡献代码前需要遵守的边界。
本页面向贡献者。产品边界、数据模型约束、API 约定和前端分层规范以 [开发约束](../guildline/development-constraints.md) 为准;本页只提供可执行的本地开发流程。
## 仓库结构
项目的核心物理目录及各模块(Server、Agent、Frontend 等)的职责分层,详见 [仓库结构](./repository.md)。
## 环境要求
| 项目 | 要求 |
| --- | --- |
| Go | `1.25+` |
| Node.js | `18+` |
| pnpm | 推荐通过 `corepack enable` 使用项目声明版本 |
| Docker | Server 容器、本地联调和 Agent Docker 镜像需要 |
| OpenResty | 本地运行 Agent 时需要可执行 `openresty` |
| PostgreSQL | 可选;未配置时 Server 使用 SQLite |
## 初始化前端依赖
```bash
cd openflare_server/web
corepack enable
pnpm install
```
构建供 Go Server 托管的静态产物:
```bash
pnpm build
```
## 启动 Server
SQLite 模式:
```bash
cd openflare_server
export JWT_SECRET='dev-jwt-secret'
export SQLITE_PATH='./openflare-dev.db'
export LOG_LEVEL='debug'
go run .
```
PostgreSQL 模式:
```bash
cd openflare_server
export JWT_SECRET='dev-jwt-secret'
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
export LOG_LEVEL='debug'
go run .
```
默认访问地址:
```text
http://localhost:3000
```
默认账号是 `root` / `123456`。
## 启动前端开发服务器
前端开发服务器默认监听 `3001`,并通过 `NEXT_DEV_BACKEND_URL` 代理到后端:
```bash
cd openflare_server/web
export NEXT_DEV_BACKEND_URL='http://127.0.0.1:3000'
pnpm dev
```
访问:
```text
http://localhost:3001
```
## 启动 Agent
创建本地 `agent.json`:
```json
{
"server_url": "http://127.0.0.1:3000",
"agent_token": "replace-with-node-auth-token",
"data_dir": "./data",
"heartbeat_interval": 10000,
"request_timeout": 10000
}
```
运行:
```bash
cd openflare_agent
export LOG_LEVEL='debug'
go run ./cmd/agent -config ./agent.json
```
未配置 `openresty_path` 时,Agent 默认调用 `openresty`。调试时可显式配置 `openresty_path`、`main_config_path`、`route_config_path` , `access_log_path`、`cert_dir`、`lua_dir` 和 `runtime_config_dir`。
## 测试
Server:
```bash
cd openflare_server
GOCACHE=/tmp/openflare-go-cache go test ./...
```
Agent:
```bash
cd openflare_agent
GOCACHE=/tmp/openflare-go-cache go test ./...
```
Frontend:
```bash
cd openflare_server/web
pnpm lint
pnpm typecheck
pnpm test
pnpm test:e2e
```
Docs:
```bash
cd docs
pnpm build
```
## 构建
管理端静态产物:
```bash
cd openflare_server/web
pnpm build
```
Server 二进制:
```bash
cd openflare_server
go build -o openflare-server .
```
Agent 二进制:
```bash
cd openflare_agent
go build -o openflare-agent ./cmd/agent
```
## 调试入口
| 场景 | 命令或位置 |
| --- | --- |
| Server 日志 | `LOG_LEVEL=debug go run .` |
| Agent 日志 | `LOG_LEVEL=debug go run ./cmd/agent -config ./agent.json` |
| Swagger | `http://localhost:3000/swagger/index.html` |
| 前端 API 代理 | `NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev` |
| OpenResty 配置校验 | `openresty -t -c ./data/etc/nginx/nginx.conf` |
## 代码风格与变更准入
贡献前先确认:
1. 需求符合 [产品边界](./index.md)。
2. 实现符合 [开发约束](../guildline/development-constraints.md)。
3. 不破坏发布、同步、回滚或升级主链路。
4. 涉及配置、部署、API 或产品边界时同步更新文档。
5. 风险较高的修改补充测试或等效联调验证。
数据库结构变更必须提升数据库版本号,并补充显式迁移方法和校验逻辑。v8-v17 保留为旧升级框架兼容链;v17 之后统一使用 goose,新的 goose 框架代码必须集中在 `openflare_server/model/goose` 包下;每次数据库升级都要在该包下新增独立的 `goose_<timestamp>_<description>.go` 文件,不得把具体迁移逻辑集中堆在 goose 注册入口中,也不得把新 goose 框架代码放回 `openflare_server/model` 根包。
+132 -185
View File
@@ -1,222 +1,169 @@
# 产品边界
你会学到:OpenFlare 是什么、解决什么问题、目标用户是谁、当前稳定能力有哪些,以及哪些设计边界在实现时不能被绕过。
你会学到:OpenFlare 是什么、当前稳定能力,以及开发时应遵守的核心产品边界与仓库结构目录分工。
OpenFlare 是一套自托管的 OpenResty 控制面,面向单团队或单组织内部运维场景。它解决反向代理配置、节点同步、证书托管、配置发布回滚与基础观测分散管理的问题。
OpenFlare 是一套自托管的 OpenResty 控制面,面向单团队或单组织内部运维场景。
---
## 项目定位
OpenFlare 适合需要统一管理多台 OpenResty 代理节点的团队:
OpenFlare 适合需要统一管理多台 OpenResty 代理节点的团队,具备以下定位:
* **控制与落地分离**:Server 控制面不直接 SSH 到代理节点,而是通过 Agent 主动拉取版本并应用。
* **不可变配置发布**:采用完整的配置版本进行预览、发布、激活和一键回滚。
* **一体化网关托管**:在同一个控制面内集成网站反代、TLS 证书自动续期申请、WAF 防护拦截、内网穿透(Tunnel)以及 Pages 静态网站托管。
* 希望用管理端维护反向代理网站配置。
* 希望每次配置变更都有完整版本、预览、激活与回滚。
* 希望节点主动同步配置,而不是由控制面 SSH 到节点执行命令。
* 希望在同一系统中管理 TLS 证书、域名资产、节点状态和基础访问分析。
**非本产品定位**:多租户云平台、Kubernetes Ingress Controller、服务网格或通用日志平台。
OpenFlare 当前不定位为通用日志平台、服务网格、Kubernetes Ingress Controller 或多租户云平台。
---
## 当前能力
| 能力 | 说明 |
| --- | --- |
| 反代规则管理 | 以网站配置为聚合边界,支持多域名与源站配置 |
| 网站级配置 | 一条规则对应一个网站,可绑定一个或多个域名,并共享站点级配置 |
| 源站管理 | 维护轻量源站目录,并允许网站保存可渲染的源站快照 |
| 配置版本 | 支持预览、发布、激活、不可变历史与回滚 |
| Agent 同步 | 支持注册、心跳、同步、应用结果上报与自更新 |
| OpenResty 托管 | 管理主配置模板、性能参数、缓存参数与 Lua 资源 |
| HTTPS/TLS | 托管证书与域名资产,并按域名绑定证书 |
| WAF | 以全局规则组与网站自定义规则组维护 IP/IP 段、IP 组、国家级地域黑白名单 |
| 基础观测 | 聚合节点请求、资源快照、健康事件和访问分析 |
| 节点管理 | 节点状态、令牌体系、部署与更新链路 |
| 管理端前端 | 基于 Next.js 的正式管理端 |
| 认证源登录 | 支持以认证源形式配置 GitHub 与标准 OIDC 登录入口,并允许第三方账号绑定已有本地用户 |
| 内网穿透 | 通过 TunnelRelay 节点与 OpenFlared 客户端,将内网 HTTP 服务安全暴露到公网,复用 Agent 的 HTTPS/WAF 能力 |
| Pages 静态托管 | 以 Pages 项目管理静态站点部署包,发布后由边缘 Agent 拉取并在本地 OpenResty 静态服务 |
| 能力 | 说明 | 详细设计/使用指南 |
| --- | --- | --- |
| **反代配置管理** | 以网站规则(Proxy Route)为聚合边界,支持多域名与多上游负载均衡 | [新建反代配置](../guide/proxy-config.md) |
| **配置版本控制** | 支持全局单一激活版本的预览、发布、不可变快照历史与秒级一键回滚 | [Agent 与发布模型](./agent-design.md) |
| **WAF 安全防护** | 全局与自定义规则组,支持手动/自动/订阅型 IP 组,GeoIP 准入与 PoW CC 防护 | [WAF 设计](./waf-design.md) / [WAF 使用指南](../guide/waf-usage.md) |
| **内网穿透** | 通过中继节点(Relay)与内网客户端(OpenFlared),反向穿透暴露内网 Web 服务 | [内网穿透设计](./tunnel-design.md) / [穿透使用指南](../guide/tunnel-usage.md) |
| **Pages 静态托管** | 直接上传前端 zip 包,由边缘节点拉取并由 OpenResty 本地服务,支持 API 反代与 SPA Fallback | [Pages 静态托管设计](./pages-design.md) |
| **TLS 证书自动续期** | 绑定 managed_domains 并通过 ACME 协议向 Let's Encrypt 申请/续期证书 | [新建反代配置](../guide/proxy-config.md) |
| **多节点监控与观测** | 收集节点资源快照、健康事件,聚合请求指标与访问日志明细 | [系统架构](./architecture.md) |
默认工作方式:
---
* 所有节点消费同一份全局激活版本。
* Server 保存配置与状态,不直接 SSH 管理节点。
* Agent 是节点侧唯一受控落地入口。
* TunnelRelay 节点同时运行 Agent(OpenResty)和 Relay(frps),提供内网穿透中继。
* OpenFlared 客户端在内网运行,管理 frpc 进程连接 Relay,将流量转发到内网服务。
## 核心产品边界与约束
## 典型使用场景
在开发与贡献代码时,**必须严格遵守**以下业务边界与技术约束,禁止为了临时需求而绕过限制:
| 场景 | 说明 |
| --- | --- |
| 内部服务统一入口 | 把多个内部 HTTP 服务通过统一域名和证书暴露 |
| 多节点反代配置同步 | 多台 OpenResty 节点消费同一份激活配置 |
| 配置变更审查 | 发布前查看预览或 diff,发布后保留不可变历史 |
| 快速回滚 | 重新激活旧版本,让 Agent 拉取并应用 |
| 证书托管 | 为不同域名绑定 TLS 证书 |
| 基础观测 | 查看节点状态、请求聚合、访问分析和健康事件 |
| 内网穿透 | 通过 Tunnel 将无法直接公网访问的内网 HTTP 服务暴露到互联网,享有 HTTPS、WAF 等全部防护能力 |
| 静态站点托管 | 上传已构建的静态资源包,将网站规则上游绑定到 Pages 项目,在边缘节点本地服务静态文件 |
### 1. 网站配置与上游约束
* **单站点域名共享策略**:一条路由规则对应一个网站,该站点下的多域名共享限流、缓存与反代上游等配置,不支持在同一规则内为不同域名做差异化服务配置。
* **上游类型互斥**:上游必须是直连地址(`direct`)、内网穿透(`tunnel`)或 Pages 静态托管(`pages`)三者之一,不允许在同一规则中混用。
* **直连类型限制**:直连上游可以是纯 `http://` 或 `https://` 的单个或多个地址(多地址仅支持纯 `scheme://host[:port]`),不支持非 HTTP 协议(如 TCP/UDP)上游。
### 2. WAF 安全边界
* **白名单优先原则**:白名单拥有绝对匹配权。若未命中白名单规则,才依次触发全局和自定义黑名单过滤。
* **GeoIP 弱依赖性**:地域准入解析完全依赖节点本地 MaxMind 库。当 GeoIP 异常或解析失败时,系统必须自动忽略地域规则,**绝对不能**破坏 IP 组过滤和反代主链路的可用性。
* **运行时数据解耦**:OpenResty 拦截时仅读取 Agent 同步至本地的 JSON,不与 Server 数据库通信。IP 组成员同步与版本发布解耦,通过 Chestsum 差分拉取以实现零重载平滑生效。
## 网站配置约束
### 3. 内网穿透边界
* **仅限 HTTP 流量**:穿透组件仅支持 HTTP/HTTPS 协议(底层依靠 frp 虚拟主机 Vhost 机制实现单端口域名路由复用),暂不支持单独的 TCP/UDP 端口分配。
* **中继配置静态化**:中继节点(Relay)配置相对静态,通过心跳被动获取,不纳入控制面的配置版本化管理体系。
* **Tunnel 与 Node 体系隔离**:Tunnel 客户端在内网发起出向建连,与控制面托管的边缘 Node(公网节点)是独立的实体,使用专属的 `tunnel_token` 进行鉴权。
`proxy_routes` 是“网站配置”的聚合对象。一条记录对应一个网站,可绑定一个或多个域名,并共享一组站点级配置。
### 4. Pages 静态托管边界
* **Direct Upload 托管模式**:仅支持直接上传预构建的 ZIP 静态资源包。不支持外部 Git 仓库自动构建、边缘 Serverless 函数、动态 SSR 服务或生成的二级预览域名。
* **包体硬上限限制**:为了保障边缘节点安全,ZIP 压缩包体最大 25 MiB,解压文件树不超过 1,000 个且总体积不超过 100 MiB。禁止上传含有任何软链接或目录跨越(Zip-Slip)的安全高危压缩包。
约束:
### 5. 系统与版本边界
* **全局单一激活版本**:所有节点拉取并消费同一份全局激活配置。不进行按节点分组的差异化配置发布。
* **单租户架构**:OpenFlare 仅供单团队在受信任的内部网络部署使用。采用单租户设计,不支持细粒度的多用户角色或多租户资源隔离。
* `proxy_routes.site_name` 是网站的业务唯一标识。
* `proxy_routes.domains` 至少包含一个域名,且 `domains[0]` 作为主域名。
* 任一域名全局只能属于一个 `proxy_routes`。
* 网站级流量限制、反向代理与缓存配置均按站点共享,不在同一网站内做域名级差异化配置。
* HTTPS 允许在同一站点内按域名绑定证书。
---
## 源站与上游约束
## 仓库结构
`origins` 服务于源站目录复用,仅保存源站地址、展示名与备注,不承载协议、端口、路径、权重或健康检查策略。`proxy_routes` 可选关联一个 `origins`,但规则内部仍保存完整上游快照以参与渲染。
在贡献代码时,请严格遵守以下物理分层与目录分工,保持代码结构清晰:
上游约束:
| 路径 | 职责 |
| ---------------------- | ---------------------------------------------------- |
| `openflare-server` | Gin + GORM + SQLite/PostgreSQL 单体控制面 |
| `openflare-server/web` | Next.js 15 App Router 管理端前端,由 Go Server 托管 |
| `openflare-agent` | Go 单体 Agent,运行在节点侧 |
| `openflare-relay` | Tunnel 中继代理,运行在公网边缘管理 frps 进程 |
| `openflared` | Tunnel 客户端,运行在内网服务器侧管理 frpc 进程 |
| `scripts` | 安装、自更新等系统辅助脚本 |
| `docs` | VitePress 文档站、设计基线、开发规范、部署与配置文档 |
| `docs/en` | 英文版文档 |
* `proxy_routes` 至少包含一个上游地址(直连类型 `direct`),或关联一个 Tunnel(内网穿透类型 `tunnel`),或关联一个 Pages 项目(静态托管类型 `pages`)。
* 多上游负载均衡统一渲染为带 keepalive 的 named `upstream`。
* 单上游允许附带 base path 或 query,并在 `proxy_pass` 中追加。多上游限定为纯 `scheme://host[:port]` 结构,且同一规则内的协议必须一致。
* `proxy_routes.origin_host` 为可选字段,用于回源时覆盖 `Host` 请求头。
* 所有直连类型上游地址都必须为合法的 `http://` 或 `https://`。
* 内网穿透类型上游必须关联有效 `tunnel_id`,并指定内网目标地址与协议。
* Pages 类型上游必须关联有效 Pages 项目,且项目必须存在已激活部署。Pages 站点不执行服务端构建、边缘函数或动态运行时代码,仅托管预构建静态资源。
### 1. Server 分层 (`openflare-server/`)
## Pages 静态托管约束
| 目录 | 职责 |
| ------------- | ------------------------------------------------ |
| `controller/` | 参数解析、调用 service、返回响应 |
| `service/` | 业务逻辑、校验、事务编排、配置渲染 |
| `model/` | 纯净实体模型类定义、旧迁移框架兼容与上下文注入 |
| `model/goose/` | goose 迁移提供者、桥接逻辑、注册入口与具体迁移文件 |
| `router/` | 路由注册 |
| `middleware/` | 认证、鉴权、限流、CORS、Turnstile 验证等横切逻辑 |
| `common/` | 配置、全局状态与初始化入口 |
| `utils/` | 纯工具函数与通用 helper |
| `job/` | 定时任务(各业务定时逻辑在独立文件中定义,cron.go 仅用于初始化调度) |
| `upload/` | 运行时本地临时文件上传目录(在 .gitignore 中忽略) |
| `logs/` | 运行时本地日志输出目录(在 .gitignore 中忽略) |
| `docs/` | API 文档(Swagger) |
| `data/` | 静态数据(如 GeoIP 数据库) |
OpenFlare Pages 面向边缘节点静态站点托管,采用“项目 + 不可变部署 + 网站规则绑定”的模型。
### 2. Agent 模块 (`openflare-agent/`)
约束:
| 目录/模块 | 职责 |
| ----------------------------- | -------------------------------------------- |
| `cmd/agent/` | Agent 命令行启动入口及主函数 |
| `internal/config/` | 配置读取与默认值 |
| `internal/heartbeat/` | 心跳与版本摘要判断 |
| `internal/sync/` | 配置拉取与应用编排 |
| `internal/nginx/` | OpenResty 文件写入、校验、reload、启动与回滚 |
| `internal/state/` | 本地状态与观测补报缓冲 |
| `internal/httpclient/` | Server 通信 |
| `internal/wsclient/` | WebSocket 客户端通信 |
| `internal/protocol/` | Agent API 协议类型 |
| `internal/updater/` | Agent 自更新逻辑 |
| `internal/logging/` | 日志处理 |
| `internal/observability/` | 可观测性(指标、链路等) |
| `internal/geoipdata/` | GeoIP 数据处理 |
| `internal/geoipupdate/` | GeoIP 数据更新 |
| `internal/agent/` | 核心 Agent 逻辑与生命周期 |
* Pages 项目保存名称、标识、启用状态、SPA fallback 启用状态、自定义回退路径和当前激活部署。
* Pages 部署由管理端上传预构建 zip 包生成;部署包保存在 Server 本地 Pages 存储目录,数据库只保存部署元数据和文件清单,不保存大体积文件内容。
* 只有项目存在激活部署后,`proxy_routes.upstream_type = 'pages'` 的网站规则才能绑定该项目。
* Pages 网站继续复用网站规则的域名、HTTPS、WAF、PoW、Basic Auth、限流、缓存配置和配置版本发布机制。
* 发布快照保存 Pages 项目、部署 ID、部署 checksum、入口文件、SPA fallback 启用状态和回退路径。Agent 拉取激活配置时按部署 checksum 下载并校验部署包,解压到本地 `pages_dir` 后再应用 OpenResty 配置。
* V1 不支持 Git 自动构建、预览域名、边缘函数、动态 SSR、外部对象存储或多租户隔离。
### 3. Frontend 分层 (`openflare-server/web/`)
## 内网穿透约束
| 目录 | 职责 |
| ------------- | -------------------------------------------- |
| `app/` | Next.js App Router 路由、布局、页面组装 |
| `features/` | 按业务域组织的功能模块 |
| `components/` | 跨 feature 复用的 UI 组件 |
| `lib/` | 请求客户端、环境变量、工具函数、常量 |
| `store/` | 少量跨页面 UI 状态管理 |
| `types/` | 共享类型定义 |
| `styles/` | 全局样式 |
| `tests/` | 前端单元测试与集成测试(Vitest、Playwright) |
| `scripts/` | 构建和部署相关脚本 |
| `public/` | 静态资源 |
OpenFlare 通过 TunnelRelay 节点与 OpenFlared 客户端实现内网穿透,底层基于 frp(快速反向代理)构建。
### 4. Relay 模块 (`openflare-relay/`)
### 节点与组件模型
| 模块 | 职责 |
| ---------------- | ------------------------------------------------ |
| `cmd/` | Relay 命令行启动入口及初始化主函数 |
| `internal/config/`| 本地配置文件解析与默认参数初始化 |
| `internal/frps/` | 管理 frps 进程生命周期、端口与 Token 并监控运行 |
| `internal/heartbeat/`| 周期性 HTTP 心跳通信、上报状态并获取更新请求 |
| `internal/httpclient/`| Server 的通用 API 客户端调用工具类 |
| `internal/observability/`| 采集本地宿主机、frps 的基础运行指标并进行预聚合 |
| `internal/relay/` | 协调中继的核心生命周期、初始化与清理 |
| `internal/state/` | 本地运行时状态、错误记录与持久化缓存 |
| `internal/updater/`| Relay 升级检查、下载安装与重启机制 |
| `internal/wsclient/`| 与 Server 保持的长连接 WebSocket 双向通信管道 |
**节点类型**:
### 5. OpenFlared (Client) 模块 (`openflared/`)
* `nodes.node_type` 区分节点类型:`edge_node`(边缘节点,默认)和 `tunnel_relay`(隧道中继)。
* TunnelRelay 节点同时运行 Agent(OpenResty)和 Relay(frps 管理器),共享同一个 `agent_token`。
- Agent 负责 HTTPS 终结、WAF 防护、缓存与流量限制等。
- Relay 管理 frps 进程,为内网客户端提供隧道中继服务。
* TunnelRelay 节点新增字段:`node_type`、`relay_bind_port`(frpc 连接端口,默认 7000)、`relay_vhost_http_port`(HTTP Vhost 端口,默认 8080)、`relay_auth_token`(自动生成)、`relay_status` 等。
| 模块 | 职责 |
| ---------------- | ------------------------------------------------ |
| `cmd/` | Client 命令行启动入口及初始化主函数 |
| `internal/config/`| 本地客户端配置加载与解析 |
| `internal/flared/`| 内网穿透客户端的核心调度与状态管理机制 |
| `internal/frpc/` | 热重载/动态生成多 Relay 的 `frpc.toml` 并监控 frpc |
| `internal/heartbeat/`| 与控制面进行的心跳通信,包含 Token 校验机制 |
| `internal/httpclient/`| 客户端通用 API 通信客户端 |
| `internal/sync/` | 增量拉取最新 Tunnel 路由绑定关系、生成快照并应用 |
| `internal/updater/`| 客户端自更新、新版检查与更新落地逻辑 |
| `internal/wsclient/`| 用于实时监听 Server 端隧道配置变更推送的 WS 信道 |
**Tunnel 客户端**:
* `tunnels` 表独立存储内网穿透客户端注册信息,与 `nodes` 体系无关。
* 每个 Tunnel 拥有唯一的 `tunnel_id`(格式 `tun-<32hex>`)和 `tunnel_token`(客户端认证凭据)。
* OpenFlared 客户端运行在内网,不对外暴露,使用 `tunnel_token` 认证,通过 `/api/flared/*` 端点与 Server 通信。
* 一个 OpenFlared 客户端可同时连接多个 Relay(为高可用)。
### 上游类型扩展
`proxy_routes` 的上游配置分为两种类型,通过 `upstream_type` 字段区分:
* **直连上游(`direct`,默认)**:直接将流量转发到源站地址,行为与现有完全一致。
* **内网穿透上游(`tunnel`)**:通过 TunnelRelay 节点将流量转发到内网服务。
- 必须指定 `tunnel_id`(关联 `tunnels` 表)。
- 必须指定 `tunnel_target_addr`(内网目标地址,如 `192.168.1.100:8080`)和 `tunnel_target_protocol`(`http` 或 `https`)。
- 发布时,Server 自动将上游地址替换为 `http://127.0.0.1:{relay_vhost_http_port}`。
### 流量路径与协议
**完整数据面流量路径**:
```
浏览器 → OpenResty (Agent, TLS/WAF) [TunnelRelay 节点]
↓
frps (Relay, HTTP Vhost 路由) [TunnelRelay 节点, 127.0.0.1:{vhost_port}]
↓
frp 隧道协议 (Host 头路由)
↓
frpc (Client, 多进程) [内网服务器]
↓
内网服务 (192.168.x.x:port)
```
**关键特性**:
* frps 使用 HTTP Vhost 单端口复用机制,所有 HTTP 隧道共享一个 `vhost_port`,通过 Host 头自动路由到对应 frpc。
* Agent 保留原始 `Host` 请求头,frps 依据此头进行虚拟主机匹配。
* 每个隧道对应一条 `proxy_routes`,可绑定多个域名。
* OpenFlared 客户端为每个连接的 Relay 管理一个独立的 frpc 进程,通过单一 frp 隧道传输多个 HTTP 代理定义。
### 配置同步模型
发布流程同时生成两类配置版本数据,统一使用 `config_version` 版本号关联:
* **Agent 侧配置**:OpenResty 主配置 + 路由配置 + WAF 规则。包含 tunnel 上游时,自动渲染为 `http://127.0.0.1:{vhost_port}` 上游。
* **Tunnel 侧配置**:Relay 列表 + frpc 代理定义。随发布流程版本化,变更时优先使用 `frpc reload` 热重载。
* **Relay 配置**:通过心跳响应下发,相对静态,不纳入版本化流程。
### 隧道设计约束
* 仅支持 HTTP 协议隧道流量(保留 TCP/UDP 隧道的可扩展性),暂不支持单独的 TCP/UDP 端口分配。
* Tunnel 类型上游的域名 DNS 应当解析到指定的 TunnelRelay 中继节点。
* frp 二进制(v0.61+)由系统部署脚本或容器镜像统一打包提供。
## HTTPS 约束
`proxy_routes.domain_cert_ids` 用于记录与 `domains` 平行的域名证书绑定;值为 `0` 表示该域名不启用 HTTPS,仅保留 HTTP。
发布渲染时:
* 带证书的域名按证书分组输出独立 `443 ssl` `server` 块。
* 未绑定证书的域名不得被自动带入 HTTPS。
* 必须将 `proxy_routes.domains` 中的全部域名一并纳入同一站点配置,避免同站点在版本快照中被拆散。
## WAF 约束
WAF 以规则组为核心配置边界。系统提供唯一的全局规则组(默认应用至所有站点),网站可在此基础上叠加多个自定义规则组。
核心能力:
* 支持单个 IP / CIDR 网段黑白名单。
* 支持 IP 组引用(包括手动、自动Expr计算、URL订阅三类 IP 组)。
* 支持基于 GeoIP 的国家/地区级地域准入过滤。
* 支持规则组自定义拦截响应(支持自定义状态码与拦截 HTML 页面,默认返回 `418`)。
IP 组与判定约束:
* **运行时解耦**:WAF 运行时只读取本地 JSON,不访问 Server 数据库;配置版本仅保存引用的 IP 组 ID。IP 组成员通过哈希 Checksum 差分心跳及 WebSocket 异步推送,实现无需平滑重载 Nginx 的热生效。
* **内置预设 Expr 规则**:
* 高频 404 扫描封禁:`request_count > 100 && status_404_ratio >= 0.8`
* 恶意 IP 直连探测:`ip_host_count > 50 && ip_host_ratio > 0.5`
* **判决优先级**:白名单拥有绝对优先权。若未命中白名单,则触发黑名单漏斗匹配(全局规则组优先,自定义组按 ID 升序匹配)。
* 地域解析依赖节点本地 MaxMind 库;当 GeoIP 异常时自动忽略地域规则,不得破坏 IP 规则与反代主链路的可用性。
## 认证源约束
`auth_sources` 统一支持 `github` 与 `oidc` 登录配置入口。`external_accounts` 存储第三方与本地用户的绑定关系。第三方账号首次接入逻辑:
* 已绑定时直接授权登录;若已有本地会话则自动建立绑定。
* 未绑定且允许注册时自动创建本地账号;若关闭注册,则要求用户提供已有本地账号密码以建立关联。
## 版本与观测约束
* `config_versions` 必须保存完整快照、渲染结果与 `checksum`。
* 全局同时只能有一个激活版本。
* 回滚通过重新激活旧版本实现。
* `nodes` 只承载控制面状态与低频摘要,不承载高频观测事实。
* 指标、趋势和访问分析优先使用服务端聚合结果,而不是前端临时统计。
* 访问明细只保留受控时间窗口,不演变成通用日志平台。
---
## 文档维护原则
* 产品范围或系统边界变化时更新本文档。
* 系统结构或模块职责变化时更新 [系统架构](./architecture.md)。
* 发布、同步、回滚与 Agent 模型变化时更新 [Agent 与发布模型](./agent-design.md)。
* 开发约束、代码规范、接口约定变化时更新 [开发约束](../guildline/development-constraints.md)。
* 部署方式变化时更新 [部署说明](../deployment/deployment.md) 与 README.
* 配置项变化时更新 [配置项参考](../reference/configuration.md)。
* 已完成阶段不再以“版本计划”形式回填。
* 新阶段开始前,先补设计,再进入实现。
* 产品范围或系统边界变化:更新本文档([产品边界](./index.md))。
* 系统结构、组件分工变化:更新 [系统架构](./architecture.md)。
* 发布、同步、回滚与 Agent 模型变化:更新 [Agent 与发布模型](./agent-design.md)。
* 开发约束、代码规范、接口约定变化:更新 [开发约束](../guideline/Constraints.md)。
* 部署方式变化:更新 [部署说明](../deployment/deployment.md) 与 README。
* 配置项变化:更新 [配置项参考](../reference/configuration.md)。
+109
View File
@@ -0,0 +1,109 @@
# Uptime Kuma 监控同步设计
你会学到:OpenFlare 与 Uptime Kuma 监控服务集成的设计背景、基于 Socket.IO 协议的控制流设计、以标签隔离为核心的防污染模型,以及差分增量同步的状态机比对逻辑。
---
## 需求分析
在多节点的网关架构中,监控系统的状态与反向代理路由的状态通常是相互脱节的:
1. **录入开销大**:每当网关控制面新增或下线一个站点,管理员都必须在监控系统(如 Uptime Kuma)中重复配置对应的探测地址与告警策略。
2. **数据不一致**:当代理路由域名发生变更或切换 HTTPS 时,容易遗漏修改监控参数,导致监控系统误报或漏报。
3. **环境污染隐患**:如果简单的在监控中执行全量“删除-重建”同步,不仅会清空监控系统中的历史统计指标和 SLA 曲线,还会影响到用户在此监控实例上自行配置的、与网关无关的其他监控任务。
为了解决这些痛点,OpenFlare 引入了基于客户端/服务器模式的 **Uptime Kuma 自动监控同步机制**,实现网关站点路由定义与可用性监测系统的强一致、低开销以及零污染同步。
---
## 核心架构设计
Uptime Kuma 同步子系统完全运行在 **Server 控制面** 的后台调度器中。
```text
[ OpenFlare 控制面 / 数据库 ] [ Uptime Kuma 实例 ]
│ │
1. 定时 Cron 触发 (Job) │
│ │
2. 读取代理路由与选项配置 │
│ │
3. 连接 Socket.IO 接口 <──── 4. Socket.IO 握手 & 登录 ───┤
│ │
├────── 5. 校验 / 创建 "OpenFlare" 标签 ────────►│
├────── 6. 比对监测站点属性与 Kuma 监控清单 ──────►│
│ │
└────── 7. 执行差分指令 (add / edit / delete) ─►│
```
同步子系统不经过数据面的 Agent 节点,而是由 Server 通过 Uptime Kuma 暴露的 Socket.IO 端点直接交互。这种设计可以降低边缘节点的网络开销,并将鉴权凭证(Kuma 用户名与密码)安全收拢在控制面中。
---
## 标签隔离与防污染设计
为了在一个共享的 Uptime Kuma 实例中安全运行,而不干扰用户手动创建的其他监控项,设计上采用了 **专属标签隔离机制**:
1. **`OpenFlare` 专属标签**:
* 同步程序首次连接时,会调用 `getTags` 接口拉取实例中的所有标签。
* 检查是否存在名为 `OpenFlare` 的标签(默认颜色为靛蓝色 `#4f46e5`)。如果不存在,则通过 `addTag` 接口在 Kuma 中自动创建它。
2. **过滤范围收拢**:
* 同步任务在拉取 Uptime Kuma 的监控列表(`monitorList`)后,仅会保留**打有 `OpenFlare` 标签**的监控项。
* 所有的修改比对(`editMonitor`)和下线清理(`deleteMonitor`)**仅在此过滤子集内进行**。任何未绑定 `OpenFlare` 标签的监控项对同步程序均是“隐形”的,实现了完美的防污染隔离。
---
## 差分同步状态机逻辑
同步程序每次执行时,会对 OpenFlare 本地配置与 Uptime Kuma 数据进行差分计算,根据比对结果执行不同的 Socket.IO 事件:
```mermaid
stateDiagram-v2
[*] --> 检查站点状态与监控范围
state "检查监控范围" as Scope {
[*] --> 校验站点是否启用并且在 Scope 内
校验站点是否启用并且在 Scope 内 --> 在Scope内 : 是
校验站点是否启用并且在 Scope 内 --> 不在Scope内 : 否
}
不在Scope内 --> 检查Kuma中是否存在同名且带标签的监控
检查Kuma中是否存在同名且带标签的监控 --> 执行清理 : 存在
检查Kuma中是否存在同名且带标签的监控 --> 忽略 : 不存在
在Scope内 --> 检查Kuma中是否存在同名监控
state "比对属性" as Compare {
[*] --> 检查是否存在
检查是否存在 --> 新建监控项 : 否
检查是否存在 --> 比对元数据 : 是
比对元数据 --> 属性一致 : 匹配
比对元数据 --> 属性不一致 : 不匹配
}
新建监控项 --> 发送add指令并绑定Tag
属性不一致 --> 发送editMonitor指令
属性一致 --> 忽略
执行清理 --> 发送deleteMonitor指令
忽略 --> [*]
```
### 1. 监测 URL 规范化
站点路由在 OpenFlare 中可配置多个域名,同步程序自动提取其主域名(Primary Domain)并根据是否启用 HTTPS 组装为标准的 `http://` 或 `https://` 前缀。
### 2. 比对属性清单
如果同名且带标签的监控已存在,同步程序会细致比对以下 5 个关键字段是否与当前网关全局 Option 一致。只要有一个字段不匹配,便会触发更新:
* **URL 地址**:`Url`
* **探测频率**:`Interval`(默认 60s)
* **重试次数**:`MaxRetries`
* **重试间隔**:`RetryInterval`(默认 60s)
* **请求超时**:`Timeout`(默认 48s)
---
## 调度器与高并发保护
1. **基于 Cron 的单线程执行**:
* Server 周期性(每 1 分钟)通过后台的 Cron Job 探测是否达到配置的同步间隔(`UptimeKumaSyncInterval`)。
* 任务内部设计了互斥锁(Mutex Locking)。如果前一次同步请求因为网络延迟等原因尚未结束,下一次调度将自动跳过,防止并发多个 Socket.IO 连接对 Uptime Kuma 实例造成 DDOS 冲击。
2. **WebSocket 状态监听**:
* 同步程序利用 Socket.IO 的事件监听机制,在连接建立后,必须等到监听到 `monitorList` 事件的完整列表推送后,才允许向下执行差分算法,以规避因为数据加载不完整导致误删监控项的边界情况。
+124
View File
@@ -0,0 +1,124 @@
# 登录验证码设计 (Login CAPTCHA Integration)
本文档阐述在 OpenFlare 控制面中引入基于 Proof-of-Work (PoW) 与无感浏览器指纹特征的开源 CAPTCHA 方案 —— Cap,以防止对登录 API 进行暴力破解与爬虫撞库攻击的设计。
---
## 1. 业务背景与产品范围
### 背景与痛点
根据我们的系统安全分析,OpenFlare 的登录端点 `/api/user/login` 虽然配置了基于 IP 的限流限制,但由于缺少用户维度的防护机制,攻击者可使用代理池绕过 IP 限制对高权限账户(如 `root`)实施撞库和暴力破解。同时,对于系统登录页面,标准的视觉验证码对用户体验和无障碍不够友好。
### 产品范围与技术选型
* **技术选型**:Cap (Proof-of-Work 驱动的无感无图像验证码解决方案)。
- **核心原理**:客户端(Widget/网页)从服务器获取工作量证明 (PoW) 的难题,使用浏览器后台计算求解并将答案回传。服务器验证答案的正确性,完成人机识别。
- **优势**:无感、无图像验证、不依赖任何外部第三方 API 节点(私密)、包极小。
* **接入范围**:控制面 Server 登录 API(`/api/user/login`)以及前端登录页面。
* **配置粒度**:支持管理员通过控制台 Option 表随时开启/关闭验证码(`CapLoginEnabled`)。
---
## 2. 系统架构与交互时序
### 2.1 模块分工
1. **Frontend (前端)**:
* 在登录页面引入 `cap-widget`(React 19 自定义元素)。
* 提交表单时,伴随提交由 Widget 求解出并得到的 `cap-token`。
2. **Server (控制面后端)**:
* 暴露 `POST /api/cap/challenge` 接口,为客户端分发 PoW 难题和签名的 JWT Token。
* 暴露 `POST /api/cap/redeem` 接口,校验客户端提交的 PoW 解答并核发带有失效时间的登录凭证(Redeem Token)。
* 将 Redeem Token 与对应过期时间保存在内存缓存/Redis 缓存中。
* 在 `POST /api/user/login` 接口中,若启用了验证码保护,先校验并消耗(单次失效)对应的 `cap-token`。
### 2.2 验证流时序图
```mermaid
sequenceDiagram
autonumber
actor User as 用户
participant Browser as 浏览器 (前端 Web)
participant Server as OpenFlare Server (后端)
participant Cache as 内存/Redis 缓存
User->>Browser: 打开登录页面
Browser->>Server: POST /api/cap/challenge (获取难题)
Server->>Browser: 返回 {challenge, token, expires} (JWT 格式)
Note over Browser: Widget 在后台(WASM/Worker)执行 PoW 难题计算
Browser->>Server: POST /api/cap/redeem (提交 solutions + token)
alt 校验 PoW 解答通过
Server->>Cache: 存储 Redeem Token (tokenKey:expires)
Server->>Browser: 返回 {success: true, token} (即 cap-token)
else 校验失败
Server->>Browser: 返回 {success: false, reason}
end
User->>Browser: 输入账号密码,点击登录
Browser->>Server: POST /api/user/login (在 HTTP 请求头中携带 X-Cap-Token)
alt CapLoginEnabled = true
Server->>Server: Middleware (CapAuth) 校验并消费 X-Cap-Token
alt token 合法且未过期且未被消费
Server->>Server: c.Next() -> 执行常规登录逻辑 (密码 Bcrypt 校验)
Server->>Browser: 返回登录成功 (JWT session)
else token 无效或已被消费
Server->>Browser: 拦截并返回验证码错误 (401 Unauthorized)
end
else CapLoginEnabled = false
Server->>Server: c.Next() -> 执行常规登录逻辑
end
```
---
## 3. 核心接口与数据模型
### 3.1 接口定义
#### 1. 获取难题 (GET/POST /api/cap/challenge)
* **请求方式**:`POST`
* **接口权限**:公开
* **响应负载**:
```json
{
"challenge": {
"c": 50,
"s": 32,
"d": 4
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjIjo1MCwicyI6MzIsImQiOjQsImV4cCI6MTcxNzY2MDgwMCwiaWF0IjoxNzE3NjYwMjAwLCJuIjoiMGExYjJjM2Q0ZTVmNiJ9.signature",
"expires": 1717660800000
}
```
#### 2. 核销难题 (POST /api/cap/redeem)
* **请求方式**:`POST`
* **请求负载**:
```json
{
"token": "challenge_jwt_token_here",
"solutions": [12345, 67890, 54321]
}
```
* **响应负载 (成功)**:
```json
{
"success": true,
"token": "random_id:ver_token",
"expires": 1717661000000
}
```
#### 3. 登录接口 (POST /api/user/login)
* **请求负载保持不变**:
```json
{
"username": "root",
"password": "your_password"
}
```
* **验证码载体**:放置于 HTTP Request Header `X-Cap-Token` 中。
---
## 4. 重放攻击防护与安全性权衡
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 秒即可静默解出,极大地兼顾了用户体验和反爬效果。
+218
View File
@@ -0,0 +1,218 @@
# Pages 静态托管设计文档
你会学到:OpenFlare Pages 静态站点托管的架构设计、不可变部署与安全解压流程、OpenResty 的静态服务与 API 反向代理配置渲染,以及控制面与 Agent 的协同工作流。
---
## 需求分析
在现代 Web 运维中,除了动态应用的反向代理,静态前端站点(如 React、Vue 等构建的单页应用 SPA,或者 Hugo、VitePress 等静态生成器产物)的部署与托管也是极高频的场景。
传统方案中,静态站点的发布通常面临以下痛点:
1. **发布与反代配置脱节**:前端构建产物上传到 Nginx 宿主机后,还需要手动或通过其他脚本修改 Nginx 虚拟主机配置,容易出错且缺乏版本控制。
2. **多节点分发困难**:当控制面管理多台边缘节点时,将静态文件同步分发到所有节点,并确保文件一致性,需要维护复杂的同步脚本(如 rsync 等)。
3. **回滚缺乏一致性**:一旦新前端包发布失败或存在严重缺陷,不仅要恢复静态文件,还要恢复对应的反代规则,很难做到原子回滚。
为了解决这些问题,OpenFlare 引入了受 Cloudflare Pages 启发的 **Pages 静态托管** 功能。该功能将“前端部署包上传”与“网站代理规则配置”合二为一,依托 OpenFlare 的 pull-based(拉取式)协同架构,实现静态文件分发与反代配置发布的强一致性、不可变性与一键秒级回滚。
---
## 核心功能
Pages 静态托管子系统包含以下核心能力:
* **Direct Upload 部署模式**:支持直接上传预构建的 `.zip` 静态资源包,省去复杂的 Git 集成和构建环境依赖。
* **不可变部署快照**:每次上传产生一个带唯一 ID 和 SHA-256 Checksum 的不可变部署记录。历史包永久保留,支持随时激活和回滚。
* **SPA Fallback 支持**:支持对单页应用(SPA)进行 Fallback 路由配置,请求找不到静态文件时自动重定向到入口文件。
* **内置 API 反代服务**:支持在 Pages 规则内一键启用 API 代理,消除跨域问题,将请求转发给指定的后端服务。
* **安全包校验与解压缩**:内置 Zip-Slip 路径逃逸防御、防软链接劫持、文件大小/数量硬上限控制,保障节点物理安全。
---
## Pages 静态托管架构
Pages 静态托管在逻辑上分为 **控制面 (Control Plane)** 与 **数据面 (Data Plane)**。
```mermaid
graph TD
%% 数据流
Browser[1. 浏览器 / 访客] -->|HTTPS 请求 / 流量| OpenResty[2. OpenResty / WAF]
OpenResty -->|1. 静态服务 try_files| StaticFiles[3. 边缘节点本地静态目录 current]
OpenResty -->|2. 转发 API 代理| BackEnd[4. 后端 API 服务]
%% 控制流与心跳
Server[OpenFlare Server 控制面] <-->|Agent API / Heartbeat| Agent[openflare-agent 进程]
Server -.->|5. 存储 ZIP 部署包| LocalStore[(Server 本地存储)]
Agent -->|1. 发现新版本| Server
Agent -->|2. 下载部署包| Server
Agent -->|3. 校验并解压缩| StaticFiles
Agent -->|4. 应用并 Reload| OpenResty
style Browser fill:#f9f,stroke:#333,stroke-width:2px
style StaticFiles fill:#9f9,stroke:#333,stroke-width:2px
style Server fill:#f96,stroke:#333,stroke-width:2px
```
* **控制面(Control Plane)**:Server 接收前端上传的部署包,并将包存储于本地磁盘,元数据写入数据库。配置发布时,编译出带有 `pages_deployment` 详情的不可变全局版本快照。
* **数据面(Data Plane)**:Agent 在心跳同步中发现版本更新并引用了 Pages 部署,通过专属 API 下载对应的部署包并执行校验解压缩。OpenResty 拦截域名请求,在本地提供静态文件服务。
---
## 数据模型与元数据设计
### 1. 核心数据库实体
* **Pages 项目 (`pages_projects`)**:
* 记录项目的业务名称、Slug 标识(URL 友好型)、启用状态、静态服务根目录(RootDir,可为空)、入口文件名(EntryFile,默认 `index.html`)、SPA Fallback 设置,以及 API 反向代理配置(APIProxyPath, APIProxyPass, APIProxyRewrite)。
* **Pages 部署 (`pages_deployments`)**:
* 记录单次上传生成的不可变快照。包含:部署号 (DeploymentNumber, 递增序列)、SHA-256 Checksum 校验和、部署状态 (uploaded/active)、部署包的本地存储路径、解压后的文件数与总字节数。
* **部署文件清单 (`pages_deployment_files`)**:
* 存储每次部署的完整静态文件树路径、文件大小及单个文件哈希。用于审计和后续校验。
### 2. 路由关联与快照
`proxy_routes` 路由规则通过 `upstream_type = "pages"` 及 `pages_project_id` 关联 Pages 项目。当路由类型为 `pages` 且该项目存在已激活的部署时,才允许将该路由加入发布流程。
发布时生成的版本快照中包含 `snapshotPagesDeployment`,主要结构为:
```json
{
"project_id": 1,
"project_slug": "my-spa-app",
"deployment_id": 12,
"deployment_number": 3,
"checksum": "a7b3c2...",
"entry_file": "index.html",
"spa_fallback_enabled": true,
"spa_fallback_path": "/index.html",
"api_proxy_enabled": true,
"api_proxy_path": "/api",
"api_proxy_pass": "http://api.internal:8000",
"api_proxy_rewrite": "/api/(.*) /$1",
"local_root": "__OPENFLARE_PAGES_DIR__/deployments/12/current"
}
```
---
## Server 端 (控制面) 职责与生命周期
### 1. ZIP 包安全校验与分析
为了避免不可信的用户上传恶意压缩包攻击服务器,控制面在 `UploadPagesDeployment` 时执行严格的流式校验:
* **大小限制**:ZIP 压缩包不得超过 25 MiB(保守的 V1 默认值),且展开后的解压总体积不得超过 100 MiB。
* **数量限制**:压缩包中包含的静态文件总数不得超过 1,000 个。
* **软链接阻断**:遍历 ZIP 文件,一旦检测到任何软链接 (`os.ModeSymlink`),立即抛出错误并拒绝上传,防御软链接劫持攻击。
* **Zip-Slip 防御**:对每个压缩文件路径进行 `Clean` 并检查是否包含 `..` 或以 `/` 开头,防御目录跨越漏洞,防止写入系统敏感路径。
* **入口文件校验**:项目指定的入口文件(例如 `index.html`,可在 `project.RootDir` 下)必须在 ZIP 压缩包中存在,否则拒绝上传。
* **公共根目录去噪**:许多打包工具(如 GitHub 导出的 zip)会包含一个多余的主文件夹作为公共根前缀。控制面自动探测公共根前缀并将其安全剥离。
### 2. 部署包存储规划
控制面仅将 zip 文件存储在本地存储目录 `artifacts/{project_slug}/{checksum}.zip`,并在数据库中记录路径和清单。**大体积静态包不写入 config_versions 记录和任何配置推送通道**,以保障控制面数据同步的轻量与高效。
---
## Agent 端 (数据落地) 职责与自愈
Agent 运行在各边缘代理节点上,在应用配置版本前,必须先将 Pages 静态资源“原子”地拉取到节点本地。
### 1. 校验式增量拉取
1. Agent 解析激活配置中的 `SourceConfigJSON`,检索出所有 `UpstreamType == "pages"` 的路由引用的部署 `DeploymentID` 和 `Checksum`。
2. 检查本地部署目录是否存在正确的版本标记文件 `.openflare-pages.json`,且 `Checksum` 匹配。
3. 若不匹配,通过专属接口 `GET /api/agent/pages/deployments/:id/package` 下载对应的部署包。下载请求头必须携带节点独有的 `X-Agent-Token` 用于 Server 鉴权。
### 2. 安全解压缩与原子切换
为了保证配置应用过程的“无缝”且能在出错时立即回滚:
1. Agent 将下载的部署包数据写入临时目录,并重新计算 SHA-256 Checksum。如果与配置指明的 checksum 不符,立即报错并阻断发布流程。
2. 解压部署包至临时目录 `releases/{checksum}.tmp`。解压时同样执行 Zip-Slip 目录跨越和软链接校验防御。
3. 解压成功后,写入标记文件 `.openflare-pages.json`。
4. 清理 `releases/{checksum}` 目录,将整个临时目录重命名为 `releases/{checksum}`。
5. **原子切换**:建立拷贝当前部署的物理副本到目标位置 `deployments/{deployment_id}/current`。切换前先备份上一版本的 `current`,一旦重载配置失败,Agent 能够快速恢复 `current` 目录并回滚 OpenResty。
6. **定时清理**:每次配置成功应用后,Agent 自动比对本地部署目录,将所有不活跃的(即未被当前激活版本引用的)历史部署包和文件夹进行物理删除,释放磁盘空间。
---
## OpenResty (静态服务与代理) 配置渲染
对于 Pages 托管站点,控制面自动渲染对应的 `server` 块,取代常规代理路由中的 `proxy_pass`。
### 1. 静态服务指令渲染
* **`root` 与 `index`**:
Server 根据配置将 `root` 指向 Agent 的 Pages 动态目录占位符 `__OPENFLARE_PAGES_DIR__/deployments/{deployment_id}/current`,并在此基础上追加项目的 `RootDir`。`index` 指向设置的入口文件。
```nginx
server {
listen 80;
server_name myapp.example.com;
root "/var/lib/openflare/pages/deployments/12/current";
index "index.html";
...
}
```
### 2. try_files 与 SPA Fallback 机制
* **禁用 SPA Fallback (默认)**:
仅匹配物理存在的文件,否则返回 strict 404:
```nginx
location / {
try_files $uri $uri/ =404;
}
```
* **启用 SPA Fallback**:
若请求的文件不存在,重定向到项目配置的入口 Fallback 文件(通常为 `/index.html`):
```nginx
location / {
try_files $uri $uri/ /index.html;
}
```
### 3. API 反向代理与重写 (Rewrite) 渲染
当静态前端项目需要请求后端 API 且不希望面临跨域问题时,可开启 API 反代。OpenResty 渲染器会自动在其对应的静态 `server` 块内嵌套专属的 API `location` 分支:
```nginx
server {
listen 80;
server_name myapp.example.com;
...
# API 代理路径匹配
location /api {
# 如果配置了 Rewrite 规则,应用重写逻辑
rewrite ^/api/(.*)$ /v1/$1 break;
rewrite ^/api$ / break;
proxy_pass http://api.internal:8000;
proxy_http_version 1.1;
proxy_set_header Host $http_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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
location / {
try_files $uri $uri/ /index.html;
}
}
```
---
## 交互逻辑与同步流程
一次完整的 Pages 上传与全局生效的生命周期如下:
```text
[ 前端管理员 ] [ Server (控制面) ] [ Agent (数据落地) ] [ OpenResty ]
| | | |
|--- 1. 上传 ZIP 包 ----->| | |
| |--- 2. 安全校验与解压分析 ----| |
| |--- 3. 归档包与持久化清单 ---| |
| | | |
|--- 4. 绑定路由并发布 -->| | |
| |--- 5. 生成新配置版本并广播 ->| |
| | | |
| | |--- 6. 下载 ZIP 部署包 -->|
| | |<-- 7. 返回文件数据 -------|
| | | |
| | |--- 8. 强一致性 Checksum -|
| | |--- 9. 安全解压缩 -------|
| | |--- 10. 原子切换 current -|
| | |--- 11. 测试与重载配置 ---->|
| | |<-- 12. 重载成功 ---------|
| |<-- 13. 上报 Apply Success | |
| | | |
```
-94
View File
@@ -1,94 +0,0 @@
# 仓库结构
你会学到:OpenFlare 仓库中 Server、Agent、前端、脚本和文档目录分别负责什么,以及贡献代码时应把逻辑放到哪一层。
| 路径 | 职责 |
| ---------------------- | ---------------------------------------------------- |
| `openflare_server` | Gin + GORM + SQLite/PostgreSQL 单体控制面 |
| `openflare_server/web` | Next.js 15 App Router 管理端前端,由 Go Server 托管 |
| `openflare_agent` | Go 单体 Agent,运行在节点侧 |
| `openflare_relay` | Tunnel 中继代理,运行在公网边缘管理 frps 进程 |
| `openflared` | Tunnel 客户端,运行在内网服务器侧管理 frpc 进程 |
| `scripts` | 安装、自更新等系统辅助脚本 |
| `docs` | VitePress 文档站、设计基线、开发规范、部署与配置文档 |
| `docs/en` | 英文版文档 |
## Server 分层
| 目录 | 职责 |
| ------------- | ------------------------------------------------ |
| `controller/` | 参数解析、调用 service、返回响应 |
| `service/` | 业务逻辑、校验、事务编排、配置渲染 |
| `model/` | 模型定义、数据库版本与迁移 |
| `router/` | 路由注册 |
| `middleware/` | 认证、鉴权、限流、CORS、Turnstile 验证等横切逻辑 |
| `common/` | 配置、全局状态与初始化入口 |
| `utils/` | 纯工具函数与通用 helper |
| `job/` | 定时任务(如 SSL 证书续期) |
| `upload/` | 文件上传处理 |
| `docs/` | API 文档(Swagger) |
| `data/` | 静态数据(如 GeoIP 数据库) |
## Agent 模块
| 模块 | 职责 |
| ---------------- | -------------------------------------------- |
| `config/` | 配置读取与默认值 |
| `heartbeat/` | 心跳与版本摘要判断 |
| `sync/` | 配置拉取与应用编排 |
| `nginx/` | OpenResty 文件写入、校验、reload、启动与回滚 |
| `state/` | 本地状态与观测补报缓冲 |
| `httpclient/` | Server 通信 |
| `wsclient/` | WebSocket 客户端通信 |
| `protocol/` | Agent API 协议类型 |
| `updater/` | Agent 自更新逻辑 |
| `logging/` | 日志处理 |
| `observability/` | 可观测性(指标、链路等) |
| `geoipdata/` | GeoIP 数据处理 |
| `geoipupdate/` | GeoIP 数据更新 |
| `agent/` | 核心 Agent 逻辑与生命周期 |
## Frontend 分层
| 目录 | 职责 |
| ------------- | -------------------------------------------- |
| `app/` | Next.js App Router 路由、布局、页面组装 |
| `features/` | 按业务域组织的功能模块 |
| `components/` | 跨 feature 复用的 UI 组件 |
| `lib/` | 请求客户端、环境变量、工具函数、常量 |
| `store/` | 少量跨页面 UI 状态管理 |
| `types/` | 共享类型定义 |
| `styles/` | 全局样式 |
| `tests/` | 前端单元测试与集成测试(Vitest、Playwright) |
| `scripts/` | 构建和部署相关脚本 |
| `public/` | 静态资源 |
## Relay 模块
| 模块 | 职责 |
| ---------------- | ------------------------------------------------ |
| `cmd/` | Relay 命令行启动入口及初始化主函数 |
| `internal/config/`| 本地配置文件解析与默认参数初始化 |
| `internal/frps/` | 管理 frps 进程生命周期、端口与 Token 并监控运行 |
| `internal/heartbeat/`| 周期性 HTTP 心跳通信、上报状态并获取更新请求 |
| `internal/httpclient/`| Server 的通用 API 客户端调用工具类 |
| `internal/observability/`| 采集本地宿主机、frps 的基础运行指标并进行预聚合 |
| `internal/relay/` | 协调中继的核心生命周期、初始化与清理 |
| `internal/state/` | 本地运行时状态、错误记录与持久化缓存 |
| `internal/updater/`| Relay 升级检查、下载安装与重启机制 |
| `internal/wsclient/`| 与 Server 保持的长连接 WebSocket 双向通信管道 |
## OpenFlared (Client) 模块
| 模块 | 职责 |
| ---------------- | ------------------------------------------------ |
| `cmd/` | Client 命令行启动入口及初始化主函数 |
| `internal/config/`| 本地客户端配置加载与解析 |
| `internal/flared/`| 内网穿透客户端的核心调度与状态管理机制 |
| `internal/frpc/` | 热重载/动态生成多 Relay 的 `frpc.toml` 并监控 frpc |
| `internal/heartbeat/`| 与控制面进行的心跳通信,包含 Token 校验机制 |
| `internal/httpclient/`| 客户端通用 API 通信客户端 |
| `internal/sync/` | 增量拉取最新 Tunnel 路由绑定关系、生成快照并应用 |
| `internal/updater/`| 客户端自更新、新版检查与更新落地逻辑 |
| `internal/wsclient/`| 用于实时监听 Server 端隧道配置变更推送的 WS 信道 |
+5 -5
View File
@@ -40,7 +40,7 @@ graph TD
FlaredFrpc -->|转发本地请求| LocalOrigin[5. 内网源站 192.168.x.x]
%% 控制流与心跳
Server[OpenFlare Server 控制面] <-->|Relay API / Heartbeat| RelayManager[openflare_relay 进程]
Server[OpenFlare Server 控制面] <-->|Relay API / Heartbeat| RelayManager[openflare-relay 进程]
Server <-->|Client API / Heartbeat| ClientManager[openflared 进程]
RelayManager -.->|管控进程及配置| RelayFrps
@@ -51,14 +51,14 @@ graph TD
style Server fill:#f96,stroke:#333,stroke-width:2px
```
* **控制面(Control Plane)**:Server 维护数据库状态;中继节点上的 `openflare_relay` 进程与内网服务器上的 `openflared` 进程通过 HTTP 心跳与 WebSocket 长通道同步隧道配置。
* **数据面(Data Plane)**:公网流量首先进入公网边缘的 Agent (OpenResty),在此完成 HTTPS 握手、TLS 终止和 WAF 过滤,接着通过 `proxy_pass` 转发到同机部署的 `openflare_relay (frps)`。`frps` 再将请求封包通过与内网 `openflared (frpc)` 建立的持久隧道传输过去,最后由 `frpc` 拆包并分发给内网实际的源站服务。
* **控制面(Control Plane)**:Server 维护数据库状态;中继节点上的 `openflare-relay` 进程与内网服务器上的 `openflared` 进程通过 HTTP 心跳与 WebSocket 长通道同步隧道配置。
* **数据面(Data Plane)**:公网流量首先进入公网边缘的 Agent (OpenResty),在此完成 HTTPS 握手、TLS 终止和 WAF 过滤,接着通过 `proxy_pass` 转发到同机部署的 `openflare-relay (frps)`。`frps` 再将请求封包通过与内网 `openflared (frpc)` 建立的持久隧道传输过去,最后由 `frpc` 拆包并分发给内网实际的源站服务。
---
## Relay (中继端) 设计
`openflare_relay` 是部署在公网边缘的中继管理器,运行在 `tunnel_relay` 类型的节点上。
`openflare-relay` 是部署在公网边缘的中继管理器,运行在 `tunnel_relay` 类型的节点上。
### 1. 核心架构与逻辑
* **进程守护**:Relay 进程内部持有 `frps` 二进制,通过 `exec.Command` 拉起 `frps -c frps.toml` 子进程,并启动 goroutine 异步监听其退出状态。如果发现 `frps` 异常退出,会结合退避机制自动拉起。
@@ -98,7 +98,7 @@ graph TD
+-------------------------------------------+-------------------------------------------+
| |
v (中继端) v (内网客户端)
openflare_relay 心跳检测到 frps 端口/Token 变化 openflared 心跳检测到 tunnel_version 发生变更
openflare-relay 心跳检测到 frps 端口/Token 变化 openflared 心跳检测到 tunnel_version 发生变更
重新渲染本地 frps.toml 请求拉取最新代理映射包
Kill 并重新拉起 frps 进程 重新渲染 frpc_<relay_id>.toml
上报健康状态为 healthy 对有变更的 Relay 进程执行重启与配置热重载
+4 -3
View File
@@ -110,7 +110,8 @@ flowchart TD
D -- 是 (匹配成功) --> E[放行请求 - ALLOW]
D -- 否 --> F{匹配到国家/地区地域白名单?}
F -- 是 (匹配成功) --> E
F -- 否 --> G{匹配到 IP 黑名单 / 黑名单 IP 组?}
F -- 否且已配置任意白名单 --> H
F -- 否且未配置白名单 --> G{匹配到 IP 黑名单 / 黑名单 IP 组?}
G -- 是 (匹配成功) --> H[阻断请求 - BLOCK]
G -- 否 --> I{匹配到国家/地区地域黑名单?}
I -- 是 (匹配成功) --> H
@@ -123,8 +124,8 @@ flowchart TD
### 2. 判决步骤细则
1. **白名单前置**:
为了防止误杀以及保障核心回源流量(如搜索引擎蜘蛛、CDN 回源 IP、办公区出口)的顺畅,WAF **优先匹配 IP 白名单与地域白名单**。一旦白名单匹配成功,直接绕过后续的所有黑名单检测和 CC 挑战,立刻放行。
为了防止误杀以及保障核心回源流量(如搜索引擎蜘蛛、CDN 回源 IP、办公区出口)的顺畅,WAF **优先匹配 IP 白名单与地域白名单**。一旦白名单匹配成功,直接绕过后续的所有黑名单检测和 CC 挑战,立刻放行。只要当前生效规则组配置了任意白名单,请求未命中全部白名单时会被拦截,白名单在这种情况下表现为准入名单。
2. **黑名单强力阻断**:
如果在白名单判定中未被捕获,请求将进入黑名单漏斗。一旦请求源 IP 命中 IP 黑名单、命中引用的黑名单 IP 组、或是处于被禁止的国家/地区范围内,Lua 引擎立即将 `ngx.ctx.openflare_waf_blocked` 标记设为 `true`。
3. **输出响应**:
命中黑名单后,Lua 提取匹配到规则组的 `block_status_code`(默认返回 418 / 403)和 `block_response_body`(拦截页面 HTML),通过 `ngx.say()` 输出响应体并执行 `ngx.exit(status)` 平滑退出请求,防止请求继续向后透传。
命中黑名单后,Lua 提取匹配到规则组的 `block_status_code`(默认返回 418 / 403)和 `block_response_body`(拦截页面 HTML),通过 `ngx.say()` 输出响应体并执行 `ngx.exit(status)` 平滑退出请求,防止请求继续向后透传。
+2 -2
View File
@@ -127,7 +127,7 @@ Manual execution:
Running from source:
```bash
cd openflare_agent
cd openflare-agent
export LOG_LEVEL='info'
go run ./cmd/agent -config /path/to/agent.json
```
@@ -135,7 +135,7 @@ go run ./cmd/agent -config /path/to/agent.json
Running compiled binary:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
export LOG_LEVEL='info'
./openflare-agent -config /path/to/agent.json
+4 -4
View File
@@ -134,7 +134,7 @@ Access `http://localhost:3000` for the first time, using the default credentials
First, build the admin frontend:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm build
@@ -143,7 +143,7 @@ pnpm build
Then, launch the Server:
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='replace-with-a-long-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -230,7 +230,7 @@ journalctl -u openflare-agent -f
Running from source:
```bash
cd openflare_agent
cd openflare-agent
export LOG_LEVEL='info'
go run ./cmd/agent -config /path/to/agent.json
```
@@ -238,7 +238,7 @@ go run ./cmd/agent -config /path/to/agent.json
Running compiled binary:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
export LOG_LEVEL='info'
./openflare-agent -config /path/to/agent.json
+3 -3
View File
@@ -1,6 +1,6 @@
# Deploy Relay (Tunnel Relay)
You will learn: The responsibilities of a TunnelRelay node, `openflare_relay` configuration parameters and environment variables, how to run the Relay via Docker, and how to build and deploy the Relay from source manually.
You will learn: The responsibilities of a TunnelRelay node, `openflare-relay` configuration parameters and environment variables, how to run the Relay via Docker, and how to build and deploy the Relay from source manually.
In the OpenFlare intranet penetration architecture, the **TunnelRelay node** plays a key role. Unlike standard Edge Nodes, in addition to running the traditional Agent (managing OpenResty for HTTPS/WAF processing), it co-locates the **Relay (frps tunnel manager)** service, responsible for listening to intranet client (OpenFlared) tunnel connections and relaying traffic.
@@ -21,7 +21,7 @@ Before deploying a TunnelRelay node, ensure:
## Configuration & Environment Variables
`openflare_relay` reads `relay.json` in the working directory by default on startup. Overriding options via environment variables is fully supported.
`openflare-relay` reads `relay.json` in the working directory by default on startup. Overriding options via environment variables is fully supported.
### Configuration Fields Details
@@ -68,7 +68,7 @@ If you prefer to run the Relay directly on a physical host or VM:
### 1. Compile the Binary
```bash
cd openflare_relay
cd openflare-relay
go build -o openflare-relay ./cmd/relay
```
+4 -4
View File
@@ -17,10 +17,10 @@ In production environments, we highly recommend explicitly configuring `SESSION_
## Build the Admin Frontend
The Go Server hosts static assets located in `openflare_server/web/build`. Before starting the Server from source, build the frontend:
The Go Server hosts static assets located in `openflare-server/web/build`. Before starting the Server from source, build the frontend:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm build
@@ -37,7 +37,7 @@ pnpm test
## Start with SQLite
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='replace-with-a-long-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -53,7 +53,7 @@ http://localhost:3000
## Start with PostgreSQL
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='replace-with-a-long-random-string'
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
export LOG_LEVEL='info'
+5 -5
View File
@@ -61,19 +61,19 @@ Internal Service (192.168.x.x)
## Server
`openflare_server` is the single-control-plane monolith:
`openflare-server` is the single-control-plane monolith:
* Gin provides the HTTP services.
* GORM accesses SQLite or PostgreSQL.
* The existing login system provides Admin Session management.
* Authentication sources support GitHub OAuth and standard OIDC logins with external account binding.
* The Go Server hosts the `openflare_server/web` static build assets.
* The Go Server hosts the `openflare-server/web` static build assets.
The Server does not directly SSH to nodes, nor does it modify node files online. It only stores control plane state, generates complete configuration versions, and lets nodes actively pull them via the Agent API.
## Agent
`openflare_agent` is a Go monolithic application:
`openflare-agent` is a Go monolithic application:
* Runs as a single binary on the node side.
* Reads or generates local node information on startup.
@@ -88,7 +88,7 @@ The node IP is maintained by default through Agent registration and heartbeat re
## Frontend
`openflare_server/web` is the official Next.js-based frontend:
`openflare-server/web` is the official Next.js-based frontend:
* Next.js 15 App Router.
* React 19.
@@ -219,5 +219,5 @@ Before modifying architectural code, please read:
1. [Product Boundaries](./index.md)
2. [Agent & Publish Model](./agent-design.md)
3. [Development Constraints](../../guildline/development-constraints.md)
3. [Development Constraints](../../guideline/Constraints.md)
4. [Repository Structure](./repository.md)
+13 -13
View File
@@ -2,7 +2,7 @@
You will learn: How to build OpenFlare's local development environment, start the Server, the Agent, and the Admin Frontend, run test and build commands, and understand the boundaries to respect before contributing code.
This page is aimed at contributors. Product boundaries, data model constraints, API conventions, and frontend layering specifications are governed by [Development Constraints](../../guildline/development-constraints.md); this page only provides actionable workflows for local development.
This page is aimed at contributors. Product boundaries, data model constraints, API conventions, and frontend layering specifications are governed by [Development Constraints](../../guideline/Constraints.md); this page only provides actionable workflows for local development.
## Repository Structure
@@ -22,7 +22,7 @@ For details on the physical directory structure and responsibilities of each mod
## Initializing Frontend Dependencies
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
```
@@ -38,7 +38,7 @@ pnpm build
SQLite Mode:
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='dev-session-secret'
export SQLITE_PATH='./openflare-dev.db'
export LOG_LEVEL='debug'
@@ -48,7 +48,7 @@ go run .
PostgreSQL Mode:
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='dev-session-secret'
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
export LOG_LEVEL='debug'
@@ -68,7 +68,7 @@ The default credentials are `root` / `123456`.
The frontend dev server listens to port `3001` by default and proxies requests to the backend via `NEXT_DEV_BACKEND_URL`:
```bash
cd openflare_server/web
cd openflare-server/web
export NEXT_DEV_BACKEND_URL='http://127.0.0.1:3000'
pnpm dev
```
@@ -96,7 +96,7 @@ Create a local `agent.json`:
Run:
```bash
cd openflare_agent
cd openflare-agent
export LOG_LEVEL='debug'
go run ./cmd/agent -config ./agent.json
```
@@ -108,21 +108,21 @@ If `openresty_path` is not configured, the Agent calls `openresty` by default. F
Server:
```bash
cd openflare_server
cd openflare-server
GOCACHE=/tmp/openflare-go-cache go test ./...
```
Agent:
```bash
cd openflare_agent
cd openflare-agent
GOCACHE=/tmp/openflare-go-cache go test ./...
```
Frontend:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm lint
pnpm typecheck
pnpm test
@@ -141,21 +141,21 @@ pnpm build
Admin static assets:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm build
```
Server binary:
```bash
cd openflare_server
cd openflare-server
go build -o openflare-server .
```
Agent binary:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
```
@@ -174,7 +174,7 @@ go build -o openflare-agent ./cmd/agent
Before contributing, verify:
1. The requirement matches [Product Boundaries](./index.md).
2. The implementation conforms to [Development Constraints](../guildline/development-constraints.md).
2. The implementation conforms to [Development Constraints](../guideline/development-constraints.md).
3. The change does not disrupt publishing, sync, rollback, or upgrading lifecycles.
4. Update corresponding documentation if configurations, deployments, APIs, or boundaries change.
5. High-risk edits must be accompanied by unit tests or equivalent integration testing.
+1 -1
View File
@@ -197,7 +197,7 @@ IP Group & Judgment Constraints:
* Update this document when the product range or system boundaries change.
* Update [System Architecture](./architecture.md) when the system structure or module responsibilities change.
* Update [Agent & Publish Model](./agent-design.md) when the publishing, synchronization, rollback, or Agent model changes.
* Update [Development Constraints](../../guildline/development-constraints.md) when developer constraints, code specifications, or API conventions change.
* Update [Development Constraints](../../guideline/Constraints.md) when developer constraints, code specifications, or API conventions change.
* Update README and [Deployment Instructions](../../deployment/deployment.md) when deployment methods change.
* Update [Configurations Reference](../reference/configuration.md) when configuration items change.
* Completed phases should no longer be backfilled as "version plans".
+4 -4
View File
@@ -4,10 +4,10 @@ You will learn: The responsibilities of Server, Agent, Frontend, scripts, and do
| Path | Responsibility |
| --- | --- |
| `openflare_server` | Gin + GORM + SQLite/PostgreSQL single monolithic control plane |
| `openflare_server/web` | Next.js 15 App Router Admin Frontend, hosted by Go Server |
| `openflare_agent` | Go monolithic Agent running on the node side |
| `openflare_relay` | Tunnel relay daemon running on public edges, managing frps processes |
| `openflare-server` | Gin + GORM + SQLite/PostgreSQL single monolithic control plane |
| `openflare-server/web` | Next.js 15 App Router Admin Frontend, hosted by Go Server |
| `openflare-agent` | Go monolithic Agent running on the node side |
| `openflare-relay` | Tunnel relay daemon running on public edges, managing frps processes |
| `openflared` | Tunnel client running on intranet servers, managing frpc processes |
| `scripts` | System helper scripts for installation, self-updating, etc. |
| `docs` | VitePress documentation website, design baselines, specifications, and configurations |
+5 -5
View File
@@ -40,7 +40,7 @@ graph TD
FlaredFrpc -->|Forward Local Request| LocalOrigin[5. Intranet Origin 192.168.x.x]
%% Control Flow & Heartbeats
Server[OpenFlare Server Control Plane] <-->|Relay API / Heartbeat| RelayManager[openflare_relay process]
Server[OpenFlare Server Control Plane] <-->|Relay API / Heartbeat| RelayManager[openflare-relay process]
Server <-->|Client API / Heartbeat| ClientManager[openflared process]
RelayManager -.->|Control Process & Config| RelayFrps
@@ -51,14 +51,14 @@ graph TD
style Server fill:#f96,stroke:#333,stroke-width:2px
```
* **Control Plane**: The Server maintains the database state. The `openflare_relay` process on relay nodes and the `openflared` process on intranet servers synchronize tunnel configurations via HTTP heartbeats and long-lived WebSocket connections.
* **Data Plane**: Public traffic enters the public edge Agent (OpenResty), where the TLS handshake, HTTPS termination, and WAF filtering are executed. It is then forwarded via `proxy_pass` to the co-located `openflare_relay (frps)` on the loopback address. `frps` encapsulates the HTTP requests into the encrypted TCP tunnel and sends them down to the intranet `openflared (frpc)`. Finally, `frpc` unpacks the requests and forwards them to the actual intranet origin service.
* **Control Plane**: The Server maintains the database state. The `openflare-relay` process on relay nodes and the `openflared` process on intranet servers synchronize tunnel configurations via HTTP heartbeats and long-lived WebSocket connections.
* **Data Plane**: Public traffic enters the public edge Agent (OpenResty), where the TLS handshake, HTTPS termination, and WAF filtering are executed. It is then forwarded via `proxy_pass` to the co-located `openflare-relay (frps)` on the loopback address. `frps` encapsulates the HTTP requests into the encrypted TCP tunnel and sends them down to the intranet `openflared (frpc)`. Finally, `frpc` unpacks the requests and forwards them to the actual intranet origin service.
---
## Relay (Server-side) Design
`openflare_relay` is a relay manager deployed on the public edge, running on nodes of type `tunnel_relay`.
`openflare-relay` is a relay manager deployed on the public edge, running on nodes of type `tunnel_relay`.
### 1. Core Architecture & Logic
* **Process Daemon**: The Relay process embeds the `frps` binary, spawning the `frps -c frps.toml` subprocess via `exec.Command` and using goroutines to asynchronously listen to its exit status. If `frps` exits unexpectedly, it automatically restarts using an exponential backoff policy.
@@ -98,7 +98,7 @@ Admin modifies tunnel/intranet port mappings -> Click Publish -> Generate new Tu
+-----------------------------------------------------------------------+-----------------------------------------------------------------------+
| |
v (Relay Side) v (Client Side)
openflare_relay heartbeat detects frps port/Token change openflared heartbeat detects tunnel_version change
openflare-relay heartbeat detects frps port/Token change openflared heartbeat detects tunnel_version change
Re-render local frps.toml Request full proxy configuration details
Kill and restart the frps process Re-render frpc_<relay_id>.toml configs
Report health status as healthy Restart or hot-reload changed frpc processes
+1 -1
View File
@@ -30,7 +30,7 @@ If you are new to OpenFlare, read the documents in the following order:
| Start Server from source code | [Launch Server](../deployment/server.md) |
| Configure GitHub or OIDC SSO | [SSO Login Configuration](./sso.md) |
| Upgrade Server or Agent | [Upgrade & Maintenance](../deployment/upgrade.md) |
| Participate in development or bug fixing | [Local Development](../design/development.md) and [Development Constraints](../../guildline/development-constraints.md) |
| Participate in development or bug fixing | [Local Development](../design/development.md) and [Development Constraints](../../guideline/Constraints.md) |
| Understand architecture and publishing | [System Architecture](../design/architecture.md) and [Agent & Publish Model](../design/agent-design.md) |
| View open-source references and credits | [Credits](./credits.md) |
+3 -3
View File
@@ -64,7 +64,7 @@ curl -I http://127.0.0.1:3000
2. If running from source, verify that the frontend static assets have been built:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm build
```
@@ -73,7 +73,7 @@ pnpm build
4. If accessing via the frontend dev server, verify the backend proxy configuration:
```bash
cd openflare_server/web
cd openflare-server/web
NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev
```
@@ -220,7 +220,7 @@ Domains without a bound certificate will not be added to the HTTPS configuration
Execute:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm lint
+1 -1
View File
@@ -152,4 +152,4 @@ Once logged into the management console, the Swagger page is accessible at:
/swagger/index.html
```
The Swagger definition file is stored in `openflare_server/docs`, generated by `swag init`.
The Swagger definition file is stored in `openflare-server/docs`, generated by `swag init`.
+11 -11
View File
@@ -7,7 +7,7 @@ You will learn: Common commands for starting, building, testing, installing, and
Start from source:
```bash
cd openflare_server
cd openflare-server
export SESSION_SECRET='replace-with-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -23,7 +23,7 @@ go run . --port 3000 --log-dir ./logs
Run tests:
```bash
cd openflare_server
cd openflare-server
GOCACHE=/tmp/openflare-go-cache go test ./...
```
@@ -32,7 +32,7 @@ GOCACHE=/tmp/openflare-go-cache go test ./...
Development:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm install
pnpm dev
```
@@ -40,14 +40,14 @@ pnpm dev
Build static assets:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm build
```
Linting and testing checks:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm lint
pnpm typecheck
pnpm test
@@ -58,21 +58,21 @@ pnpm test
Run from source:
```bash
cd openflare_agent
cd openflare-agent
go run ./cmd/agent -config /path/to/agent.json
```
Compile:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
```
Run tests:
```bash
cd openflare_agent
cd openflare-agent
GOCACHE=/tmp/openflare-go-cache go test ./...
```
@@ -81,14 +81,14 @@ GOCACHE=/tmp/openflare-go-cache go test ./...
Run from source:
```bash
cd openflare_relay
cd openflare-relay
go run ./cmd -config /path/to/relay.json
```
Compile:
```bash
cd openflare_relay
cd openflare-relay
go build -o openflare-relay ./cmd
```
@@ -128,7 +128,7 @@ Regenerate Swagger documentation:
```bash
go install github.com/swaggo/swag/cmd/swag@v1.16.4
cd openflare_server
cd openflare-server
swag init -g main.go -o docs
```
+1 -1
View File
@@ -46,7 +46,7 @@ The Client (Intranet Client) supports:
## Server CLI Arguments
```bash
cd openflare_server
cd openflare-server
go run . --port 3000 --log-dir ./logs
```
+48 -79
View File
@@ -1,100 +1,69 @@
# 发布第一份配置
你会学到:如何创建第一条网站配置、绑定源站与证书、发布配置版本,并确认 Agent 已经应用。
你会学到:如何以最简单的方式创建第一条反向代理规则、发布配置版本,并确认 Agent 已经拉取并应用配置。
OpenFlare 的发布链路以完整配置版本为中心。你在管理端修改网站配置后,需要发布并激活新版本,Agent 才会在后续 heartbeat 中拉取并应用。
OpenFlare 的发布链路以“不可变配置版本”为核心。你在管理端修改规则后,需要发布并激活新版本,在线的 Agent 才会自动同步并应用。
---
## 发布前检查
确认以下条件已经满足:
在开始发布前,请确保以下条件已满足:
| 项目 | 期望 |
| 检查项 | 状态要求 |
| --- | --- |
| Server | 可以登录管理端 |
| Agent | 至少一个节点在线 |
| 源站 | Agent 节点可以访问源站地址 |
| 域名 | 域名已经解析到 OpenResty 节点,或准备通过本地 hosts / curl Host 头验证 |
| HTTPS | 如需 HTTPS,证书已上传或托管 |
| **Server** | 控制面板已正常启动,且能顺利登录管理端 |
| **Agent** | 至少有一个 Agent 节点处于在线状态(可在「节点管理」中确认) |
| **源站** | 确认你的后端源站服务可从 Agent 宿主机正常访问 |
| **域名/测试** | 域名已完成 DNS 解析,或者准备好在客户端使用本地 hosts / curl 命令行 Host 头进行测试 |
## 创建网站配置
---
在管理端新增网站配置时至少需要:
## 步骤一:创建首个网站配置
| 字段 | 说明 |
| --- | --- |
| 网站名称 | 业务唯一标识;未显式填写时可使用主域名 |
| 域名 | 至少一个域名,第一项视为主域名 |
| 源站地址 | 合法的 `http://` 或 `https://` 上游地址 |
| 启用状态 | 只有启用的网站配置会参与发布渲染 |
为了快速验证,我们首先部署一个最基础的 HTTP 反代站点:
示例:
1. 登录控制面板,进入 **「网站配置」**,点击 **「创建网站」**。
2. 填写最基础的站点配置:
* **网站名称**:输入简易标识(如 `first-app`)。
* **域名 (Domains)**:输入用于测试的域名(如 `first.example.com`)。**第一项默认作为主域名**。
3. 配置上游源站(Upstream):
* **源站类型**:选择「标准反代」。
* **源站地址**:勾选手动输入并填入后端服务地址(如 `http://10.0.0.10:8080` 或测试专用的 `http://httpbin.org`)。
4. 点击保存,完成网站创建。
| 字段 | 示例 |
| --- | --- |
| 网站名称 | `app` |
| 域名 | `app.example.com` |
| 源站地址 | `http://10.0.0.20:8080` |
> [!TIP]
> **关于 HTTPS 与证书准备**
> 本节仅引导快速部署基础 HTTP 规则。若你需要导入已有的 SSL 证书或通过 ACME 协议向 Let's Encrypt 自动申请证书并开启 443 端口 HTTPS 代理,请前往 [新建反代配置](./proxy-config.md) 查阅详细步骤。
同一个域名只能属于一个网站配置。同一网站内的流量限制、反向代理和缓存配置按站点共享。
---
## 绑定证书
## 步骤二:预览并发布配置版本
HTTPS 证书按域名绑定。没有绑定证书的域名不会被自动放入 `443 ssl` server 块。
新增的网站配置仍保存在 Server 的数据库中,处于草稿状态,需要通过发布版本分发到数据面:
如果一个网站包含多个域名,发布渲染会按证书分组生成 HTTPS 配置,并确保所有域名仍属于同一站点快照。
1. 点击控制面板右上角的 **「配置预览」** 按钮,系统会展示本次新增路由的物理配置文件 Diff 差异。
2. 确认渲染出的配置内容正确无误后,点击 **「发布并激活」**。
3. 控制面将生成一个唯一的配置版本号(格式为 `YYYYMMDD-NNN`)。
## 发布与激活
---
标准链路:
## 步骤三:验证 Agent 生效状态
```text
修改规则 -> 预览 / 查看 diff -> 发布 -> 生成完整配置版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
```
发布成功后,控制面会立即通过 WebSocket 通知在线 Agent(若 WebSocket 离线,则会在 Agent 的心跳中作为差分感知):
发布时 Server 会读取全部启用的网站配置、OpenResty 主配置模板、性能参数与缓存参数,渲染完整 OpenResty 配置,计算 `checksum`,写入 `config_versions`,再切换激活版本。
## 验证结果
发布后在管理端确认:
| 位置 | 期望结果 |
| --- | --- |
| 节点列表 | 节点在线 |
| 节点详情 | 当前版本与激活版本一致 |
| 应用记录 | 最近一次应用成功 |
| 版本页面 | 新版本处于激活状态 |
在节点上确认 Agent 日志:
```bash
journalctl -u openflare-agent -n 100 --no-pager
```
用域名访问:
```bash
curl -I http://app.example.com
```
如果域名还没有正式解析,可以临时指定 Host 头访问节点 IP:
```bash
curl -I -H 'Host: app.example.com' http://NODE_IP
```
HTTPS 验证:
```bash
curl -I https://app.example.com
```
## 回滚
如果目标版本应用失败并回滚,Agent 会在本地阻断同一 `version + checksum` 的重复应用,直到控制面激活版本或 checksum 发生变化。
回滚到旧版本:
1. 打开配置版本页面。
2. 找到上一个确认可用的历史版本。
3. 重新激活该版本。
4. 查看节点应用记录,确认 Agent 应用成功。
1. **管理端验证**:进入「节点管理」-> 点击节点进入详情,检查**当前版本号**是否已成功变为刚刚发布的最新激活版本,且「应用记录」显示为成功。
2. **边缘节点验证**:你可以在 Agent 节点宿主机上通过日志检查应用情况:
```bash
# 如果是 Docker 部署的 Agent
docker logs openflare-agent
# 如果是本地 systemd 部署的 Agent
journalctl -u openflare-agent -n 50 --no-pager
```
3. **连通性测试**:
在客户端电脑上,使用 `curl` 携带测试 Host 请求 Agent 节点的 IP 地址进行最终验证:
```bash
curl -I -H "Host: first.example.com" http://AGENT_NODE_IP
```
若返回的状态码与后端源站响应一致,即代表你的第一条反代规则已成功在边缘节点落地生效!
+14 -8
View File
@@ -9,13 +9,16 @@ OpenFlare 是一套自托管的 OpenResty 控制面。它把反向代理网站
如果你第一次接触 OpenFlare,按下面顺序阅读:
1. [快速开始](./quick-start.md):用 Docker Compose 启动 Server,登录管理端,并接入第一个 Agent。
2. [基础使用](./usage.md):了解网站配置、源站、证书、发布、回滚和观测的常见操作。
3. [内网穿透与隧道使用](./tunnel-usage.md):学习部署 Relay 与 Client,实现安全、无公网 IP 反向穿透。
4. [WAF 安全防护使用](./waf-usage.md):掌握 IP 黑白名单、自动 IP 组 Expr 自动聚合、地域限制与 PoW CC 防护。
5. [WAF 自动 IP 组语法](./waf-ip-group-expr.md):编写自动 IP 组 Expr 规则,了解关键字含义和预设规则。
6. [部署说明](../deployment/deployment.md):把 Server 和 Agent 放到更接近生产的环境中运行。
7. [配置项参考](../reference/configuration.md):查 Server 环境变量、运行时 Option 和 Agent 配置字段。
8. [故障排查](./troubleshooting.md):按症状排查登录、数据库、节点同步、OpenResty 应用和前端构建问题。
2. [发布第一份配置](./first-site.md):快速新建一条最基础的 HTTP 反代站点规则,并验证节点生效状态。
3. [新建反代配置](./proxy-config.md):一步一步了解如何从证书导入与申请开始,配置 HTTPS 加密与上游源站管理。
4. [Pages 静态托管使用](./pages-usage.md):了解静态项目 ZIP 上传限制、SPA Fallback、以及内置 API 反向代理配置。
5. [内网穿透与隧道使用](./tunnel-usage.md):部署 Relay 与 Client,实现安全、无公网 IP 反向穿透。
6. [WAF 安全防护使用](./waf-usage.md):配置 WAF 规则组,掌握 IP 黑白名单、自动/订阅 IP 组、地域限制与 PoW CC 防护。
7. [WAF 自动 IP 组语法](./waf-ip-group-expr.md):编写自动 IP 组 Expr 规则,了解关键字含义和预设规则。
8. [Uptime Kuma 监控同步](./uptime-kuma.md):配置并使用 Uptime Kuma 自动差分同步和监控范围控制。
9. [SSO 登录配置](./sso.md):配置 GitHub 或 OIDC 实现第三方单点登录 (SSO) 接入。
10. [故障排查](./troubleshooting.md):按症状排查登录、数据库、节点同步、OpenResty 应用和前端构建问题。
11. [引用与致谢](./credits.md):查看系统依赖的优秀开源项目与社区致谢清单。
## 按角色查找
@@ -23,14 +26,17 @@ OpenFlare 是一套自托管的 OpenResty 控制面。它把反向代理网站
| --- | --- |
| 5 分钟内跑起管理端 | [快速开始](./quick-start.md) |
| 发布第一条反向代理配置 | [发布第一份配置](./first-site.md) |
| 配置域名证书与高级反代 | [新建反代配置](./proxy-config.md) |
| 托管单页应用或静态网站 | [Pages 静态托管使用](./pages-usage.md) |
| 配置内网穿透映射 | [内网穿透与隧道使用](./tunnel-usage.md) |
| 配置防 CC 与 IP 组拦截 | [WAF 安全防护使用](./waf-usage.md) |
| 编写自动 IP 组规则 | [WAF 自动 IP 组语法](./waf-ip-group-expr.md) |
| 自动同步监测站点状态 | [Uptime Kuma 监控同步](./uptime-kuma.md) |
| 接入或重装节点 Agent | [接入 Agent](../deployment/agent.md) |
| 从源码启动 Server | [启动 Server](../deployment/server.md) |
| 配置 GitHub 或 OIDC 登录 | [SSO 登录配置](./sso.md) |
| 升级 Server 或 Agent | [升级与维护](../deployment/upgrade.md) |
| 参与开发或修复问题 | [本地开发](../design/development.md) 与 [开发约束](../guildline/development-constraints.md) |
| 参与开发或修复问题 | [启动 Server](../deployment/server.md) 与 [开发约束](../guideline/Constraints.md) |
| 理解架构和发布模型 | [系统架构](../design/architecture.md) 与 [Agent 与发布模型](../design/agent-design.md) |
| 查看开源引用与致谢 | [引用与致谢](./credits.md) |
+86
View File
@@ -0,0 +1,86 @@
# Pages 静态托管使用
你会学到:如何在 OpenFlare 中使用 Pages 静态托管功能部署前端项目(如 React、Vue 等 SPA 或 VitePress、Hugo 等静态站点),配置单页应用 (SPA) Fallback 路由以及接口反向代理 (API Proxy),并理解不可变部署与 Agent 侧原子切换的底层逻辑。
---
## 核心机制与工作流
OpenFlare Pages 提供受 Cloudflare Pages 启发的 **Direct Upload (直接上传)** 静态网站托管服务。它与常规代理站点的不同之处在于,数据面的边缘节点 (Agent) 会将静态文件拉取并解压到节点本地,直接通过本地的 OpenResty 提供高性能的静态文件服务,无需维护额外的 Nginx 宿主机静态目录同步。
```text
[ 管理员 / CI ] ────── 1. 上传 ZIP 压缩包 ──────► [ OpenFlare Server ]
│
[ 访客浏览器 ] ◄────── 4. 访问页面 / 静态资源 ────────── [ Agent 节点 / OpenResty ]
▲
│
2. 检查 Checksum 并拉取 ZIP
3. 解压并原子切换 current 链接
```
1. **直接上传部署包**:在控制面上传预构建好的网站 `.zip` 压缩包,Server 会生成一条带有唯一 SHA-256 校验和 (Checksum) 的不可变部署记录。
2. **发布与推送**:在路由配置中将源站类型 (Upstream Type) 设为 `Pages 静态托管` 并绑定项目。发布配置版本后,Server 会广播给所有 Agent 节点。
3. **安全拉取与部署**:Agent 节点识别到新配置引用了新的 Pages 部署,增量下载 ZIP 包,校验 Checksum 保证一致性,并在本地解压、完成原子目录切换,重载 OpenResty 使服务生效。
---
## 第一步:上传部署包与创建 Pages 项目
1. 登录管理端控制面板,进入左侧导航 **「静态托管 (Pages)」**,点击 **「创建项目」**。
2. 填写项目基本信息:
* **项目名称**:业务名称(如 `我的前端应用`)。
* **项目标识 (Slug)**:URL 友好的唯一英文标识(如 `my-react-app`),将作为存储目录的文件夹名。
3. 设定站点目录结构与入口:
* **入口文件名**:默认为 `index.html`。
* **静态资源根路径 (RootDir)**:如果你的打包产物在压缩包的子目录下(例如打包出来的 zip 里包含一个 `dist/` 目录),则需要在这里填入子路径(如 `dist`)。若打包产物直接在 zip 根目录,留空即可。
4. **上传 ZIP 压缩包**:
* 上传你的项目静态资源打包生成的 `.zip` 文件。
> [!IMPORTANT]
> **部署包安全限制规范**
> 为了保障控制面和边缘节点的系统安全与性能,上传的部署包必须满足以下硬性指标,否则会被系统拒绝:
> * **大小限制**:ZIP 压缩包体积不得超过 **25 MiB**,解压后的总文件大小不得超过 **100 MiB**。
> * **数量限制**:解压后的文件总数不得超过 **1,000 个**。
> * **软链接拦截**:ZIP 包内禁止包含任何软链接 (Symbolic Link),防御软链接劫持攻击。
> * **Zip-Slip 防御**:压缩包中所有文件路径会被强制规范化,禁止使用 `..` 或以 `/` 开头,防止解压路径穿越攻击。
> * **入口文件检查**:你指定的入口文件(在静态资源根路径下,如 `dist/index.html`)**必须在压缩包中存在**。
---
## 第二步:配置高级路由规则
在项目详情的配置页面中,你可以根据前端项目类型开启以下高级特性:
### 1. 单页应用 (SPA) Fallback 路由
对于使用 React Router、Vue Router 等进行前端路由的单页应用 (SPA),当用户直接刷新类似 `/profile/settings` 的子路径时,边缘节点本地并不存在该物理文件,会导致 404 错误。
* **配置方式**:在项目设置中开启 **「SPA Fallback」**,并将路径设为入口文件(如 `/index.html`)。
* **生效逻辑**:开启后,如果访客请求的静态资源在物理上不存在,OpenResty 会自动降级重定向渲染入口文件,将路由交由前端 JavaScript 接管,避免 404 报错。
### 2. 内置 API 反向代理
为了避免前端请求后端 API 时遭遇跨域 (CORS) 限制,Pages 托管支持在同一个域名下直通后端 API。
* **配置方式**:
* **API 代理路径 (APIProxyPath)**:匹配的 URL 前缀(如 `/api`)。
* **后端服务地址 (APIProxyPass)**:后端 API 的源站地址(如 `http://10.0.0.5:8080`)。
* **重写规则 (APIProxyRewrite)**:可选。如果需要剥离前缀或重写路径,可使用正则匹配。例如:
* 剥离前缀:将请求 `/api/users` 重写为 `/users` 发送给后端,配置为 `^/api/(.*)$ /$1`。
* **生效逻辑**:所有以 `/api` 开头的请求会被直接转发至后端服务,而其他请求则继续由静态托管服务处理。
---
## 第三步:绑定代理路由并发布
Pages 项目配置并上传好部署包后,需要绑定到对外公开的域名上才能被访客访问。
1. 导航至左侧菜单 **「网站配置」**,创建或编辑一个代理站点。
2. 在「路由规则」中修改或添加一条路由:
* **源站类型 (Upstream Type)**:选择 **「Pages 静态托管」**。
* **绑定 Pages 项目**:选择你刚才创建的项目,并指定要激活的部署版本(默认会自动关联最新上传成功的部署)。
3. 点击右上角 **「配置预览」** -> 确认无误后点击 **「发布并激活」**。
## 运维与回滚
* **不可变部署与回滚**:每次在 Pages 项目下上传 `.zip` 文件,系统都会产生一个全新且唯一的部署版本。如果在历史部署列表中将上一版本设为激活并重新发布,可实现边缘节点的秒级回滚。
* **原子切换与自愈**:边缘节点(Agent)在拉取静态资源包时,会执行校验与流式解压,并通过原子切换物理目录来保障服务的无缝过渡。同时,Agent 会定时清理不再引用的历史部署包。
> [!TIP]
> 关于不可变部署、目录结构设计、增量拉取和安全防逃逸校验等底层架构与自愈细节,请参阅 [Pages 静态托管设计](../design/pages-design.md)。
+102
View File
@@ -0,0 +1,102 @@
# 新建反代配置
你会学到:如何一步一步在 OpenFlare 中从零新建并发布一个反向代理网站配置。本指南将指导你如何完成证书导入与申请、源站定义、路由规则配置、版本发布以及连通性验证。
---
## 推荐操作流程
在网关控制面中,建议遵循以下步骤新增反代规则:
```text
[ 步骤 1. 证书管理 ] ──► [ 步骤 2. 源站定义 (可选) ] ──► [ 步骤 3. 新增网站配置 ]
│
[ 步骤 5. 验证访问 ] ◄── [ 步骤 4. 发布与激活版本 ] ◄───────────────┘
```
---
## 第一步:证书准备(导入与申请)
在使用 HTTPS 安全加密流量前,你需要先配置对应的 TLS 证书。OpenFlare 支持以下两种证书获取方式:
### 1. 手动导入已有证书
如果你已经有第三方的证书(如腾讯云、阿里云申请的免费/收费证书,或者自签证书):
1. 导航至左侧菜单 **「证书管理」**,点击 **「导入证书」**。
2. 填入证书名称(如 `my-domain-cert`)。
3. 复制并粘贴你的 **证书内容 (PEM 格式公钥)** 以及 **证书私钥 (KEY 格式)**,点击保存。
### 2. 通过 ACME 协议自动申请
OpenFlare 集成了 ACME 客户端,支持自动向 Let's Encrypt 申请并到期续签证书:
1. **添加 ACME 账户**:进入「证书管理」->「ACME 账户」->「创建账户」,填入你的联系邮箱。
2. **添加 DNS 账户 (用于 DNS-01 验证)**:进入「证书管理」->「DNS 账户」->「创建账户」,选择你的 DNS 托管商(当前仅 Cloudflare)并填入 API Token 凭证。
3. **申请证书**:在「证书管理」中点击「申请证书」:
* 选择配置好的 ACME 账户和 DNS 账户。
* 输入需要托管证书的域名(支持通配符,如 `*.example.com`)。
* 点击申请,系统将自动配置 DNS 挑战码并向 CA 申请证书,且会在到期前 30 天自动触发续期。
---
## 第二步:准备上游源站(可选)
源站(Origin)代表被代理的后端真实服务地址。虽然在新建网站时可以直接填写 IP,但推荐先在源站库中进行注册,以便后续复用与维护:
1. 进入左侧导航 **「源站管理」**,点击 **「创建源站」**。
2. 填写源站名称(如 `production-api`)。
3. 填入合法的上游地址(如 `http://10.0.0.10:8080`),点击保存。
---
## 第三步:新建网站配置
证书和源站就绪后,即可创建核心网站代理路由:
1. 进入左侧导航 **「网站配置」**,点击 **「创建网站」**。
2. 填写网站基本配置:
* **网站名称**:业务唯一标识(如 `app-portal`)。
* **域名 (Domains)**:输入该站点绑定的域名列表。**第一项将自动视为主域名**。
3. 配置上游源站(Upstream):
* **源站类型**:选择「标准反代」。
* **源站地址**:从下拉框中选择第二步创建的源站;或者勾选手动输入并填入 `http://10.0.0.20:9000`。
4. **绑定证书启用 HTTPS**:
* 在域名列表中,点击域名旁边的配置按钮或 HTTPS 切换开关。
* 勾选「启用 HTTPS」,并从证书下拉列表中选择第一步准备好的证书。
* *注意:未绑定证书的域名只会保留 80 端口 HTTP 服务,不会被写入 443 端口代理中。*
5. 点击保存创建配置。
---
## 第四步:发布并生效配置
你在管理端新增的网站配置仅保存在 Server 数据库中,**不会立即生效**。必须生成配置版本快照并分发到 Agent 边缘节点:
1. 点击控制面板右上角的 **「配置预览」** 按钮。
2. 检查配置文件的 Diff 差异,确认你刚刚新增的 `server` 块以及证书绑定规则无误。
3. 点击 **「发布并激活」** 按钮。
4. **Agent 落地机制**:
* 数据面的 Agent 节点在心跳中发现激活的版本 Checksum 变更,会自动拉取完整的 OpenResty 配置文件和证书包到本地。
* 自动在本地执行配置校验(类似于 `openresty -t`),确认无语法错误后,执行平滑重载(`reload`)。
* *如果重载或校验失败,Agent 会安全阻断并回滚至上一稳定版本,保证节点高可用。*
---
## 第五步:连通性与回滚验证
### 1. 验证访问
你可以通过以下方式验证新配置是否生效:
* **浏览器访问**:直接在浏览器输入 `https://your-domain.com` 查看是否成功代理后端。
* **命令行验证**(推荐):使用 `curl` 探测:
```bash
curl -I https://your-domain.com
```
* **绕过 DNS 校验**:若你的域名尚未正式解析,可以临时指定 `Host` 请求头请求 Agent 节点物理 IP:
```bash
curl -I -H "Host: your-domain.com" https://AGENT_NODE_IP --insecure
```
### 2. 一键秒级回滚
如果发布的新配置导致了线上业务异常:
1. 导航至左侧 **「配置版本」** 菜单。
2. 在历史列表中找到发布前的上一个稳定版本。
3. 点击 **「激活此版本」**。
4. 所有在线 Agent 节点将在秒级自动重载回历史配置,实现秒级避险。
+6 -25
View File
@@ -164,34 +164,15 @@ journalctl -u openflare-agent -f
如果没有 systemd,脚本会输出手动启动命令。
## 4. 发布第一份配置
## 4. 后续步骤
在管理端完成以下操作:
完成控制面板启动和 Agent 节点接入后,你已经成功搭建好了 OpenFlare 网关的基础运行环境。接下来你可以按顺序继续阅读以下两份指南,开始部署你的第一个反代站点:
1. 新增网站配置,填写网站名称、域名和源站地址。
2. 确认网站配置处于启用状态。
3. 发布前查看预览或变更摘要。
4. 发布并激活新版本。
5. 等待 Agent 在后续 heartbeat 中发现版本并应用。
1. **发布第一个网站**:
* 请参阅 [发布第一份配置](./first-site.md)。它将引导你以最简单的方式(使用纯 HTTP)发布你的第一条代理规则,并验证节点落地状态。
2. **完整配置反向代理(HTTPS 与源站管理)**:
* 请参阅 [新建反代配置](./proxy-config.md)。它将指导你从证书导入与申请开始,配置域名 HTTPS 证书绑定、源站管理并预览发布。
版本号格式为 `YYYYMMDD-NNN`。历史版本不可变,回滚通过重新激活旧版本完成。
## 5. 验证是否成功
在管理端确认:
| 位置 | 期望结果 |
| --- | --- |
| 节点列表 | Agent 节点在线 |
| 节点详情 | 当前版本与激活版本一致 |
| 应用记录 | 最近一次应用成功 |
| 版本页面 | 新版本处于激活状态 |
在 Agent 节点确认:
```bash
journalctl -u openflare-agent -n 100 --no-pager
```
## 常见失败原因
+3 -3
View File
@@ -64,7 +64,7 @@ curl -I http://127.0.0.1:3000
2. 如果是源码运行,确认已经构建前端静态产物:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm build
```
@@ -73,7 +73,7 @@ pnpm build
4. 如果通过前端开发服务器访问,确认后端代理地址:
```bash
cd openflare_server/web
cd openflare-server/web
NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev
```
@@ -220,7 +220,7 @@ curl -Iv https://your-domain
执行:
```bash
cd openflare_server/web
cd openflare-server/web
corepack enable
pnpm install
pnpm lint
+49
View File
@@ -0,0 +1,49 @@
# Uptime Kuma 监控同步
你会学到:如何启用并配置 Uptime Kuma 自动同步集成,控制监测站点的同步范围与心跳探测参数,以及 OpenFlare 与 Uptime Kuma 差分同步的底层原理。
---
## 功能概述
在边缘多节点运维中,及时了解各个代理站点的可用性至关重要。为了避免手动在监控系统中重复录入站点信息,OpenFlare 提供了与开源监控服务 **Uptime Kuma** 的深度集成。
启用集成后,OpenFlare 会启动一个后台同步调度器,自动将管理端配置的代理站点同步为 Uptime Kuma 中的 HTTP 监控任务。支持检测范围过滤、差分属性更新以及对下线站点的自动清理。
---
## 第一步:在系统设置中配置集成
1. 登录管理端控制面板,进入左侧导航 **「系统设置」** -> **「Uptime Kuma 集成」**(或通过控制台中的集成配置入口)。
2. 配置以下核心连接参数:
* **启用状态 (Enabled)**:开启集成开关。
* **实例地址 (Instance URL)**:你的 Uptime Kuma 服务地址。例如 `http://192.168.1.100:3001` 或 `https://kuma.example.com`(必须包含协议前缀 `http://` 或 `https://`)。
* **用户名 (Username)** 与 **密码 (Password)**:具有管理权限的 Uptime Kuma 账户凭证,用于 API 鉴权。
---
## 第二步:控制监控范围与心跳参数
在集成面板中,你可以对监控范围和具体探测行为进行细粒度控制:
### 1. 监控范围 (Monitor Scope)
* **全部站点 (All)**:默认选项。OpenFlare 将自动同步所有**已启用**的代理路由站点。当新站点被创建且启用,或者旧站点被停用时,监控列表将自动增删。
* **选择站点 (Selected)**:仅监控指定站点。选择此模式后,可以点击 **「选择监控站点」** 弹出框。在弹出框内可以通过搜索过滤站点并进行勾选。被取消勾选或未勾选的站点将不会被同步(若已存在则会被自动清理)。
### 2. 监测频率与心跳设置
你可以为自动生成的监控项指定统一的探测参数:
* **同步间隔 (Sync Interval)**:自动差分同步的频率(分钟),默认为 `5` 分钟。即控制面每 5 分钟与 Uptime Kuma 进行一次状态比对。
* **心跳检测频率 (Interval)**:Uptime Kuma 探测站点的频率(秒),默认为 `60` 秒。
* **最大重试次数 (Retry)**:探测失败后,判定为 Down 之前的最大重试次数,默认为 `0`。
* **重试间隔时间 (Retry Interval)**:重试之间的等待秒数,默认为 `60` 秒。
* **请求超时时间 (Timeout)**:探测请求判定为超时的秒数,默认为 `48` 秒。
---
## 同步与清理机制
* **专属标签隔离**:所有自动创建的监控项均会绑定 `OpenFlare` 专属标签(紫蓝色)。同步和清理程序仅操作带有该标签的监控任务,**绝不干扰或破坏你在 Uptime Kuma 中手动创建的其他监控项**。
* **差分增量同步**:同步程序会周期性对比监控元数据。当检测到域名或心跳配置变更时,仅执行差分更新,避免中断历史统计数据;当站点停用或移出范围时,会自动执行监控下线清理。
> [!TIP]
> 关于 Uptime Kuma 监控同步的 Socket.IO 控制流、防污染标签模型及差分比对算法细节,请参阅 [Uptime Kuma 监控同步设计](../design/kuma-design.md)。
-171
View File
@@ -1,171 +0,0 @@
# 基础使用
你会学到:OpenFlare 中网站配置、源站、证书、版本、节点和观测分别是什么,以及日常使用时应按什么顺序操作。
OpenFlare 不直接在线修改节点上的 Nginx/OpenResty 配置。你在管理端修改的是控制面数据;只有发布并激活新版本后,Agent 才会拉取完整配置并应用到节点。
## 核心概念
| 概念 | 说明 |
| --- | --- |
| 网站配置 | 反向代理配置的聚合对象,一条网站配置可以绑定一个或多个域名 |
| 主域名 | `domains` 列表中的第一个域名,用作该网站的主要展示域名 |
| 源站 | 被反向代理访问的上游地址,例如 `http://10.0.0.10:8080` |
| 配置版本 | 一次发布生成的完整 OpenResty 配置快照,历史版本不可变 |
| 激活版本 | 当前全局生效的配置版本,所有节点默认消费同一份激活版本 |
| Agent | 节点侧进程,负责注册、心跳、同步、校验、reload 和失败回滚 |
## 推荐操作顺序
日常发布一条反向代理配置时,推荐按这个顺序:
1. 确认至少有一个 Agent 节点在线。
2. 新增或选择源站地址。
3. 新增网站配置,填写域名、源站和站点级配置。
4. 如需 HTTPS,上传或选择证书,并按域名绑定。
5. 预览配置或查看变更摘要。
6. 发布并激活新版本。
7. 在节点详情和应用记录中确认应用结果。
## 创建网站配置
网站配置至少需要:
| 字段 | 要求 |
| --- | --- |
| 网站名称 | 业务唯一标识;未显式填写时通常可使用主域名 |
| 域名 | 至少一个域名,第一项为主域名;任一域名全局只能属于一个网站 |
| 源站地址 | 合法的 `http://` 或 `https://` 地址 |
| 启用状态 | 只有启用的网站配置会参与发布渲染 |
示例:
| 字段 | 示例 |
| --- | --- |
| 网站名称 | `docs` |
| 域名 | `docs.example.com` |
| 源站地址 | `http://10.0.0.10:8080` |
| 回源 Host | `docs.internal.example.com` |
上游地址规则:
* 单上游可以携带 base path 或 query,例如 `https://app.example.com/base?from=openflare`。
* 多上游用于负载均衡时,每个上游必须是纯 `scheme://host[:port]`。
* 多上游在同一规则内应使用一致协议。
## 管理源站
源站是轻量目录,用来复用常见上游地址。网站配置关联源站后,仍会保存可渲染的 `origin_url` 快照,确保历史配置版本可以独立回放。
推荐做法:
* 把经常复用的内部服务地址维护为源站。
* 修改源站目录后,检查已发布的网站配置是否需要同步更新源站快照。
* 发布前使用预览或 diff 确认渲染结果。
## 托管 Pages 静态站点
Pages 用于托管已经构建完成的静态资源包。当前阶段只支持 Direct Upload,不执行 Git 构建、边缘函数或 SSR。
操作顺序:
1. 进入 **Pages** 页面,点击 **新建 Pages 项目**。
2. 填写项目名称、标识、描述;如为前端 history 路由应用,启用 **SPA fallback** 并填写回退路径,默认是 `/index.html`,也可以设置为 `/app.html` 等站点内绝对路径。
3. 创建后回到 Pages 项目列表,点击项目进入详情。
4. 在项目详情中上传 zip 静态资源包,并激活某个部署。
5. 新建或编辑网站规则,将回源方式切换为 **Pages 静态站点**,选择该 Pages 项目。
6. 发布并激活配置版本,Agent 会下载部署包、校验 checksum、解压到本地 Pages 目录,再由 OpenResty 本地服务静态文件。
Pages 项目只有在启用且存在激活部署后,才会出现在网站规则的 Pages 项目选择列表中。
## 启用 HTTPS
HTTPS 按域名绑定证书,而不是按整个网站统一强制启用。
操作顺序:
1. 在证书管理中上传或托管证书。
2. 进入网站配置,为需要 HTTPS 的域名选择证书。
3. 未绑定证书的域名会保留 HTTP,不会被自动放入 `443 ssl` server 块。
4. 发布并激活新版本。
如果一个网站包含多个域名,Server 发布时会按证书分组渲染 HTTPS 配置,同时保持这些域名属于同一份网站快照。
## 配置 WAF 与 PoW
安全防护统一从管理端侧边栏的 **WAF** 入口进入:
* WAF 页面维护全局规则组和自定义规则组。全局规则组始终应用到全部网站;自定义规则组可以在规则组内一键选择网站,也可以在网站详情的 `WAF` 分区绑定。
* 点击 WAF 页面中的 **管理 IP 组** 可以进入独立 IP 组页面。手动 IP 组直接维护 IP/IP 段;自动 IP 组使用 Expr 规则按单个 IP 聚合请求日志并定时更新名单;订阅 IP 组可从远程文本或 JSON 源定时同步。
* 自动 IP 组页面提供两个预设:单个 IP 请求数大于 100 且 404 占比不低于 80%;单个 IP 通过 IP 地址访问次数大于 50 且该访问占比大于 50%。保存前可点击 **测试规则** 查看当前日志窗口命中的 IP,保存后可点击 **立即执行** 更新组内名单,语法见 [WAF 自动 IP 组规则语法](./waf-ip-group-expr.md)。
* 在 WAF 规则组的黑白名单中,IP 维度既可以直接添加 IP/IP 段,也可以引用已有 IP 组。发布时版本只携带 IP 组引用 ID;Agent 会按 checksum 差异同步 IP 组成员,并在 Server 通过 WebSocket 广播 IP 组更新时实时落地到节点。
* `PoW` 是规则组内的一个配置 Tab,位于 `黑白名单` 与 `拦截返回` 之间,复用站点已有 PoW 执行逻辑,可将当前 PoW 配置应用到全部网站或当前规则组绑定的网站。
* 网站详情页不再单独编辑 PoW 规则,只展示全局 WAF 规则组并绑定自定义 WAF 规则组。PoW 的启用范围和规则内容应回到 WAF 页面统一维护。
WAF 规则组、网站绑定或 PoW 配置修改后,需要重新发布并激活配置版本,Agent 才会拉取并应用到 OpenResty。IP 组成员变化不需要重新发布版本;在线 Agent 会通过 WebSocket 增量更新,离线或未升级 WS 的 Agent 会在下一次心跳中按 checksum 差异补齐。
详细的 WAF 安全配置与拦截判决原理请查阅 [WAF 安全防护使用](./waf-usage.md)。
## 发布、激活与回滚
标准链路:
```text
修改配置 -> 预览 / diff -> 发布 -> 生成完整版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
```
发布时 Server 会读取全部启用的网站配置、OpenResty 主配置模板、性能参数、缓存参数和证书资源,生成完整配置并计算 `checksum`。
回滚不是修改历史版本,而是重新激活旧版本。Agent 发现激活版本变化后,会按普通同步流程拉取并应用。
## 查看节点与观测
节点页面适合回答三个问题:
| 问题 | 查看位置 |
| --- | --- |
| 节点是否在线 | 节点列表或节点详情 |
| 当前运行哪个版本 | 节点详情中的当前版本 |
| 最近一次应用是否成功 | 应用记录 |
节点 IP 默认由 Agent 注册和后续心跳自动回填。若在管理端填写或修改 IP,节点编辑会默认开启“锁定节点 IP”;开启后 Agent 上报不会覆盖该 IP。关闭锁定后,下一次 Agent 心跳或 WebSocket 状态上报会重新按自动逻辑更新。
访问分析和资源快照用于基础观测。OpenFlare 只保留受控时间窗口内的访问明细,不定位为通用日志平台。如果需要长期日志检索,应接入独立日志系统。
## 常见场景
### 新增一个内部服务反代
1. 确认源站服务可从 Agent 节点访问。
2. 在管理端新增网站配置。
3. 填写域名,例如 `app.example.com`。
4. 填写源站,例如 `http://10.0.0.20:8080`。
5. 发布并激活版本。
6. 在 Agent 节点或浏览器访问域名验证。
> [!TIP]
> 如果你的源站部署在内网、没有公网 IP 且 Agent 无法直接访问,请使用内网穿透隧道功能将服务映射至公网。详细操作步骤请查阅 [内网穿透与隧道使用](./tunnel-usage.md)。
### 给已有域名启用 HTTPS
1. 准备覆盖该域名的证书。
2. 在证书管理中上传或创建证书记录。
3. 回到网站配置,为对应域名选择证书。
4. 发布并激活版本。
5. 用浏览器或 `curl -I https://your-domain` 验证证书链和状态码。
### 回滚一次失败发布
1. 打开配置版本页面。
2. 找到上一个已知可用版本。
3. 重新激活该版本。
4. 查看节点应用记录,确认 Agent 已应用旧版本。
5. 修正配置后再发布新版本。
## 推荐实践
* 生产环境必须显式配置 `JWT_SECRET`,并优先使用 PostgreSQL。
* 修改网站配置后先看预览或 diff,再发布。
* 每次发布后检查节点详情与应用记录。
* 多节点部署时保持 Agent 到 Server 的网络路径稳定。
* 不在节点上手动修改 OpenFlare 托管的 OpenResty 配置文件;下次发布会覆盖这些文件。
+20 -4
View File
@@ -42,8 +42,22 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的
* **配置**:点击「创建 IP 组」-> 类型选择「手动」-> 按行直接填入 IP 或 CIDR 格式(例如 `192.168.1.100` 或 `10.0.0.0/24`)。
#### 2. 订阅 IP 组 (Subscription)
* **用途**:接入第三方威胁情报库或云厂商公布的 IP 范围。
* **配置**:类型选择「订阅」-> 输入抓取 URL(支持按行分隔的文本文件或标准的 JSON 格式)。控制面板的定时任务会周期性拉取订阅源并自动同步至该组名单中。
* **用途**:接入第三方开源威胁情报库、云厂商公布的官方网段(如 Cloudflare, GitHub Action IP 列表),或团队内部统一维护的动态 IP 源。
* **配置参数**:
* **订阅 URL**:必须是合法的 `http` 或 `https` 链接。
* **订阅格式**:支持 `Text` 与 `JSON` 两种数据格式:
* **Text 格式**:纯文本格式。按行分隔读取 IP/CIDR,会自动过滤掉以 `#` 开头的注释行和空白行。
* **JSON 格式**:当订阅源是一个结构化的 JSON 响应时,需要编写 **映射规则 (Mapping Rule)** 从 JSON 数据中提取 IP 列表。
* **映射规则**:使用类似 JSONPath 的轻量点语法定位 IP 数组,支持以 `[]` 展开数组。例如:
* 若 JSON 结构为 `{"data": {"ips": ["1.1.1.1", "2.2.2.2"]}}`,则映射规则填写 `$.data.ips[]`(或 `data.ips[]`)。
* 若 JSON 根节点本身即为字符串数组(如 `["1.1.1.1", "2.2.2.2"]`),映射规则留空或填写 `$` 即可。
* **同步间隔 (分钟)**:该订阅组自动同步的周期,默认为 `1440` 分钟(24小时),允许范围为 `5` 至 `43200` 分钟。
* **安全限额与同步频率**:
* 为防止恶意或超大订阅源造成系统负担,单次抓取上限限制为 **2 MiB**,网络拉取超时为 15 秒。
* Server 默认每 5 分钟在后台扫描一次到期的订阅 IP 组并拉取同步。
> [!TIP]
> 关于 WAF 的动态 IP 组异步差分同步模型(WebSocket 实时热同步、不触发 Nginx Reload 机制)以及高性能 Lua 缓存方案等底层设计细节,请参阅 [WAF 设计](../design/waf-design.md)。
#### 3. 自动 IP 组 (Automatic)
* **用途**:**最具杀伤力的防扫描、防爆破自动通道**。
@@ -130,7 +144,8 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的
│ (否)
▼
2. 匹配国家 / 省份地域白名单? ────────(是)─────► [ 放行 (ALLOW) ]
│ (否)
│ (否,且已配置任意白名单) ─► [ 拦截 (BLOCK) ]
│ (否,且未配置白名单)
▼
3. 匹配 IP 黑名单 / 黑名单 IP 组? ──────(是)─────► [ 拦截 (BLOCK) ] ──► 返回自定义状态码与HTML拦截页
│ (否)
@@ -152,7 +167,8 @@ IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的
## 最佳实践与调优建议
* **白名单前置与保护**:在部署高强度黑名单或地域屏蔽前,建议首先创建一个「受信任 IP 组」,放入你团队的办公室出口 IP、本地开发 IP 以及可能访问你的第三方回调源站 IP(如微信、支付宝支付回调地址),并在规则组的**白名单**中优先引入。这可以有效防止误杀。
* **白名单准入语义**:一旦某个生效规则组配置了 IP 白名单、白名单 IP 组或地域白名单,请求必须命中其中至少一条白名单规则才会继续放行;未命中白名单的请求会被拦截。白名单命中后仍会优先绕过后续黑名单与 PoW 检查。
* **白名单前置与保护**:在部署高强度黑名单或地域屏蔽前,建议首先创建一个「受信任 IP 组」,放入你团队的办公室出口 IP、本地开发 IP 以及可能访问你的第三方回调源站 IP(如微信、支付宝支付回调地址),并在规则组的**白名单**中优先引入。这可以有效防止误杀,但也会让未在白名单内的来源无法访问。
* **合理微调 PoW 难度**:人机 CC 挑战的哈希碰撞计算(`challenge_difficulty`)是一把双刃剑。
* 难度值 `3`:几乎瞬间完成计算,防 CC 强度低。
* 难度值 `4`:普通手机/低端浏览器在 100~300ms 内完成计算,防护性能良好。
+188
View File
@@ -0,0 +1,188 @@
# 开发约束
OpenFlare 代码修改的准入标准、后端/Agent/前端分层约束、数据模型边界、API 约定、数据库迁移要求和测试交付基线。
## 变更准入
新需求进入实现前,按以下顺序判断:
1. 是否符合 [产品边界](../design/index.md)。
2. 是否符合本文档的后端、Agent 与前端约束。
3. 是否会破坏现有发布、同步、回滚或升级主链路。
4. 是否需要同步更新部署、配置、README 或文档站页面。
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
## 技术基线
Server:
* Go 1.25+
* Gin
* GORM
* SQLite / PostgreSQL
* 现有登录体系
Agent:
* 单二进制
* 节点本地执行
* 通过 `openresty_path` 或默认 `openresty` 控制 OpenResty 二进制
* Docker 部署使用内置 OpenResty 的 Agent 镜像,不由 Agent 再控制独立 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、Agent、Frontend)的物理目录分层职责详见 [仓库结构](../design/index.md#仓库结构)。在此结构下,开发必须遵守以下核心分层规则:
* **Server 开发规则**:
* 禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
* **定时任务开发规则**:禁止将不同业务模块(如 Uptime Kuma 整合、WAF IP 同步等)的定时任务具体执行逻辑与状态堆积在单个 `cron.go` 文件中。各模块对应的定时任务结构体和运行逻辑必须在独立的 Go 文件中定义,`cron.go` 只允许承担统一注册、初始化与调度器启停的职责。
* **Agent 开发规则**:每个模块职责单一,外部命令调用集中封装,状态落盘与配置落盘分离。
* **Frontend 开发规则**:页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
## 数据模型规范
在定义和修改 Go/GORM 模型实体时,所有模型的业务边界与设计约束必须严格符合 [产品边界](../design/index.md)。
### 1. 当前有效实体
* **核心配置与反代**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
* **Pages 静态托管**:`pages_projects` (Pages 项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
* **节点与状态**:`nodes` (节点), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
* **内网穿透**:`tunnels` (隧道客户端), `tunnel_tokens` (隧道认证令牌,可选持久化).
* **观测与分析**:`node_request_reports` (请求上报), `node_access_logs` (访问明细), `node_metric_snapshots` (指标快照), `traffic_analytics_rollups` (流量聚合), `node_health_events` (健康事件).
* **系统配置与第三方登录**:`options` (全局参数), `auth_sources` (第三方认证源), `external_accounts` (外部绑定账号).
* **安全与 WAF**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
### 2. 底层数据库技术约束
在编写或修改模型时,必须严格遵守以下持久化与数据库设计准则:
* **禁止随意引入平台化新实体**:除非 [产品边界](../design/index.md) 设计发生调整并经评审。
## 数据库迁移
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
数据库版本号定义在 `openflare-server/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
数据库升级统一使用 goose。新的 goose provider、桥接逻辑、注册入口和具体迁移文件必须全部放在 `openflare-server/model/goose` 包下,`openflare-server/model` 根包只保留纯净实体类、旧框架兼容适配和必要的上下文注入。每次新增数据库升级都必须新建一个单独的 Go 文件,文件名使用 `openflare-server/model/goose/goose_<timestamp>_<description>.go`,例如 `openflare-server/model/goose/goose_202606020001_add_node_capabilities_json.go`。迁移文件必须同时包含该版本的 goose migration 构造函数、升级逻辑和校验逻辑;`model/goose/migrations.go` 只能作为注册入口和公共构造工具,禁止把具体迁移逻辑集中堆放在该文件中。
执行数据库升级时必须按以下步骤完成:
1. 判断是否需要升级数据库版本:凡是新增/删除/重命名表、字段、索引、约束、列类型、分表规则,或改变持久化数据语义,都必须升级。
2. 新增 `openflare-server/model/goose/goose_<timestamp>_<description>.go`,其中 `<timestamp>` 为 goose 版本号。文件头部或迁移构造函数附近必须包含注释,说明本次升级了什么内容,以及为什么需要升级。
3. 在该文件中实现独立迁移构造函数,并返回通过 `newGORMMigration(...)` 创建的 migration;随后只在 `openflare-server/model/goose/migrations.go` 的 `registeredMigrations(...)` 中新增一条注册项。
4. 在同一个单独迁移文件中写入升级逻辑。可通过 goose `Context` 调用 `ApplyCurrentSchema`、历史 backfill、默认数据初始化等公共能力;复杂数据修复必须显式处理,不得只依赖 `AutoMigrate`。
5. 在同一个单独迁移文件中写入升级后的校验逻辑。校验至少要覆盖新增表/字段/索引是否存在、关键默认数据是否存在、必要的数据回填是否成功。
6. 如果新迁移需要新的公共 backfill 或校验辅助函数,优先放在该迁移文件中;只有多个迁移共同复用时,才放到 `openflare-server/model/goose` 包内的公共文件中。不要把新 goose 框架代码放回 `openflare-server/model` 根包。
7. 补充迁移测试:至少覆盖从旧框架终点或上一 goose 版本升级后 schema version、字段/表结构、关键数据回填和校验结果。还应保留旧库从 v15/v17 桥接到 goose 的回归覆盖。
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
如果迁移失败或校验失败,启动流程必须中止,确保数据库能够回滚。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
## API 与鉴权
管理端与 Agent/Relay/Client API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
```json
{
"success": true,
"message": "",
"data": {}
}
```
约定:
* Agent API 固定放在 `/api/agent/*`,使用 `X-Agent-Token` 认证(节点专属 token)。
* **Relay API** 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 认证(同 TunnelRelay 节点)。
- Server 通过 token + `/api/relay/*` 路径区分 Relay 请求。
* **Tunnel Client API** 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 认证(独立的 tunnel_token)。
- OpenFlared 使用 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。
* **Admin Tunnel 管理 API** - `/api/tunnels/*`。
- CRUD tunnel 实体(创建、查询、更新、删除)。
- Token 管理(生成、轮换)。
- 强制同步(触发 Client 立即拉取新配置)。
* **Admin Pages 管理 API** - `/api/pages/*`。
- CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。
- 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。
* **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。
- Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
* 管理端登录成功后返回用户 token;管理端 API 只允许从 `OPENFLARE_TOKEN` 请求头读取登录凭证,不得通过 Cookie Session 放行。
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
* 系统仅单租户使用, 不得创建用户。
* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。
* 首次接入 Agent 可使用全局 `discovery_token`;首次接入 Client 由 Server 生成 tunnel_token,直接用于部署命令。
* Agent/Relay 请求头统一使用 `X-Agent-Token`;Client 请求头统一使用 `X-Tunnel-Token`。
## 前端请求、状态与类型
所有 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 工作流。
## 后续维护方式
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
* 产品边界变动:更新 [产品边界](../design/index.md)。
* 工程约束变动:更新本文档。
* 部署与配置变动:更新 [部署说明](../deployment/deployment.md)、[配置项](../reference/configuration.md) 与 README。
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](../design/index.md),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
@@ -13,7 +13,6 @@
- 日志方式
- 测试组织方式
- 依赖注入方式
- 现有编码风格
如果你不确定某个模块的职责,先通过代码上下文推断,不要随意新建重复模块。
@@ -43,12 +42,12 @@
3. 可维护性
- 修改前先分析影响范围。
- 尽量最小改动,不做无关重构。
- 不改变公开 API、数据库结构、配置格式,除非任务明确要求。
- 如果必须改变,要说明兼容性影响和迁移方案。
- 删除代码前确认没有调用方。
- 避免复制粘贴已有逻辑,应抽取到合适位置,但不要过度抽象。
- 对复杂业务逻辑添加必要注释,解释“为什么”,不要注释显而易见的“是什么”。
- 开发前先检查 utils、helpers 包,避免重复造轮子。
4. 测试要求
- 新增业务逻辑必须补充单元测试。
@@ -81,6 +80,8 @@
- 日志中不要打印密码、token、密钥、身份证号等敏感数据。
- 返回结构保持向后兼容。
- HTTP 状态码要语义正确。
- API 返回要有一致的格式,例如 { "code": 0, "message": "success", "data": {...} }。
- API 返回统一使用封装的方法 response.go,不要直接构造响应。
8. 日志与可观测性
- 关键路径要有必要日志。
-57
View File
@@ -1,57 +0,0 @@
# OpenFlare 特定项目开发准则 (Project Guidelines)
本文档定义了针对 **OpenFlare** 项目特定的后端开发约束、架构设计模式、GORM 数据库交互规范以及关键的 JSON 序列化避坑指南。所有参与项目后端开发的代码必须严格遵守。
---
## 1. 统一接口输入与响应处理(Controller 约束)
为了保证 API 的一致性,并消除控制器层中大量的样板代码,所有 Gin Controller 必须遵守以下规范:
### 1.1 参数解析与绑定
- **URL ID 参数解析**:必须调用统一的 `parseIDParam(c)` 辅助函数。严禁手写 `strconv.ParseUint(c.Param("id"), ...)`。
- **JSON 请求体绑定**:必须调用统一的 `bindJSON(c, &input)` 辅助函数。严禁手动调用 `c.ShouldBindJSON` 或 `json.NewDecoder` 并重复编写错误返回逻辑。
### 1.2 标准 API 响应
- 所有控制器方法的返回必须统一使用 `respondSuccess`、`respondFailure`、`respondBadRequest` 等标准方法。
- **严禁手写** `c.JSON(http.StatusOK, gin.H{...})`,以确保全局 API 响应字段结构(`success`/`message`/`data`)的百分之百一致。
> [!IMPORTANT]
> 接口的入参解析与响应统一规范定义在 [openflare_server/controller/response.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare_server/controller/response.go) 中。
---
## 2. 纯净工具类与数据库逻辑完全隔离(Utils 约束)
为了确保代码的可测试性、高内聚和低耦合,`utils/` 目录下的工具包必须保持纯净性:
### 2.1 无副作用与解耦原则
- 所有底层客户端与外部服务对接包(如 `utils/acme` 证书操作、邮件发送、DNS 供应商对接等)**必须完全剥离数据库或 GORM 依赖**。
- 工具包中严禁导入 `openflare/model` 包或直接访问数据库连接。它们应当只接受基础数据类型(如 `string`、`[]byte` 等)或本地无依赖结构体作为输入,并返回纯粹的计算或请求结果。
### 2.2 业务服务层(Service)职责
- 业务服务层 `service/` 负责数据库实体的加载、组装、事务持久化,并将底层的具体网络或加密操作委托给 `utils/` 工具包。
- 这样不仅保证了底层工具类的百分之百可单元测试性,也维护了清晰的系统分层。
---
## 3. Go 泛型切片去重与 JSON 序列化陷阱(Slice 约束)
在进行切片操作和去重时,必须使用泛型辅助函数,并注意 Go Slice 的空/零值在 JSON 序列化中的表现。
### 3.1 避免重复编写 map-seen 逻辑
- 禁止在 `service/` 或 `model/` 中手写临时的 map-seen 去重样板代码。
- 必须统一调用基于 Go 泛型实现的 [openflare_server/utils/slice.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare_server/utils/slice.go) 中的 `utils.Unique()` 辅助函数。
### 3.2 关键的 JSON 序列化规则(Nil vs. Empty Slice)
在 Go 中,未初始化的 `nil` 切片和已初始化的空切片 `[]T{}` 在内存中不同,它们在序列化为 JSON 时也有着决定性的区别:
- **`nil` 切片**:序列化为 JSON `null`。
- **空切片 (`make([]T, 0)`)**:序列化为 JSON `[]`。
> [!CAUTION]
> **开发避坑准则**:
> 1. GORM 数据库的很多 JSON/Array 字段(例如 `domain_cert_ids`、`upstreams` 等)或配置版本变更检测机制(如 `checksum` 计算和 `diff` 检测),要求空数组在 JSON 中必须表示为 `[]` 而非 `null`,否则会触发重复发布或解析失败的 bug。
> 2. `utils.Unique` 必须具备 **Nil-Preservation(空值保留)** 特性:
> - 如果传入的 Slice 是 `nil`,它必须返回 `nil`,以支持 `omitempty` 或在需要表示“缺失”的场景中输出 `null`。
> - 如果传入的 Slice 不是 `nil`(即使长度为 0 或去重后长度为 0),它必须返回非 nil 的空切片 `make([]T, 0)`,以确保序列化为 `[]`。
> 3. 所有类似的切片加工辅助函数都必须遵循此行为。
-308
View File
@@ -1,308 +0,0 @@
# 开发约束
你会学到:OpenFlare 代码修改的准入标准、后端/Agent/前端分层约束、数据模型边界、API 约定、数据库迁移要求和测试交付基线。
本文档融合原开发规范、前端规范与开发计划。
## 变更准入
新需求进入实现前,按以下顺序判断:
1. 是否符合 [产品边界](../design/index.md)。
2. 是否符合本文档的后端、Agent 与前端约束。
3. 是否会破坏现有发布、同步、回滚或升级主链路。
4. 是否需要同步更新部署、配置、README 或文档站页面。
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
## 技术基线
Server:
* Go 1.25+
* Gin
* GORM
* SQLite / PostgreSQL
* 现有登录体系
Agent:
* 单二进制
* 节点本地执行
* 通过 `openresty_path` 或默认 `openresty` 控制 OpenResty 二进制
* Docker 部署使用内置 OpenResty 的 Agent 镜像,不由 Agent 再控制独立 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、Agent、Frontend)的物理目录分层职责详见 [仓库结构](../design/repository.md)。在此结构下,开发必须遵守以下核心分层规则:
* **Server 开发规则**:
* 禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
* **定时任务开发规则**:禁止将不同业务模块(如 Uptime Kuma 整合、WAF IP 同步等)的定时任务具体执行逻辑与状态堆积在单个 `cron.go` 文件中。各模块对应的定时任务结构体和运行逻辑必须在独立的 Go 文件中定义,`cron.go` 只允许承担统一注册、初始化与调度器启停的职责。
* **Agent 开发规则**:每个模块职责单一,外部命令调用集中封装,状态落盘与配置落盘分离。
* **Frontend 开发规则**:页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
## 数据模型规范
在定义和修改 Go/GORM 模型实体时,所有模型的业务边界与设计约束必须严格符合 [产品边界](../design/index.md)。
### 1. 当前有效实体
* **核心配置与反代**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
* **Pages 静态托管**:`pages_projects` (Pages 项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
* **节点与状态**:`nodes` (节点), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
* **内网穿透**:`tunnels` (隧道客户端), `tunnel_tokens` (隧道认证令牌,可选持久化).
* **观测与分析**:`node_request_reports` (请求上报), `node_access_logs` (访问明细), `node_metric_snapshots` (指标快照), `traffic_analytics_rollups` (流量聚合), `node_health_events` (健康事件).
* **系统配置与第三方登录**:`options` (全局参数), `auth_sources` (第三方认证源), `external_accounts` (外部绑定账号).
* **安全与 WAF**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
### 2. 底层数据库技术约束
在编写或修改模型时,必须严格遵守以下持久化与数据库设计准则:
* **禁止随意引入平台化新实体**:除非 [产品边界](../design/index.md) 设计发生调整并经评审。
* **业务唯一性保障**:
* `proxy_routes.site_name` 作为业务唯一主标识。
* `proxy_routes.domains` 中的各域名必须全局唯一,不可跨站点冲突,列表第一项视为主域名。
* `nodes.node_id` 唯一标识节点(自动生成或由用户指定)。
* `tunnels.tunnel_id` 唯一标识内网穿透客户端(格式 `tun-<32hex>`,自动生成)。
* **兼容字段处理**:遗留的 `proxy_routes.domain` 只能作为 `domains[0]` 的只读/兼容镜像,新代码不得以该字段为唯一业务输入。
* **多上游及 Keepalive**:单上游时应支持 base path/query 并在 `proxy_pass` 中正确补齐 URI;多上游负载均衡时仅允许纯 `scheme://host[:port]`。
* **证书映射**:证书绑定必须通过逐域名平行的 `domain_cert_ids` 字段精确保存,未绑定证书的域名不得参与 HTTPS 渲染。
* **版本快照一致性**:`config_versions` 必须保存版本发布时的完整快照及 checksum 校验码,确保渲染结果不可变且全局单激活版本。
* **外部账户唯一绑定**:第三方登录必须通过 `external_accounts` 映射至本地唯一用户,原 `users.github_id` 仅用于向后兼容迁移,任何新登录流程禁止以此为业务输入。
* **Tunnel 与上游关联**:
* `proxy_routes.upstream_type = 'tunnel'` 时,必须指定 `tunnel_id`(关联到 `tunnels` 表)。
* 必须指定 `tunnel_target_addr`(内网目标地址,如 `192.168.1.100:8080`)和 `tunnel_target_protocol`(`http` 或 `https`)。
* 发布配置时,Server 自动将此上游渲染为 `http://127.0.0.1:{relay_vhost_port}`,Agent 依据 Host 头由 frps 路由。
* **Pages 与上游关联**:
* `proxy_routes.upstream_type = 'pages'` 时,必须指定 `pages_project_id`。
* 被引用的 Pages 项目必须启用,且必须存在当前激活部署。
* Pages 项目可启用 SPA fallback 并配置站点内绝对回退路径(默认 `/index.html`);回退路径必须经 Server 校验后进入发布快照,不得直接拼接未校验输入到 OpenResty 配置。
* Pages 部署包必须作为 Server 本地文件保存,数据库只保存部署元数据与文件清单;禁止把静态资源内容写入 `config_versions.support_files_json`。
* 发布配置时,Server 将 Pages 上游渲染为 OpenResty `root` + `try_files` 静态服务,并保留网站规则已有的 HTTPS、WAF、PoW、Basic Auth、限流与缓存配置。
* **TunnelRelay 节点配置**:
* `nodes.node_type = 'tunnel_relay'` 时,新增字段 `relay_bind_port`、`relay_vhost_http_port`、`relay_auth_token` 必须有合理默认值。
* `relay_bind_port` 默认 7000,`relay_vhost_http_port` 默认 8080。
* `relay_auth_token` 由 Server 自动生成(32 位随机字符串),不由用户输入。
* 相对静态配置(如 `relay_agent_access_addr`、`relay_client_access_addr`)由 Relay 心跳下发,Server 可记录但不纳入版本化流。
* **Tunnel 客户端状态**:
* `tunnels.status` 记录客户端在线/离线/待激活状态。
* `tunnels.current_version` / `tunnels.current_checksum` 记录当前已应用的配置版本。
* `tunnels.connected_relays` 以 JSON 数组形式存储已连接 Relay 的信息(relay_node_id、连接状态等)。
* `last_seen_at`、`last_error` 用于调试和可观测性。
## 数据库迁移
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
数据库版本号定义在 `openflare_server/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
v1-v7 视为历史初始基线,不再维护逐版本升级文件。v8-v17 是旧升级框架的兼容迁移链,只保留在 `openflare_server/model/migrate` 目录中用于老库升级。旧库启动时必须先按旧框架升级到 `legacyMigrationTerminalVersion`,再桥接到 goose;不得为了整理文件而改变已发布 v8-v17 迁移的语义。
从 v17 之后,数据库升级统一使用 goose。新的 goose provider、桥接逻辑、注册入口和具体迁移文件必须全部放在 `openflare_server/model/goose` 包下,`openflare_server/model` 根包只保留纯净实体类、旧框架兼容适配和必要的上下文注入。每次新增数据库升级都必须新建一个单独的 Go 文件,文件名使用 `openflare_server/model/goose/goose_<timestamp>_<description>.go`,例如 `openflare_server/model/goose/goose_202606020001_add_node_capabilities_json.go`。迁移文件必须同时包含该版本的 goose migration 构造函数、升级逻辑和校验逻辑;`model/goose/migrations.go` 只能作为注册入口和公共构造工具,禁止把具体迁移逻辑集中堆放在该文件中。
执行数据库升级时必须按以下步骤完成:
1. 判断是否需要升级数据库版本:凡是新增/删除/重命名表、字段、索引、约束、列类型、分表规则,或改变持久化数据语义,都必须升级。
2. 新增 `openflare_server/model/goose/goose_<timestamp>_<description>.go`,其中 `<timestamp>` 为 goose 版本号。文件头部或迁移构造函数附近必须包含注释,说明本次升级了什么内容,以及为什么需要升级。
3. 在该文件中实现独立迁移构造函数,并返回通过 `newGORMMigration(...)` 创建的 migration;随后只在 `openflare_server/model/goose/migrations.go` 的 `registeredMigrations(...)` 中新增一条注册项。
4. 在同一个单独迁移文件中写入升级逻辑。可通过 goose `Context` 调用 `ApplyCurrentSchema`、历史 backfill、默认数据初始化等公共能力;复杂数据修复必须显式处理,不得只依赖 `AutoMigrate`。
5. 在同一个单独迁移文件中写入升级后的校验逻辑。校验至少要覆盖新增表/字段/索引是否存在、关键默认数据是否存在、必要的数据回填是否成功。
6. 如果新迁移需要新的公共 backfill 或校验辅助函数,优先放在该迁移文件中;只有多个迁移共同复用时,才放到 `openflare_server/model/goose` 包内的公共文件中。不要把新 goose 框架代码放回 `openflare_server/model` 根包。
7. 补充迁移测试:至少覆盖从旧框架终点或上一 goose 版本升级后 schema version、字段/表结构、关键数据回填和校验结果。还应保留旧库从 v15/v17 桥接到 goose 的回归覆盖。
8. 同步更新设计/开发文档;如果管理端 API、配置项或用户可见行为变化,还要同步更新对应指南、配置参考和 Swagger 文档。
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
如果迁移失败或校验失败,启动流程必须中止,且不得提升数据库版本记录。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
## API 与鉴权
管理端与 Agent/Relay/Client API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
```json
{
"success": true,
"message": "",
"data": {}
}
```
约定:
* Agent API 固定放在 `/api/agent/*`,使用 `X-Agent-Token` 认证(节点专属 token)。
* **Relay API** 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 认证(同 TunnelRelay 节点)。
- Server 通过 token + `/api/relay/*` 路径区分 Relay 请求。
- Relay 心跳返回 frps 配置(bindPort、vhostHTTPPort、authToken)。
- Relay 上报进程状态、连接数、proxy 列表等指标。
* **Tunnel Client API** 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 认证(独立的 tunnel_token)。
- OpenFlared 使用 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。
- Client 心跳返回 tunnel 配置版本摘要。
- Client 可拉取完整配置(relay 列表 + frpc 代理定义)。
- Client 上报配置应用结果。
* **Admin Tunnel 管理 API** - `/api/tunnels/*`,要求管理端 `OPENFLARE_TOKEN`。
- CRUD tunnel 实体(创建、查询、更新、删除)。
- Token 管理(生成、轮换)。
- 强制同步(触发 Client 立即拉取新配置)。
* **Admin Pages 管理 API** - `/api/pages/*`,要求管理端 `OPENFLARE_TOKEN`。
- CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。
- 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。
* **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。
- Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。
* 总览与节点详情优先使用专用聚合接口。
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
* 管理端登录成功后返回用户 token;管理端 API 只允许从 `OPENFLARE_TOKEN` 请求头读取登录凭证,不得通过 Cookie Session 放行。
* 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root 级 `OPENFLARE_TOKEN`。
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
* 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。
* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。
* 首次接入 Agent 可使用全局 `discovery_token`;首次接入 Client 由 Server 生成 tunnel_token,直接用于部署命令。
* Agent/Relay 请求头统一使用 `X-Agent-Token`;Client 请求头统一使用 `X-Tunnel-Token`。
禁止暴露远程 shell 或任意命令执行入口,禁止在日志中打印完整 Token,禁止绕过占位符约束保存不可渲染的主配置模板。
## 发布与运行
发布逻辑必须保持:
* 发布时读取全部启用的 `proxy_routes`。
* 同时读取 OpenResty 主配置参数、反代性能参数与缓存参数。
* 读取 WAF 规则组、规则组引用的 IP 组与网站绑定关系,并在发布快照中保存可回放数据。
* 自动型 WAF IP 组只能由 Server 定时任务读取请求日志并执行 Expr 布尔规则,OpenResty Lua 与 Agent 不得直接访问请求日志库或执行自动挖掘逻辑。
* 发布版本不得展开 WAF IP 组成员;Agent 必须通过独立的 IP 组 checksum 差异同步和 WebSocket 增量广播维护本地 `waf_ip_groups.json`。
* **Pages 配置扩展**:区分上游类型,为 `upstream_type = 'pages'` 的代理规则生成 Pages 部署快照。
* OpenResty 侧:将 Pages 上游渲染为本地静态目录 `root` 与 `try_files`,启用 SPA fallback 时使用项目配置的回退路径,不得渲染 `proxy_pass`。
* Agent 侧:在应用 OpenResty 配置前,必须确保引用的 Pages 部署包已下载、checksum 校验通过并解压到 `pages_dir`。
* **内网穿透配置扩展**:区分上游类型,为 `upstream_type = 'tunnel'` 的代理规则生成独立的 tunnel 配置数据。
* OpenResty 侧:将 tunnel 上游自动渲染为 `http://127.0.0.1:{relay_vhost_port}`,必须保留原始 `Host` 请求头。
* Tunnel 侧:为每个 Client 生成完整的 relay 列表与 frpc 代理定义(frpc proxy 配置)。
* 生成完整 OpenResty 配置。
* 计算 `checksum`。
* 写入 `config_versions`(OpenResty 部分)+ 生成或更新 tunnel 配置版本数据。
* 通过切换 `is_active` 激活版本。
版本约束:
* 版本号格式固定为 `YYYYMMDD-NNN`。
* 同一版本号同时关联 OpenResty 配置与 Tunnel 配置,保证一致性。
* 不在线修改历史版本。
* 不做按节点分组的差异化版本。
* 预览与 diff 是只读能力,不产生发布记录。
Agent 必须满足:
* 启动后读取或生成本地 `node_id`。
* 周期性心跳与同步。
* 常规同步优先依据 heartbeat 返回的版本摘要判断。
* WS 连接升级开启且连接成功时,Agent 可通过 WS 接收激活版本摘要并立即同步;WS 失败或断开必须退回 HTTP heartbeat。
* 发现新版本时先备份旧文件。
* 写入主配置、路由配置与必要证书文件。
* 写入 WAF/PoW 运行时配置,并确保 WAF Lua 资源由 Agent 统一管理。
* 如果激活配置引用 Pages 部署,必须通过 Agent API 拉取部署包,校验配置快照中的 SHA-256 checksum,安全解压并原子切换本地当前部署目录;zip 路径不得逃逸 `pages_dir`,不得接受符号链接。
* WAF IP 组同步必须按组增量更新,不得在每次心跳或每次同步中传输全部 IP 组。
* 写入新配置后执行 `openresty -t -c <main_config_path>`,再 reload;reload 发现运行时未启动时允许直接启动 OpenResty。
* 周期性运行时健康检查不得调用 `openresty -t`,避免健康探针触发 upstream 域名同步解析;应优先请求本地 `openresty_observability_port` 上的 `/openflare/stub_status`,以 HTTP `200 OK` 作为 OpenResty 主进程和 worker 正在提供服务的判断依据。
* 新配置激活失败时必须先尝试用目标配置恢复运行,再回滚到旧配置并重新拉起 OpenResty。
* 回滚后 OpenResty 恢复正常时上报警告;如果本地没有历史主配置可恢复,必须允许写入内置安全兜底配置并拉起对外只监听 `80` 端口、统一返回 `503` 的 OpenResty 运行态;兜底配置仍需保留本地 `stub_status` 健康检查入口。
* 兜底运行态不得清除失败目标的阻断状态;应用记录必须能体现目标版本失败但 fallback runtime 已启动。存在历史主配置但回滚后仍无法恢复运行时上报失败。
* 某个目标 `version + checksum` 一旦应用失败并回退,Agent 必须在本地状态中阻断该目标的重复应用。
* Agent 维护本地 MaxMind mmdb 时,下载或刷新失败只能记录警告,不得阻断心跳、同步、配置应用或 OpenResty 健康检查。
OpenFlareRelay 必须满足:
* 启动后从 config 读取 Server 地址和 `agent_token`。
* 周期性向 Server 发送心跳,获取 frps 配置(bindPort、vhostHTTPPort、authToken)。
* 根据心跳响应生成 frps.toml,启动或更新 frps 进程。
* 上报 frps 进程健康状态、连接数、proxy 数等指标。
* frps 进程异常时自动重启,并上报失败信息。
* 可选支持 WebSocket 升级连接,接收实时配置推送。
OpenFlared 必须满足:
* 启动后从 config 读取 Server 地址和 `tunnel_token`。
* 周期性向 Server 发送心跳,获取 tunnel 配置版本摘要。
* 发现新版本后拉取完整 tunnel 配置(relay 列表 + frpc 代理定义)。
* 为每个 relay 生成独立 frpc.toml,启动新 frpc 进程或对已有进程执行热重载。
* 上报每个 frpc 进程的健康状态与连接情况。
* 配置应用失败时记录错误并上报,支持重试。
* 可选支持 WebSocket 升级连接,接收实时配置变更通知。
## 前端请求、状态与类型
所有 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 工作流。
## 后续维护方式
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
* 产品边界变动:更新 [产品边界](../design/index.md)。
* 工程约束变动:更新本文档。
* 部署与配置变动:更新 [部署说明](../deployment/deployment.md)、[配置项](../reference/configuration.md) 与 README。
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](../design/index.md),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
+4 -1
View File
@@ -4,7 +4,7 @@ layout: home
hero:
name: OpenFlare
text: 开源 CDN 编排与边缘安全平台
tagline: 支持反向代理、集中式配置同步、内网穿透(Tunnels)、动态 WAF 防护与人机防 CC 挑战。
tagline: 支持反向代理、集中式配置同步、Pages 静态托管、内网穿透(Tunnels)、动态 WAF 防护与人机防 CC 挑战。
actions:
- theme: brand
text: 快速开始
@@ -23,6 +23,9 @@ features:
- icon: 🌐
title: 分布式 CDN 编排
details: 将独立的 OpenResty 编排为高度协同的分布式 CDN 舰队,支持源站多负载均衡。
- icon: 📄
title: Pages 静态托管
details: 直接上传前端打包 zip 资产,由边缘节点拉取解压并提供高性能本地服务与 API 代理。
- icon: 🚇
title: 安全内网穿透 (Tunnels)
details: 对标 Cloudflare Tunnels,无须公网 IP 或暴露入向端口,安全穿透本地服务至公网。
@@ -0,0 +1,85 @@
# 交互式智能安装脚本实现计划
本计划旨在拓展 OpenFlare Agent 安装脚本的功能,支持交互式选择本地安装或 Docker 容器安装,并提供智能检测/在线安装 Docker 环境的机制,同时保留通过命令行传参进行自动化安装的既有能力。
---
## 1. 目标与背景 (Goal & Context)
* **需求背景**:当前安装脚本 `install-agent.sh` 仅支持在宿主机直接下载二进制并配置为本地 systemd 服务运行。随着 Docker 部署方式的普及,需要让用户在一键安装时能根据需要交互式选择 Docker 或本地部署,从而提升部署体验。
* **开发范围 (Scope)**:
* 支持交互式运行(未传参时):提示用户选择 Local 方式或 Docker 方式。
* 当选择 Docker 方式时,检测本地是否存在 `docker` 命令。若不存在,提示并在线安装 Docker(支持国内镜像源及自动测速选择最低延迟源)。
* 交互引导用户配置 `server_url` 和 `agent-token` 或 `discovery-token`。
* 如果选择 Docker,则最终拉取 Agent 镜像并运行容器;如果选择 Local,则继续原有的本地二进制下载及配置发布逻辑。
* 兼容非交互式模式:如果执行脚本时传递了任意参数,则跳过任何交互式提示,直接进行自动化安装(支持新参数 `--docker` / `--method docker` 来自动选用 Docker 部署)。
---
## 2. 设计与决策 (Design & Decisions)
### 交互工作流
1. 检查 `$#`(参数数量)。若 `$# -eq 0`,激活 `INTERACTIVE=true`。
2. 在交互模式下:
* 引导用户选择安装方法(1: Local, 2: Docker)。
* 若选择 Docker,调用 `Install_Docker` 检测并安装环境。
* 引导用户输入 `SERVER_URL` 并进行非空校验。
* 引导用户选择 Token 类型(1: Discovery Token, 2: Agent Token),并输入对应的 Token 值。
* 若选择 Local 且未传 `--openresty-path`,如果 `openresty` 二进制未能在 $PATH 中找到,交互提示用户手动输入 OpenResty 路径。
3. 非交互模式下:
* 解析命令行参数。
* 支持通过 `--docker` 或 `--method docker` 指定 Docker 容器安装。
* 依然根据传入的 `--server-url` 和 Token 自动执行安装,绝不进行任何交互。
### 数据流与架构图
```mermaid
graph TD
Start[执行 install-agent.sh] --> CheckArgs{是否有命令行参数?}
CheckArgs -- 是 (非交互模式) --> ParseArgs[解析参数]
ParseArgs --> IsDockerParam{是否指定 Docker?}
IsDockerParam -- 是 --> RunDocker[Docker 容器拉取与启动]
IsDockerParam -- 否 --> RunLocal[本地二进制下载与 systemd 服务创建]
CheckArgs -- 否 (交互模式) --> PromptMethod[提示选择 Local 或 Docker]
PromptMethod --> MethodChosen{选择结果}
MethodChosen -- Docker --> CheckDocker{本地有 Docker 吗?}
CheckDocker -- 否 --> PromptDockerInstall[询问是否安装 Docker?]
PromptDockerInstall -- 是 --> InstallDocker[在线安装 Docker + 配置国内镜像加速]
PromptDockerInstall -- 否 --> ExitScript[取消安装并退出]
CheckDocker -- 是 --> PromptServerUrl[提示输入 Server URL & Token]
InstallDocker --> PromptServerUrl
MethodChosen -- Local --> PromptLocalConfig[检测 OpenResty 并提示输入 Server URL & Token]
PromptServerUrl --> RunDocker
PromptLocalConfig --> RunLocal
```
---
## 3. 具体修改文件清单 (Proposed Changes)
### 边缘 Agent 与部署脚本
* #### [MODIFY] [install-agent.sh](file:///Users/ryan/DEV/Go/OpenFlare/scripts/install-agent.sh)
* 职责:
1. 引入交互式选择逻辑和 `Install_Docker` / `configure_accelerator` 函数。
2. 新增 `--docker` 和 `--method` 命令行参数支持。
3. 支持用户交互输入配置项。
4. 增加 Docker 镜像拉取、停止旧容器并启动新容器的安装路径。
* #### [MODIFY] [agent.md](file:///Users/ryan/DEV/Go/OpenFlare/docs/deployment/agent.md)
* 职责:更新一键安装说明文档,增加交互式模式的说明以及 `--docker` 参数的自动化 Docker 安装说明。
---
## 4. 验证计划 (Verification Plan)
### 自动化与脚本测试
* 在干净的测试环境运行脚本:
* `bash scripts/install-agent.sh` (测试交互模式 -> 选用 Local)
* `bash scripts/install-agent.sh` (测试交互模式 -> 选用 Docker)
* `bash scripts/install-agent.sh --server-url http://127.0.0.1:3000 --discovery-token mytoken` (测试自动 Local 安装)
* `bash scripts/install-agent.sh --server-url http://127.0.0.1:3000 --discovery-token mytoken --docker` (测试自动 Docker 安装)
### 数据面生效验证
* 通过 `docker ps` 和 `docker logs openflare-agent` 确认容器成功拉取并启动,环境参数注入正确。
@@ -0,0 +1,92 @@
# 登录集成 Cap 验证码实现计划
本计划规定了在 OpenFlare 系统的登录流程中集成 Cap(基于 Proof-of-Work 和无感浏览器检测的验证码)的具体开发步骤。
---
## 1. 目标与背景 (Goal & Context)
### 需求背景
为解决安全分析中识别到的“登录接口缺少防暴力破解/撞库逻辑”这一安全风险,我们需要在登录接口中集成 Cap 验证码服务。通过让客户端(爬虫/浏览器)在登录前必须求解一个 PoW 工作量难题并核销,显著提高恶意爬虫爆破的计算成本,从根本上防止针对登录接口的恶意爆破。
### 开发范围
1. **后端验证服务**:在 Server 端移植 `capjs-core` 的 PoW 校验算法(包括 FNV-1a、自定义 PRNG、SHA-256 检验、JWT 难题派发与核销缓存组件)。
2. **公开路由映射**:
* `POST /api/cap/challenge` (分发难题)
* `POST /api/cap/redeem` (核销难题并核发 `cap-token`)
3. **控制开关**:增加全局选项 `CapLoginEnabled`,管理员可动态启停。
4. **前端交互接入**:在登录页面引入 `cap-widget` 自定义组件,并在提交登录请求时附带 `cap_token`。
---
## 2. 设计与决策 (Design & Decisions)
### 核心对象与数据模型
本方案不涉及复杂数据库结构重构,但需要:
1. 在 `options` 表中保存 `CapLoginEnabled` (true/false) 选项, 默认为 True, 设置路径在 设置->系统设置->登录与注册开关。
2. 建立一个全局的、线程安全的内存验证码核销存储/核销缓存,具备过期清理功能,用于存放核销的 `cap-token` 以及消费过的 JWT 难题 Nonce(Signature),支持 Redis 与本地内存模式。
### API 与鉴权设计
1. **`POST /api/cap/challenge`**:公开接口。
2. **`POST /api/cap/redeem`**:公开接口。
3. **`POST /api/user/login`**:接受可选/必选的 `cap_token` 参数。
### 算法移植 (Proof-of-Work Go 实现)
* FNV-1a 状态机复现。
* 伪随机数生成器 (PRNG) 与 `strings.HasPrefix(sha256Hex, target)` 校验。
---
## 3. 具体修改文件清单 (Proposed Changes)
### 后端 Server
* #### [MODIFY] [constants.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/common/constants.go)
* 增加 `CapLoginEnabled` 全局常量/变量,默认 `true`。
* #### [MODIFY] [option.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/model/option.go)
* 在 `InitOptionMap` 和 `updateOptionMap` 中添加 `CapLoginEnabled` 的支持。
* #### [NEW] [prng.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/prng.go)
* 职责:实现 FNV-1a、FNV-1a resume 及 XORShift-based 自定义 PRNG 伪随机数算法。
* #### [NEW] [cap.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/cap.go)
* 职责:实现无状态 PoW 难题生成、验证及 JWT 校验。
* #### [NEW] [store.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/store.go)
* 职责:定义 `Store` 接口并提供默认的高性能、线程安全的内存 TTL 缓存核销存储实现。
* #### [NEW] [manager.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/manager.go)
* 职责:封装验证码的核心逻辑,暴露出 `Generate`、`Redeem` 与 `VerifyToken` 高阶 API。
* #### [NEW] [middleware.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/middleware.go)
* 职责:实现通用的 Gin 中间件 `VerifyMiddleware`。其不依赖任何 OpenFlare 业务代码,完全通过构造注入。
* #### [NEW] [cap.go (service)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/service/cap.go)
* 职责:适配器服务,将 OpenFlare 的全局参数(如 `JWTSecret`、`CapLoginEnabled`、`RDB`)注入并实例化全局的 `CapManager` 实例。
* #### [NEW] [cap.go (middleware)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/middleware/cap.go)
* 职责:极简的适配器中间件,直接调用并返回 `service.CapManager.VerifyMiddleware(scope)`。
* #### [NEW] [cap.go (controller)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/cap.go)
* 职责:实现 `GetCapChallenge` 和 `RedeemCapChallenge` 控制器。
* #### [MODIFY] [api-router.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/router/api-router.go)
* 职责:挂载 `/api/cap/challenge` 和 `/api/cap/redeem` 路由,并在 `/api/user/login` 上应用 `middleware.CapAuth("login")`。
* #### [MODIFY] [user.go (controller)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/user.go)
* 无需修改:登录控制器和入参结构体保持完全无侵入。
* #### [MODIFY] [misc.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/misc.go)
* 职责:在 `GetStatus` 中返回 `cap_login_enabled` 开关状态。
### 前端 Web
* #### [MODIFY] [public-status.ts](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/types/public-status.ts)
* 添加 `cap_login_enabled: boolean` 字段。
* #### [MODIFY] [auth.ts](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/types/auth.ts)
* 在 `LoginPayload` 中添加可选的 `cap_token?: string` 属性。
* #### [MODIFY] [login-form.tsx](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/features/auth/components/login-form.tsx)
* 动态载入 `cap-widget`(脚本 CDN:`https://cdn.jsdelivr.net/npm/cap-widget`)。
* 若后台返回 `cap_login_enabled === true`,则渲染 `<cap-widget data-cap-api-endpoint="/api/cap/" />` 组件。
* 在表单提交时,将 `cap-token` 塞入 `loginMutation` 的 Payload 中提交。
---
## 4. 验证计划 (Verification Plan)
### 自动化单元测试
* 针对 Go 中的 PoW 核心算法,编写单测 `openflare-server/service/cap_test.go`。
* 运行单测命令:`go test -v ./openflare-server/service/...`
### 手动功能与防暴力破解验证
1. 打开控制台选项开启 `CapLoginEnabled`。
2. 访问登录页面,观察人机验证组件静默加载并完成 PoW 计算,输入正确账户成功登录。
3. 使用 `curl` 模拟恶意爬虫不携带或携带错误的 `cap_token` 对登录 API 发起 POST 请求,预期被拦截并返回“验证码错误”。
4. 使用已被核销的同一 `cap_token` 二次请求登录,验证防重放失效机制。
@@ -0,0 +1,147 @@
# OpenFlare 引用替换为 GitHub 路径方案评估计划
## 1. 目标与背景 (Goal & Context)
* **需求背景**:当前 OpenFlare 内部组件(Server、Agent、Relay、Flared)之间采用本地包名引用(例如 `openflare`、`openflare-agent`),并使用 Go `replace` 相对路径指向本地目录。这导致代码无法直接以标准的 GitHub 路径(如 `github.com/rain-kl/openflare`)进行分发、远程安装或被外部引用(例如 `go install` 远程二进制会因为 replace 指令失效而报错)。
* **评估目标**:评估将本地引用替换为 `github.com/rain-kl/openflare` 格式的两种可行方案(单模块 Monorepo 方案 vs 多模块 Multi-Module 方案),分析各自的优缺点、工作量及对现有 CI/CD、Docker 镜像构建的影响,给出推荐方案。
## 2. 设计与决策 (Design & Decisions)
### 方案 A:标准 Go 多模块方案 (Multi-Module with Sub-paths)
保留当前 4 个独立的 Go 模块结构,在各自的 `go.mod` 中将模块名改写为符合 GitHub 结构的子路径:
- `openflare-server/go.mod` -> `module github.com/rain-kl/openflare/openflare-server`
- `openflare-relay/go.mod` -> `module github.com/rain-kl/openflare/openflare-relay`
- `openflare-agent/go.mod` -> `module github.com/rain-kl/openflare/openflare-agent`
- `openflared/go.mod` -> `module github.com/rain-kl/openflare/openflared`
同时,其他模块(Relay, Agent, Flared)的 `go.mod` 中的 `replace` 修改为:
`replace github.com/rain-kl/openflare/openflare-server => ../openflare-server`
#### 优缺点分析:
* **优点**:
- **模块边界清晰**:各二进制模块依赖独立。例如 `openflare-agent` 不会引入 Server 依赖的 GORM、Gin、Swagger 等库,保持各自模块的 `go.sum` 纯净。
- **改动小**:对 Dockerfile 和 GitHub Workflows 影响极小,构建上下文仍可保持原样。
* **缺点**:
- **远程安装不可用**:仍然需要在 `go.mod` 中保留 `replace` 指令。由于 Go 不允许在远程 `go install` 或 `go get` 时解析本地相对路径的 `replace` 指令,用户依然无法直接通过 `go install github.com/rain-kl/openflare/openflared/cmd/flared@latest` 安装,必须先克隆整个仓库到本地再构建。
---
### 方案 B:统一单模块方案 (Unified Single Module Monorepo - 推荐)
将整个仓库合并为一个 Go 模块。在仓库根目录下创建 `go.mod`,模块名为 `github.com/rain-kl/openflare`,并删除子目录中的所有 `go.mod` 和 `go.sum`。
所有内部包导入路径统一改写为:
- `"github.com/rain-kl/openflare/openflare-server/..."`
- `"github.com/rain-kl/openflare/openflare-relay/..."`
- `"github.com/rain-kl/openflare/openflare-agent/..."`
- `"github.com/rain-kl/openflare/openflared/..."`
#### 优缺点分析:
* **优点**:
- **彻底摆脱 replace**:完全不需要在 `go.mod` 中写 `replace` 指令,代码清爽、易于维持。
- **支持远程 Go 工具链**:用户和开发者可以直接使用 `go install github.com/rain-kl/openflare/openflared/cmd/flared@latest` 或 `go install github.com/rain-kl/openflare/openflare-agent/cmd/agent@latest` 远程下载并安装最新二进制。
- **版本依赖统一**:所有组件共享相同的依赖版本,避免了组件间因第三方库版本不一致导致潜在的运行时兼容问题。
* **缺点**:
- **依赖库大一统**:根目录的 `go.mod` 会包含 Server、Agent、Relay 等所有组件的依赖,但这只影响开发时的依赖下载,对最终编译出的二进制大小和运行效率**没有任何影响**(Go 编译器会自动进行死代码消除/树摇)。
- **构建配置变动**:Dockerfile 以及 GitHub Actions 需要修改构建上下文,从原本 COPY 子目录改为从根目录统一进行 COPY 和 `go build`。
---
## 3. 具体修改文件清单 (Proposed Changes)
如果采用**方案 B(推荐)**,需要修改的文件清单和逻辑如下:
### 根目录与配置文件
* #### [NEW] [go.mod](file:///Users/ryan/DEV/Go/OpenFlare/go.mod)
- 职责:全局单一 Go 模块定义,模块名:`github.com/rain-kl/openflare`。
* #### [DELETE] `openflare-server/go.mod` / `go.sum`
* #### [DELETE] `openflare-relay/go.mod` / `go.sum`
* #### [DELETE] `openflare-agent/go.mod` / `go.sum`
* #### [DELETE] `openflared/go.mod` / `go.sum`
### 源代码文件 (约 252 个 Go 文件)
* #### [MODIFY] `openflare-server/**/*.go`
- 职责:将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
* #### [MODIFY] `openflare-relay/**/*.go`
- 职责:将 `import "openflare-relay/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-relay/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
* #### [MODIFY] `openflare-agent/**/*.go`
- 职责:将 `import "openflare-agent/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-agent/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
* #### [MODIFY] `openflared/**/*.go`
- 职责:将 `import "openflare-flared/..."` 替换为 `import "github.com/rain-kl/openflare/openflared/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
### Dockerfile & Workflows
如果采用**方案 B(推荐)**,我们将继续保持每个组件(Server、Agent、Relay、Flared)编译并产生自己独立的 Docker 镜像(共 4 个镜像),但其 Dockerfile 的构建上下文(Build Context)统一提升至仓库根目录。具体调整细节如下:
* #### [MODIFY] [openflare-server/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/Dockerfile)
- 职责:由于 `openflare-server` 中没有独立的 `go.mod`,构建上下文必须在**仓库根目录**执行。
- 修改内容:
```dockerfile
# 更改 go-builder 阶段的 COPY 方式:
COPY go.mod go.sum ./
RUN go mod download
COPY openflare-server/ ./openflare-server/
# go build 指定编译子包:
RUN go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'" -o openflare ./openflare-server
```
* #### [MODIFY] [openflare-relay/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-relay/Dockerfile)
- 职责:适配单 go.mod 构建上下文。
- 修改内容:
```dockerfile
# 更改 builder 阶段的 COPY 方式:
COPY go.mod go.sum ./
RUN go mod download
COPY openflare-server/ ./openflare-server/
COPY openflare-relay/ ./openflare-relay/
# go build 指定编译子包:
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-relay/internal/config.Version=$VERSION'" -o openflare-relay ./openflare-relay/cmd/relay
```
* #### [MODIFY] [openflare-agent/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-agent/Dockerfile)
- 职责:适配单 go.mod 构建上下文。
- 修改内容:
```dockerfile
# 更改 builder 阶段的 COPY 方式:
COPY go.mod go.sum ./
RUN go mod download
COPY openflare-server/ ./openflare-server/
COPY openflare-agent/ ./openflare-agent/
# go build 指定编译子包:
RUN go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-agent/internal/config.Version=$VERSION'" -o /build/openflare-agent ./openflare-agent/cmd/agent
```
* #### [MODIFY] [openflared/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflared/Dockerfile)
- 职责:适配单 go.mod 构建上下文。
- 修改内容:
```dockerfile
# 更改 builder 阶段的 COPY 方式:
COPY go.mod go.sum ./
RUN go mod download
COPY openflare-server/ ./openflare-server/
COPY openflared/ ./openflared/
# go build 指定编译子包:
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflared/internal/config.Version=$VERSION'" -o flared ./openflared/cmd/flared
```
* #### [MODIFY] [.github/workflows/release.yml](file:///Users/ryan/DEV/Go/OpenFlare/.github/workflows/release.yml)
- 职责:更新 go build 构建命令及 ldflags 版本注入参数(例如将 `-ldflags "-X 'openflare/common.Version=$VERSION'"` 替换为 `-ldflags "-X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'"`,同时编译命令需要指向正确的子包目录,如 `./openflare-server`)。
---
## 4. 验证计划 (Verification Plan)
### 编译与运行测试
* 运行单测以确保各包逻辑正常:
`go test ./...`(在根目录执行)
* 本地编译各个二进制:
`go build -o bin/openflare-server ./openflare-server`
`go build -o bin/openflare-agent ./openflare-agent/cmd/agent`
`go build -o bin/openflare-relay ./openflare-relay/cmd/relay`
`go build -o bin/openflared ./openflared/cmd/flared`
* 启动服务并检查版本输出:
`./bin/openflare-server --version`
### Docker 构建验证
* 验证镜像构建命令:
`docker build -t openflare-server -f openflare-server/Dockerfile .`
`docker build -t openflare-agent -f openflare-agent/Dockerfile .`
`docker build -t openflare-relay -f openflare-relay/Dockerfile .`
`docker build -t openflared -f openflared/Dockerfile .`
+32
View File
@@ -0,0 +1,32 @@
# AI 接手计划模板
说明:本模板用于在 AI 代理上下文发生截断、压缩(Compaction)或将任务转移给另一个 AI 代理时使用,帮助新接手的 AI 快速恢复 100% 的工作状态。
---
## 1. 当前任务状态 (Current Status)
* **主线任务描述**:用一句话说清楚当前正在解决的核心问题。
* **开发分支/提交**:记录当前的工作目录、修改的未暂存文件、或 Git 临时分支名。
* **已完成内容 (Completed)**:
- [x] 功能 A 后端接口及单测
- [x] 前端面板表单组件
- **进行中内容 (In Progress)**:
- [/] 配置文件渲染与重写模块
- **待处理内容 (To Do)**:
- [ ] 边缘节点同步下载与校验落地
- [ ] 发布功能整体连通性验证
## 2. 核心文件与上下文 (Key Files & Context)
列出与当前开发高度相关的核心文件以及需要注意的特殊背景:
* `file:///path/to/core_file.go#L100-L150`:此处负责...,修改时需要注意...
* `file:///path/to/frontend_component.tsx`:用于展现...
## 3. 待决策与遗留问题 (Outstanding Decisions & Issues)
* [ ] **疑问/阻塞点**:是否需要支持某某场景?目前是如何兜底处理的?
* [ ] **异常与缺陷**:单测 `./controller/...` 运行时目前有 1 个 Fail,失败原因为...
## 4. 下一步行动指南 (Next Steps)
新接手 AI 进来后应当立即执行的前 3 步命令或编辑操作:
1. **第一步**:执行 `go test ./controller/...` 确认环境并复现 Fail 异常。
2. **第二步**:修改 `openflare-server/controller/xxx.go` 中的逻辑以修复该 Fail。
3. **第三步**:在管理端前端页面调试 xxx 表单的提交是否正常。
+47
View File
@@ -0,0 +1,47 @@
# 功能开发实现计划模板
说明:本模板用于指导新特性或重大模块开发前的技术规划,明确需求、范围与设计决策。
---
## 1. 目标与背景 (Goal & Context)
* **需求背景**:说明为什么要开发这个特性,解决什么业务痛点或安全隐患。
* **开发范围 (Scope)**:明确 V1 阶段的核心交付指标。哪些是本次必做的,哪些是留到后续迭代的(Out of Scope)。
## 2. 设计与决策决策 (Design & Decisions)
* **核心对象/数据模型**:
* 说明是否需要修改或新增数据库表(Gorm 结构体、Migration SQL,包括新增字段与关联)。
* **API 与鉴权设计**:
* 详细定义新增的 REST API 路由、请求载荷(Payload JSON)与响应格式。
* **数据流与架构图**:
* 使用 Mermaid 绘制数据或控制流的流向。
* **设计决策权衡**:
* 记录为何选用方案 A 而非方案 B。
## 3. 具体修改文件清单 (Proposed Changes)
按模块或组件列出需要修改的物理文件路径及修改点:
### 后端 Server
* #### [NEW] `openflare-server/model/entity.go`
* 职责:...
* #### [MODIFY] `openflare-server/service/feature.go`
* 职责:...
### 边缘 Agent 与 OpenResty
* #### [MODIFY] `openflare-agent/sync/sync.go`
* 职责:...
### 前端 Web
* #### [NEW] `openflare-server/web/features/feature-view.tsx`
* 职责:...
---
## 4. 验证计划 (Verification Plan)
### 自动化单元测试
* 运行的单测命令,如:`go test -v ./service/...`
### 数据面重载与生效验证
* 说明如何验证新配置在数据面落地。
* 提供验证测试的 `curl` 指令或手动操作路径。
+16
View File
@@ -0,0 +1,16 @@
# 开发计划与 AI 接手
本分区用于存放正在进行的开发计划(Plan)以及 AI 代理之间的工作接手计划(Handover)。这能帮助不同的 AI 代理快速掌握当前项目状态、历史上下文与后续开发步骤。
## 计划模板
在创建具体的开发计划或接手文档时,请使用以下标准模板进行初始化:
1. **[实现计划模板](./implementation-plan-template.md)**:用于新功能开发或重大重构前的技术方案规划。
2. **[AI 接手计划模板](./handover-plan-template.md)**:用于在上下文截断、压缩或更换 AI 代理时,记录当前任务状态、已完成内容与下一步执行计划。
## 使用建议
* **命名规范**:正在进行的开发计划建议命名为 `docs/plan/YYYYMMDD-[feature-name].md`,接手计划建议命名为 `docs/plan/handover-[task-name].md`。
* **物理隔离**:本目录下的计划文件只在开发周期内进行更新。当对应功能开发完毕并上线后,相应的计划文档应予以保留或归档,以供日后维护与新 AI 追溯历史决策。
* **禁止空文件**:请确保新创建的计划文档均基于对应的模板进行初始化填充。
-143
View File
@@ -1,143 +0,0 @@
# API 约定
你会学到:OpenFlare 管理端 API 与 Agent API 的响应结构、路径约定、鉴权方式和 Swagger 入口。
OpenFlare 的管理端 API 与 Agent API 都使用 JSON。
## 响应结构
成功与失败都应返回清晰的 `message`:
```json
{
"success": true,
"message": "",
"data": {}
}
```
## 路径约定
| 类型 | 约定 |
| --- | --- |
| 管理端 API | 由 `OPENFLARE_TOKEN` 请求头鉴权 |
| Agent API | 固定放在 `/api/agent/*` |
| Relay API | 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 鉴权(与 Agent 复用同一 token) |
| OpenFlared API | 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 鉴权(独立的 tunnel_token) |
| 只读接口 | 使用 `GET` |
| 变更类接口 | 使用 `POST` |
## WAF IP 组接口
管理端 WAF IP 组接口统一要求管理端 `OPENFLARE_TOKEN` 鉴权:
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `GET` | `/api/waf/ip-groups` | 查询 IP 组列表 |
| `GET` | `/api/waf/ip-groups/:id` | 查询单个 IP 组 |
| `POST` | `/api/waf/ip-groups` | 创建 IP 组 |
| `POST` | `/api/waf/ip-groups/test` | 测试自动 IP 组 Expr 规则,不保存配置,返回当前日志窗口内命中的 IP 列表 |
| `POST` | `/api/waf/ip-groups/:id/update` | 更新 IP 组 |
| `POST` | `/api/waf/ip-groups/:id/delete` | 删除 IP 组;已被规则组引用时会拒绝 |
| `POST` | `/api/waf/ip-groups/:id/sync` | 立即同步订阅型 IP 组或立即执行自动型 IP 组 |
IP 组 `type` 支持 `manual`、`automatic`、`subscription`。自动型 IP 组的 `auto_config` 是 JSON 对象
自动规则使用 Expr 语法,表达式必须返回布尔值。规则按单个 IP 的请求日志聚合指标计算,可用字段包括 `ip`、`request_count`、`status_404_count`、`status_404_ratio`、`ip_host_count`、`ip_host_ratio`、`client_error_count`、`server_error_count`、`last_seen_unix`。完整语法和字段含义见 [WAF 自动 IP 组规则语法](../guide/waf-ip-group-expr.md)。订阅格式支持 `text` 与 `json`:文本格式按行解析 IP/IP 段并忽略空行和 `#` 开头的注释;JSON 格式可通过映射规则选择数组,默认读取根数组。
## 鉴权
管理端登录成功后返回用户 token,后续所有管理端 API 必须在请求头中携带:
```http
OPENFLARE_TOKEN: <token>
```
Server 只从 `OPENFLARE_TOKEN` 读取管理端登录凭证,不再通过 Cookie Session 放行管理端 API。角色和用户状态仍以数据库中的当前用户记录为准。
Agent 正式请求统一使用节点专属 `agent_token`,首次接入可使用全局 `discovery_token`。Agent 请求头固定为:
```http
X-Agent-Token: <token>
```
### Agent WAF IP 组同步
Agent 心跳 payload 可携带本地 WAF IP 组 checksum:
```json
{
"waf_ip_group_checksums": {
"1": "sha256..."
}
}
```
Server 会根据当前激活版本引用的 IP 组 ID 对比 checksum,并在心跳响应顶层返回差异组:
```json
{
"waf_ip_groups": [
{
"id": 1,
"name": "自动黑名单",
"type": "automatic",
"enabled": true,
"ip_list": ["203.0.113.10"],
"checksum": "sha256..."
}
]
}
```
Agent 也可以在应用新版本后主动请求差异同步:
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/api/agent/waf/ip-groups/sync` | 根据 Agent 上报的 `ids` 与 `checksums` 返回不一致的 IP 组 |
当 Server 侧 IP 组更新时,已连接的 Agent WebSocket 会收到 `type = "waf_ip_groups"` 的消息,payload 为发生变化的 IP 组数组。Agent 应只更新收到的组,不要求 Server 每次下发全部 IP 组。
## OpenFlared API
OpenFlared 客户端用于内网穿透场景,通过 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。所有接口都使用 `X-Tunnel-Token` 鉴权,Server 会校验节点 `node_type = tunnel_client`,否则返回 `403`。
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/api/flared/heartbeat` | 客户端心跳,刷新在线状态并返回 tunnel 配置版本摘要 |
| `GET` | `/api/flared/config/active` | 拉取完整的 tunnel 路由配置(relay 列表 + frpc 代理定义) |
| `POST` | `/api/flared/apply-log` | 上报配置应用结果(success / warning / failed) |
| `GET` | `/api/flared/ws` | 升级为 WebSocket,用于实时接收 `active_config` 推送 |
心跳请求示例:
```http
POST /api/flared/heartbeat
X-Tunnel-Token: <tunnel_token>
Content-Type: application/json
{
"client_version": "v0.2.0",
"frp_version": "0.61.0",
"tunnel_status": "running",
"connected_relays": [
{ "relay_node_id": "node-relay-1", "status": "healthy", "proxy_count": 3 }
],
"current_version": "v1",
"current_checksum": "sha256..."
}
```
心跳响应包含 `active_config` 摘要与 `tunnel_settings`(包含心跳间隔、WebSocket 升级开关等运行时参数)。当 Server 发布新版本时,已连接的 OpenFlared WebSocket 会收到 `type = "active_config"` 消息,payload 为版本摘要,客户端应立即拉取完整配置并应用。
日志中不得打印完整 Token。
## Swagger
登录管理端后可访问:
```text
/swagger/index.html
```
Swagger 文件位于 `openflare_server/docs`,由 `swag init` 生成。
+11 -11
View File
@@ -7,7 +7,7 @@
源码启动:
```bash
cd openflare_server
cd openflare-server
export JWT_SECRET='replace-with-random-string'
export SQLITE_PATH='./openflare.db'
export LOG_LEVEL='info'
@@ -23,7 +23,7 @@ go run . --port 3000 --log-dir ./logs
测试:
```bash
cd openflare_server
cd openflare-server
GOCACHE=/tmp/openflare-go-cache go test ./...
```
@@ -32,7 +32,7 @@ GOCACHE=/tmp/openflare-go-cache go test ./...
开发:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm install
pnpm dev
```
@@ -40,14 +40,14 @@ pnpm dev
构建静态产物:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm build
```
检查:
```bash
cd openflare_server/web
cd openflare-server/web
pnpm lint
pnpm typecheck
pnpm test
@@ -58,21 +58,21 @@ pnpm test
源码运行:
```bash
cd openflare_agent
cd openflare-agent
go run ./cmd/agent -config /path/to/agent.json
```
编译:
```bash
cd openflare_agent
cd openflare-agent
go build -o openflare-agent ./cmd/agent
```
测试:
```bash
cd openflare_agent
cd openflare-agent
GOCACHE=/tmp/openflare-go-cache go test ./...
```
@@ -81,14 +81,14 @@ GOCACHE=/tmp/openflare-go-cache go test ./...
源码运行:
```bash
cd openflare_relay
cd openflare-relay
go run ./cmd -config /path/to/relay.json
```
编译:
```bash
cd openflare_relay
cd openflare-relay
go build -o openflare-relay ./cmd
```
@@ -129,7 +129,7 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/unin
```bash
go install github.com/swaggo/swag/cmd/swag@v1.16.4
cd openflare_server
cd openflare-server
swag init -g main.go -o docs
```
+1 -1
View File
@@ -46,7 +46,7 @@ Client (内网客户端) 支持:
## Server 命令行参数
```bash
cd openflare_server
cd openflare-server
go run . --port 3000 --log-dir ./logs
```
+1 -3
View File
@@ -8,6 +8,4 @@
| --- | --- |
| [配置项](./configuration.md) | Server 环境变量、命令行参数、运行时 Option 与 Agent 配置字段 |
| [命令与脚本](./cli.md) | 常用启动、构建、测试、安装和卸载命令 |
| [API 约定](./api.md) | 管理端 API 与 Agent API 的响应结构、鉴权和路径约定 |
| [仓库结构](../design/repository.md) | `openflare_server`、`openflare_agent`、`openflare_relay`、`openflared` 模块的职责与分层目录说明 |
| [部署与升级](../deployment/) | Server 与 Agent 的部署、配置与升级指南(见专属分区) |
| [仓库结构](../design/index.md#仓库结构) | `openflare-server`、`openflare-agent`、`openflare-relay`、`openflared` 模块的职责与分层目录说明 |
+2 -4
View File
@@ -1,8 +1,9 @@
module openflare
module github.com/rain-kl/openflare
go 1.25.7
require (
github.com/appleboy/gin-jwt/v2 v2.10.3
github.com/bwmarrin/snowflake v0.3.0
github.com/dgraph-io/ristretto/v2 v2.2.0
github.com/expr-lang/expr v1.17.8
@@ -32,14 +33,11 @@ require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/appleboy/gin-jwt/v2 v2.10.3 // indirect
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/bytedance/sonic v1.12.9 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
+8 -26
View File
@@ -8,14 +8,12 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/appleboy/gin-jwt/v2 v2.10.3 h1:KNcPC+XPRNpuoBh+j+rgs5bQxN+SwG/0tHbIqpRoBGc=
github.com/appleboy/gin-jwt/v2 v2.10.3/go.mod h1:LDUaQ8mF2W6LyXIbd5wqlV2SFebuyYs4RDwqMNgpsp8=
github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4=
github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A=
github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@@ -25,13 +23,6 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
@@ -60,15 +51,12 @@ github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
@@ -99,8 +87,6 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
@@ -109,8 +95,6 @@ github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
@@ -152,8 +136,6 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU=
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@@ -200,8 +182,6 @@ github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -238,6 +218,12 @@ github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -249,9 +235,6 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -359,4 +342,3 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
@@ -12,19 +12,15 @@ ENV CGO_ENABLED=0 \
GOARCH=${TARGETARCH}
WORKDIR /build
COPY openflare_server/go.mod openflare_server/go.sum ./openflare_server/
COPY openflare_agent/go.mod openflare_agent/go.sum ./openflare_agent/
WORKDIR /build/openflare_agent
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
WORKDIR /build
COPY openflare_server ./openflare_server
COPY openflare_agent ./openflare_agent
WORKDIR /build/openflare_agent
COPY openflare-server ./openflare-server
COPY openflare-agent ./openflare-agent
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -trimpath -ldflags "-s -w -X 'openflare-agent/internal/config.Version=$VERSION'" -o /build/openflare-agent ./cmd/agent
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-agent/internal/config.Version=$VERSION'" -o /build/bin/openflare-agent ./openflare-agent/cmd/agent
FROM openresty/openresty:alpine
@@ -36,7 +32,7 @@ RUN apk add --no-cache ca-certificates tzdata perl libmaxminddb \
ENV OPENFLARE_OPENRESTY_PATH=openresty \
OPENFLARE_DATA_DIR=/data
COPY --from=builder /build/openflare-agent /usr/local/bin/openflare-agent
COPY --from=builder /build/bin/openflare-agent /usr/local/bin/openflare-agent
EXPOSE 80 443 18081
ENTRYPOINT ["/usr/local/bin/openflare-agent"]
@@ -8,17 +8,17 @@ import (
"os/signal"
"syscall"
"openflare-agent/internal/agent"
"openflare-agent/internal/config"
"openflare-agent/internal/geoipupdate"
"openflare-agent/internal/heartbeat"
"openflare-agent/internal/httpclient"
"openflare-agent/internal/logging"
"openflare-agent/internal/nginx"
"openflare-agent/internal/state"
syncservice "openflare-agent/internal/sync"
"openflare-agent/internal/updater"
"openflare-agent/internal/wsclient"
"github.com/rain-kl/openflare/openflare-agent/internal/agent"
"github.com/rain-kl/openflare/openflare-agent/internal/config"
"github.com/rain-kl/openflare/openflare-agent/internal/geoipupdate"
"github.com/rain-kl/openflare/openflare-agent/internal/heartbeat"
"github.com/rain-kl/openflare/openflare-agent/internal/httpclient"
"github.com/rain-kl/openflare/openflare-agent/internal/logging"
"github.com/rain-kl/openflare/openflare-agent/internal/nginx"
"github.com/rain-kl/openflare/openflare-agent/internal/state"
syncservice "github.com/rain-kl/openflare/openflare-agent/internal/sync"
"github.com/rain-kl/openflare/openflare-agent/internal/updater"
"github.com/rain-kl/openflare/openflare-agent/internal/wsclient"
)
func main() {
@@ -8,11 +8,11 @@ import (
"strings"
"time"
"openflare-agent/internal/config"
"openflare-agent/internal/observability"
"openflare-agent/internal/protocol"
"openflare-agent/internal/state"
"openflare-agent/internal/wsclient"
"github.com/rain-kl/openflare/openflare-agent/internal/config"
"github.com/rain-kl/openflare/openflare-agent/internal/observability"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/state"
"github.com/rain-kl/openflare/openflare-agent/internal/wsclient"
)
type HeartbeatService interface {
@@ -10,9 +10,9 @@ import (
"testing"
"time"
"openflare-agent/internal/config"
"openflare-agent/internal/protocol"
"openflare-agent/internal/state"
"github.com/rain-kl/openflare/openflare-agent/internal/config"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/state"
)
type fakeHeartbeatService struct {
@@ -6,15 +6,16 @@ import (
"errors"
"fmt"
"net"
"openflare/utils"
"openflare/utils/geoip"
"openflare/utils/geoip/iputil"
"os"
pathpkg "path"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/rain-kl/openflare/openflare-server/utils"
"github.com/rain-kl/openflare/openflare-server/utils/geoip"
"github.com/rain-kl/openflare/openflare-server/utils/geoip/iputil"
)
const (
@@ -5,11 +5,12 @@ import (
"encoding/json"
"errors"
"net"
"openflare/utils/geoip"
"os"
"path/filepath"
"testing"
"time"
"github.com/rain-kl/openflare/openflare-server/utils/geoip"
)
func TestLoadDefaultsToManagedBinaryPaths(t *testing.T) {
@@ -9,8 +9,8 @@ import (
"path/filepath"
"time"
"openflare-agent/internal/geoipdata"
"openflare/utils/geoip"
"github.com/rain-kl/openflare/openflare-agent/internal/geoipdata"
"github.com/rain-kl/openflare/openflare-server/utils/geoip"
)
type Updater struct {
@@ -3,7 +3,7 @@ package heartbeat
import (
"context"
"openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
)
type Client interface {
@@ -12,7 +12,7 @@ import (
"strings"
"time"
"openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
)
type Client struct {
@@ -20,10 +20,10 @@ import (
"strings"
"time"
"openflare/utils"
openrestyrender "openflare/utils/render/openresty"
"github.com/rain-kl/openflare/openflare-server/utils"
openrestyrender "github.com/rain-kl/openflare/openflare-server/utils/render/openresty"
"openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
)
const RuntimeConfigDirPlaceholder = "__OPENFLARE_RUNTIME_CONFIG_DIR__"
@@ -12,7 +12,7 @@ import (
"strings"
"testing"
"openflare-agent/internal/protocol"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
)
type runCall struct {
@@ -699,6 +699,18 @@ func TestManagedPowLuaFilesUseInternalChallengeFlow(t *testing.T) {
}
}
func TestManagedWAFLuaTreatsWhitelistAsAllowlist(t *testing.T) {
if !strings.Contains(openRestyWAFRuntimeLua, "local function first_allowlist_group(groups)") {
t.Fatal("expected waf runtime to detect allowlist rule groups")
}
if !strings.Contains(openRestyWAFRuntimeLua, "local allowlist_group = first_allowlist_group(groups)") {
t.Fatal("expected waf runtime to enter allowlist mode when whitelist rules exist")
}
if !strings.Contains(openRestyWAFRuntimeLua, "return exit_with_group(allowlist_group)") {
t.Fatal("expected waf runtime to block requests that miss configured whitelists")
}
}
func TestManagerRollbackRestoresCertFiles(t *testing.T) {
tempDir := t.TempDir()
routePath := filepath.Join(tempDir, "routes.conf")
@@ -1,6 +1,6 @@
package nginx
import "openflare-agent/internal/protocol"
import "github.com/rain-kl/openflare/openflare-agent/internal/protocol"
const (
openRestyObservabilityWindowTTL = "7200"
@@ -2,9 +2,10 @@ package nginx
import (
"embed"
"openflare-agent/internal/protocol"
"path/filepath"
"strings"
"github.com/rain-kl/openflare/openflare-agent/internal/protocol"
)
//go:embed pow_static

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show More