From b6c4181c707ff1ab8440c2cb9914bb9022c887d9 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 19 Jun 2026 17:40:30 +0800 Subject: [PATCH] fix(openflare): serialize access log snowflake IDs as strings - Return AccessLogView.id as string to avoid JS Number precision loss - Store OpenFlareAccessLog IDs as uint64 with json id,string - Update frontend AccessLogItem.id type to string --- docker-compose.yaml | 14 +- docs/changelog/index.md | 1 + docs/docs.go | 1954 +++++++++++++++-- docs/swagger.json | 1954 +++++++++++++++-- docs/swagger.yaml | 1280 +++++++++-- frontend/lib/services/openflare/types.ts | 2 +- .../observability/access_log_logics.go | 9 +- internal/model/openflare_access_log.go | 2 +- internal/model/openflare_access_log_store.go | 4 +- .../openflare_access_log_store_memory.go | 2 +- internal/model/openflare_observability.go | 2 +- .../openflare_observability_store_memory.go | 10 +- 12 files changed, 4556 insertions(+), 678 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 974d4b74..5b91cda8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -31,21 +31,19 @@ services: postgres: image: postgres:17-alpine restart: unless-stopped - environment: - POSTGRES_DB: ${POSTGRES_DB:-openflare} - POSTGRES_USER: ${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} - TZ: ${TZ:-Asia/Shanghai} ports: - - "${POSTGRES_PORT:-5432}:5432" + - "5432:5432" + environment: + POSTGRES_DB: openflare + POSTGRES_USER: openflare + POSTGRES_PASSWORD: replace-with-strong-password volumes: - ./data/postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-openflare}"] + test: ["CMD-SHELL", "pg_isready -U openflare -d openflare"] interval: 10s timeout: 5s retries: 5 - start_period: 10s redis: image: valkey/valkey:8.0-alpine diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 7a339289..d47a46fb 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -35,6 +35,7 @@ sidebar: false - 修复总览看板「24 小时请求趋势」摘要误展示 24 小时累计值的问题:趋势图摘要改为「当前小时」桶数据,顶部 24h 统计改为按小时趋势聚合。 - 修复访问日志 ClickHouse 聚合查询因 `trim(x) AS x` 别名与表列同名导致总览看板地域分布及 IP 统计失败的问题。 - 修复访问日志页 `count()` 扫描类型不匹配(ClickHouse `UInt64` 写入 `int64`)导致列表计数失败的问题。 +- 修复访问日志 Snowflake ID 超出 JS 安全整数范围导致列表 React key 重复告警的问题:API `id` 改为字符串序列化。 ### 变更 diff --git a/docs/docs.go b/docs/docs.go index d553c93d..247e7e7c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -4054,6 +4054,375 @@ const docTemplate = `{ } } }, + "/api/v1/agent/apply-logs": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "记录 Agent 配置下发与应用结果", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "上报配置应用日志", + "parameters": [ + { + "description": "应用日志", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.ApplyLogPayload" + } + } + ], + "responses": { + "200": { + "description": "日志记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.OpenFlareApplyLog" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/config-versions/active": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "返回当前生效的完整配置包,供 Agent 拉取并应用", + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "获取活跃配置版本", + "responses": { + "200": { + "description": "活跃配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.ConfigResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/nodes/heartbeat": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "上报节点状态、指标与健康事件,返回远程控制配置与活跃配置元信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "Agent 心跳上报", + "parameters": [ + { + "description": "心跳数据", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.NodePayload" + } + } + ], + "responses": { + "200": { + "description": "心跳成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/nodes/register": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "使用节点 access token 重新注册,或使用全局 discovery token 发现新节点;请求头需携带 X-Agent-Token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "注册或发现 Agent 节点", + "parameters": [ + { + "description": "节点上报数据", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.NodePayload" + } + } + ], + "responses": { + "200": { + "description": "注册成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.RegistrationResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/pages/deployments/{deployment_id}/package": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "流式下载指定部署的静态资源压缩包,供 Agent 边缘分发", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "openflare-agent" + ], + "summary": "下载 Pages 部署包", + "parameters": [ + { + "type": "integer", + "description": "部署 ID", + "name": "deployment_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "部署包文件", + "schema": { + "type": "file" + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/waf/ip-groups/sync": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "按 ID 与校验和增量同步 WAF IP 组定义", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "同步 WAF IP 组", + "parameters": [ + { + "description": "同步请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.WAFIPGroupSyncInput" + } + } + ], + "responses": { + "200": { + "description": "同步结果", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.WAFIPGroupSyncResult" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/ws": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "升级为 WebSocket 长连接,用于实时推送配置同步、WAF IP 组等指令;需携带 X-Agent-Token", + "tags": [ + "openflare-agent" + ], + "summary": "Agent WebSocket 连接", + "responses": { + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, "/api/v1/config/public": { "get": { "description": "返回系统配置表中 visibility 为 1 的配置键值集合", @@ -4205,8 +4574,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4280,8 +4649,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4397,8 +4766,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4520,8 +4889,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4625,8 +4994,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4718,8 +5087,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4779,12 +5148,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5479,8 +5842,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5543,8 +5906,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5616,8 +5979,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5674,12 +6037,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5762,12 +6119,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5832,8 +6183,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5905,8 +6256,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5975,8 +6326,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -6033,12 +6384,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -6121,12 +6466,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -7011,8 +7350,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7085,8 +7424,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7160,8 +7499,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7223,8 +7562,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7286,8 +7625,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7679,8 +8018,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7752,8 +8091,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7825,12 +8164,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "部署不存在", "schema": { @@ -7901,12 +8234,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -7965,12 +8292,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8044,12 +8365,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8130,12 +8445,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8213,12 +8522,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目或部署不存在", "schema": { @@ -8284,12 +8587,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目或部署不存在", "schema": { @@ -8372,12 +8669,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8815,8 +9106,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -8888,8 +9179,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -8963,8 +9254,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9055,8 +9346,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9125,12 +9416,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9201,12 +9486,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9289,12 +9568,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9353,12 +9626,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9429,12 +9696,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9517,12 +9778,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9605,12 +9860,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9675,8 +9924,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9739,8 +9988,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9812,8 +10061,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9887,8 +10136,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9957,12 +10206,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10021,12 +10264,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10097,12 +10334,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10185,12 +10416,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10255,8 +10480,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -10328,8 +10553,8 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -10398,12 +10623,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10462,12 +10681,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10550,12 +10763,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10638,12 +10845,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10714,12 +10915,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10800,12 +10995,6 @@ const docTemplate = `{ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -11228,6 +11417,324 @@ const docTemplate = `{ } } }, + "/api/v1/relay/heartbeat": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "Relay 节点定期上报运行状态与 frps 观测数据,返回运行时配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-relay" + ], + "summary": "上报 Relay 心跳", + "parameters": [ + { + "description": "心跳载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/relay.HeartbeatPayload" + } + } + ], + "responses": { + "200": { + "description": "心跳响应", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/relay.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Agent Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/relay/ws": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "将已认证的 Relay 连接升级为 WebSocket 长连接,用于配置推送", + "tags": [ + "openflare-relay" + ], + "summary": "升级 Relay WebSocket 连接", + "responses": { + "401": { + "description": "Agent Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/apply-log": { + "post": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "Tunnel 客户端上报配置应用结果,服务端记录下发日志", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "上报 Tunnel 配置下发结果", + "parameters": [ + { + "description": "下发结果载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/flared.ApplyLogPayload" + } + } + ], + "responses": { + "200": { + "description": "下发日志记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.OpenFlareApplyLog" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/config/active": { + "get": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "返回 Tunnel 客户端当前应应用的完整路由配置(含中继列表与代理定义)", + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "获取活跃隧道配置", + "responses": { + "200": { + "description": "隧道配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/flared.TunnelConfigResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/heartbeat": { + "post": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "Tunnel 客户端定期上报运行状态与中继连接信息,返回活跃配置元数据与隧道设置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "上报 Tunnel 心跳", + "parameters": [ + { + "description": "心跳载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/flared.HeartbeatPayload" + } + } + ], + "responses": { + "200": { + "description": "心跳响应", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/flared.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/ws": { + "get": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "将已认证的 Tunnel 客户端连接升级为 WebSocket 长连接,用于配置推送", + "tags": [ + "openflare-tunnel" + ], + "summary": "升级 Tunnel WebSocket 连接", + "responses": { + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, "/api/v1/upload": { "post": { "security": [ @@ -12183,6 +12690,265 @@ const docTemplate = `{ } }, "definitions": { + "agent.ActiveConfigMeta": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "agent.ApplyLogPayload": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "main_config_checksum": { + "type": "string" + }, + "message": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "result": { + "type": "string" + }, + "route_config_checksum": { + "type": "string" + }, + "support_file_count": { + "type": "integer" + }, + "version": { + "type": "string" + } + } + }, + "agent.ConfigResponse": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "source_config_json": { + "type": "string" + }, + "support_files": { + "type": "array", + "items": { + "$ref": "#/definitions/agent.SupportFile" + } + }, + "version": { + "type": "string" + } + } + }, + "agent.HeartbeatResponse": { + "type": "object", + "properties": { + "active_config": { + "$ref": "#/definitions/agent.ActiveConfigMeta" + }, + "agent_settings": { + "$ref": "#/definitions/agent.Settings" + }, + "node": { + "$ref": "#/definitions/model.OpenFlareNode" + }, + "waf_ip_groups": { + "type": "array", + "items": { + "$ref": "#/definitions/agent.WAFIPGroup" + } + } + } + }, + "agent.NodePayload": { + "type": "object", + "properties": { + "access_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog" + } + }, + "buffered_observability": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord" + } + }, + "current_version": { + "type": "string" + }, + "ext_version": { + "type": "string" + }, + "health_events": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent" + } + }, + "ip": { + "type": "string" + }, + "last_error": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "openresty_message": { + "type": "string" + }, + "openresty_observation": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation" + }, + "openresty_status": { + "type": "string" + }, + "profile": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile" + }, + "snapshot": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot" + }, + "traffic_report": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport" + }, + "version": { + "type": "string" + }, + "waf_ip_group_checksums": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "agent.RegistrationResponse": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + } + } + }, + "agent.Settings": { + "type": "object", + "properties": { + "auto_update": { + "type": "boolean" + }, + "heartbeat_interval": { + "type": "integer" + }, + "restart_openresty_now": { + "type": "boolean" + }, + "update_channel": { + "type": "string" + }, + "update_now": { + "type": "boolean" + }, + "update_repo": { + "type": "string" + }, + "update_tag": { + "type": "string" + }, + "websocket_upgrade_enabled": { + "type": "boolean" + } + } + }, + "agent.SupportFile": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "agent.WAFIPGroup": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "ip_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "agent.WAFIPGroupSyncInput": { + "type": "object", + "properties": { + "checksums": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "ids": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "agent.WAFIPGroupSyncResult": { + "type": "object", + "properties": { + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup" + } + } + } + }, "apply_log.CleanupInput": { "type": "object", "properties": { @@ -12722,6 +13488,98 @@ const docTemplate = `{ } } }, + "flared.ApplyLogPayload": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "main_config_checksum": { + "type": "string" + }, + "message": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "result": { + "type": "string" + }, + "route_config_checksum": { + "type": "string" + }, + "support_file_count": { + "type": "integer" + }, + "version": { + "type": "string" + } + } + }, + "flared.HeartbeatPayload": { + "type": "object", + "properties": { + "client_version": { + "type": "string" + }, + "connected_relays": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredConnectedRelay" + } + }, + "current_checksum": { + "type": "string" + }, + "current_version": { + "type": "string" + }, + "frp_version": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "tunnel_status": { + "type": "string" + } + } + }, + "flared.HeartbeatResponse": { + "type": "object", + "properties": { + "active_config": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta" + }, + "tunnel_settings": { + "$ref": "#/definitions/protocol.RelaySettings" + } + } + }, + "flared.TunnelConfigResponse": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "proxies": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredProxyEntry" + } + }, + "relays": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredRelayInfo" + } + }, + "version": { + "type": "string" + } + } + }, "github_com_Rain-kl_Wavelet_internal_apps_cap.RedeemResponse": { "type": "object", "properties": { @@ -12739,6 +13597,240 @@ const docTemplate = `{ } } }, + "github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord": { + "type": "object", + "properties": { + "access_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog" + } + }, + "openresty_observation": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation" + }, + "snapshot": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot" + }, + "traffic_report": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport" + }, + "window_started_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "logged_at_unix": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "remote_addr": { + "type": "string" + }, + "status_code": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent": { + "type": "object", + "properties": { + "event_type": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "triggered_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "cpu_usage_percent": { + "type": "number" + }, + "disk_read_bytes": { + "type": "integer" + }, + "disk_write_bytes": { + "type": "integer" + }, + "memory_total_bytes": { + "type": "integer" + }, + "memory_used_bytes": { + "type": "integer" + }, + "network_rx_bytes": { + "type": "integer" + }, + "network_tx_bytes": { + "type": "integer" + }, + "storage_total_bytes": { + "type": "integer" + }, + "storage_used_bytes": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "openresty_connections": { + "type": "integer" + }, + "openresty_rx_bytes": { + "type": "integer" + }, + "openresty_tx_bytes": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "cpu_cores": { + "type": "integer" + }, + "cpu_model": { + "type": "string" + }, + "hostname": { + "type": "string" + }, + "kernel_version": { + "type": "string" + }, + "os_name": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "reported_at_unix": { + "type": "integer" + }, + "total_disk_bytes": { + "type": "integer" + }, + "total_memory_bytes": { + "type": "integer" + }, + "uptime_seconds": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport": { + "type": "object", + "properties": { + "error_count": { + "type": "integer" + }, + "request_count": { + "type": "integer" + }, + "source_countries": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "status_codes": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "top_domains": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "unique_visitor_count": { + "type": "integer" + }, + "window_ended_at_unix": { + "type": "integer" + }, + "window_started_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "ip_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, "handler.batchDownloadRequest": { "type": "object", "required": [ @@ -13361,6 +14453,110 @@ const docTemplate = `{ } } }, + "model.OpenFlareNode": { + "type": "object", + "properties": { + "auto_update_enabled": { + "type": "boolean" + }, + "capabilities_json": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "current_version": { + "type": "string" + }, + "ext_version": { + "type": "string" + }, + "geo_latitude": { + "type": "number" + }, + "geo_longitude": { + "type": "number" + }, + "geo_manual_override": { + "type": "boolean" + }, + "geo_name": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ip": { + "type": "string" + }, + "ip_manual_override": { + "type": "boolean" + }, + "last_error": { + "type": "string" + }, + "last_seen_at": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "node_type": { + "type": "string" + }, + "openresty_message": { + "type": "string" + }, + "openresty_status": { + "type": "string" + }, + "relay_agent_access_addr": { + "type": "string" + }, + "relay_bind_port": { + "type": "integer" + }, + "relay_client_access_addr": { + "type": "string" + }, + "relay_client_proxy_url": { + "type": "string" + }, + "relay_status": { + "type": "string" + }, + "relay_vhost_http_port": { + "type": "integer" + }, + "relay_web_server_enabled": { + "type": "boolean" + }, + "restart_openresty_requested": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "update_channel": { + "type": "string" + }, + "update_requested": { + "type": "boolean" + }, + "update_tag": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "model.OpenFlareNodeSystemProfile": { "type": "object", "properties": { @@ -14456,7 +15652,7 @@ const docTemplate = `{ "type": "string" }, "id": { - "type": "integer" + "type": "string" }, "logged_at": { "type": "string" @@ -15254,6 +16450,228 @@ const docTemplate = `{ } } }, + "protocol.AgentNodeHealthEvent": { + "type": "object", + "properties": { + "event_type": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "triggered_at_unix": { + "type": "integer" + } + } + }, + "protocol.AgentNodeMetricSnapshot": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "cpu_usage_percent": { + "type": "number" + }, + "disk_read_bytes": { + "type": "integer" + }, + "disk_write_bytes": { + "type": "integer" + }, + "memory_total_bytes": { + "type": "integer" + }, + "memory_used_bytes": { + "type": "integer" + }, + "network_rx_bytes": { + "type": "integer" + }, + "network_tx_bytes": { + "type": "integer" + }, + "storage_total_bytes": { + "type": "integer" + }, + "storage_used_bytes": { + "type": "integer" + } + } + }, + "protocol.AgentNodeSystemProfile": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "cpu_cores": { + "type": "integer" + }, + "cpu_model": { + "type": "string" + }, + "hostname": { + "type": "string" + }, + "kernel_version": { + "type": "string" + }, + "os_name": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "reported_at_unix": { + "type": "integer" + }, + "total_disk_bytes": { + "type": "integer" + }, + "total_memory_bytes": { + "type": "integer" + }, + "uptime_seconds": { + "type": "integer" + } + } + }, + "protocol.FlaredConnectedRelay": { + "type": "object", + "properties": { + "proxy_count": { + "type": "integer" + }, + "relay_node_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "protocol.FlaredProxyEntry": { + "type": "object", + "properties": { + "custom_domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "local_addr": { + "type": "string" + }, + "local_port": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "protocol.FlaredRelayInfo": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "auth_token": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "relay_node_id": { + "type": "string" + } + } + }, + "protocol.RelayConfig": { + "type": "object", + "properties": { + "auth_token": { + "type": "string" + }, + "bind_port": { + "type": "integer" + }, + "log_level": { + "type": "string" + }, + "vhost_http_port": { + "type": "integer" + }, + "web_server_enabled": { + "type": "boolean" + } + } + }, + "protocol.RelayProxyStat": { + "type": "object", + "properties": { + "client_addr": { + "type": "string" + }, + "client_version": { + "type": "string" + }, + "last_close_time": { + "type": "string" + }, + "last_start_time": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "protocol.RelaySettings": { + "type": "object", + "properties": { + "auto_update": { + "type": "boolean" + }, + "heartbeat_interval": { + "type": "integer" + }, + "update_channel": { + "type": "string" + }, + "update_now": { + "type": "boolean" + }, + "update_repo": { + "type": "string" + }, + "update_tag": { + "type": "string" + }, + "websocket_upgrade_enabled": { + "type": "boolean" + } + } + }, "proxy_route.CustomHeaderInput": { "type": "object", "properties": { @@ -15804,6 +17222,64 @@ const docTemplate = `{ } } }, + "relay.HeartbeatPayload": { + "type": "object", + "properties": { + "frp_version": { + "type": "string" + }, + "frps_client_count": { + "type": "integer" + }, + "frps_connections": { + "type": "integer" + }, + "frps_proxies": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.RelayProxyStat" + } + }, + "frps_proxy_count": { + "type": "integer" + }, + "health_events": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.AgentNodeHealthEvent" + } + }, + "ip": { + "type": "string" + }, + "name": { + "type": "string" + }, + "profile": { + "$ref": "#/definitions/protocol.AgentNodeSystemProfile" + }, + "relay_status": { + "type": "string" + }, + "snapshot": { + "$ref": "#/definitions/protocol.AgentNodeMetricSnapshot" + }, + "version": { + "type": "string" + } + } + }, + "relay.HeartbeatResponse": { + "type": "object", + "properties": { + "relay_config": { + "$ref": "#/definitions/protocol.RelayConfig" + }, + "relay_settings": { + "$ref": "#/definitions/protocol.RelaySettings" + } + } + }, "response.Any": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index ade96ab2..87722673 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -4047,6 +4047,375 @@ } } }, + "/api/v1/agent/apply-logs": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "记录 Agent 配置下发与应用结果", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "上报配置应用日志", + "parameters": [ + { + "description": "应用日志", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.ApplyLogPayload" + } + } + ], + "responses": { + "200": { + "description": "日志记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.OpenFlareApplyLog" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/config-versions/active": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "返回当前生效的完整配置包,供 Agent 拉取并应用", + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "获取活跃配置版本", + "responses": { + "200": { + "description": "活跃配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.ConfigResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/nodes/heartbeat": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "上报节点状态、指标与健康事件,返回远程控制配置与活跃配置元信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "Agent 心跳上报", + "parameters": [ + { + "description": "心跳数据", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.NodePayload" + } + } + ], + "responses": { + "200": { + "description": "心跳成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/nodes/register": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "使用节点 access token 重新注册,或使用全局 discovery token 发现新节点;请求头需携带 X-Agent-Token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "注册或发现 Agent 节点", + "parameters": [ + { + "description": "节点上报数据", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.NodePayload" + } + } + ], + "responses": { + "200": { + "description": "注册成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.RegistrationResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/pages/deployments/{deployment_id}/package": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "流式下载指定部署的静态资源压缩包,供 Agent 边缘分发", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "openflare-agent" + ], + "summary": "下载 Pages 部署包", + "parameters": [ + { + "type": "integer", + "description": "部署 ID", + "name": "deployment_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "部署包文件", + "schema": { + "type": "file" + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/waf/ip-groups/sync": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "按 ID 与校验和增量同步 WAF IP 组定义", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-agent" + ], + "summary": "同步 WAF IP 组", + "parameters": [ + { + "description": "同步请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent.WAFIPGroupSyncInput" + } + } + ], + "responses": { + "200": { + "description": "同步结果", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/agent.WAFIPGroupSyncResult" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/agent/ws": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "升级为 WebSocket 长连接,用于实时推送配置同步、WAF IP 组等指令;需携带 X-Agent-Token", + "tags": [ + "openflare-agent" + ], + "summary": "Agent WebSocket 连接", + "responses": { + "401": { + "description": "Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, "/api/v1/config/public": { "get": { "description": "返回系统配置表中 visibility 为 1 的配置键值集合", @@ -4198,8 +4567,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4273,8 +4642,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4390,8 +4759,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4513,8 +4882,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4618,8 +4987,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4711,8 +5080,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -4772,12 +5141,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5472,8 +5835,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5536,8 +5899,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5609,8 +5972,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5667,12 +6030,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5755,12 +6112,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -5825,8 +6176,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5898,8 +6249,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -5968,8 +6319,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -6026,12 +6377,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -6114,12 +6459,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -7004,8 +7343,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7078,8 +7417,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7153,8 +7492,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7216,8 +7555,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7279,8 +7618,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7672,8 +8011,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7745,8 +8084,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -7818,12 +8157,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "部署不存在", "schema": { @@ -7894,12 +8227,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -7958,12 +8285,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8037,12 +8358,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8123,12 +8438,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8206,12 +8515,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目或部署不存在", "schema": { @@ -8277,12 +8580,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目或部署不存在", "schema": { @@ -8365,12 +8662,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "项目不存在", "schema": { @@ -8808,8 +9099,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -8881,8 +9172,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -8956,8 +9247,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9048,8 +9339,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9118,12 +9409,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9194,12 +9479,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9282,12 +9561,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9346,12 +9619,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9422,12 +9689,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9510,12 +9771,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9598,12 +9853,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -9668,8 +9917,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9732,8 +9981,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9805,8 +10054,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9880,8 +10129,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -9950,12 +10199,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10014,12 +10257,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10090,12 +10327,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10178,12 +10409,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10248,8 +10473,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -10321,8 +10546,8 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", + "404": { + "description": "无权限或不存在", "schema": { "$ref": "#/definitions/response.Any" } @@ -10391,12 +10616,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10455,12 +10674,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10543,12 +10756,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10631,12 +10838,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10707,12 +10908,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -10793,12 +10988,6 @@ "$ref": "#/definitions/response.Any" } }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, "404": { "description": "记录不存在", "schema": { @@ -11221,6 +11410,324 @@ } } }, + "/api/v1/relay/heartbeat": { + "post": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "Relay 节点定期上报运行状态与 frps 观测数据,返回运行时配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-relay" + ], + "summary": "上报 Relay 心跳", + "parameters": [ + { + "description": "心跳载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/relay.HeartbeatPayload" + } + } + ], + "responses": { + "200": { + "description": "心跳响应", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/relay.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Agent Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/relay/ws": { + "get": { + "security": [ + { + "AgentTokenAuth": [] + } + ], + "description": "将已认证的 Relay 连接升级为 WebSocket 长连接,用于配置推送", + "tags": [ + "openflare-relay" + ], + "summary": "升级 Relay WebSocket 连接", + "responses": { + "401": { + "description": "Agent Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/apply-log": { + "post": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "Tunnel 客户端上报配置应用结果,服务端记录下发日志", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "上报 Tunnel 配置下发结果", + "parameters": [ + { + "description": "下发结果载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/flared.ApplyLogPayload" + } + } + ], + "responses": { + "200": { + "description": "下发日志记录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.OpenFlareApplyLog" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/config/active": { + "get": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "返回 Tunnel 客户端当前应应用的完整路由配置(含中继列表与代理定义)", + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "获取活跃隧道配置", + "responses": { + "200": { + "description": "隧道配置", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/flared.TunnelConfigResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/heartbeat": { + "post": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "Tunnel 客户端定期上报运行状态与中继连接信息,返回活跃配置元数据与隧道设置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "openflare-tunnel" + ], + "summary": "上报 Tunnel 心跳", + "parameters": [ + { + "description": "心跳载荷", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/flared.HeartbeatPayload" + } + } + ], + "responses": { + "200": { + "description": "心跳响应", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Any" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/flared.HeartbeatResponse" + } + } + } + ] + } + }, + "400": { + "description": "参数错误", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, + "/api/v1/tunnel/ws": { + "get": { + "security": [ + { + "TunnelTokenAuth": [] + } + ], + "description": "将已认证的 Tunnel 客户端连接升级为 WebSocket 长连接,用于配置推送", + "tags": [ + "openflare-tunnel" + ], + "summary": "升级 Tunnel WebSocket 连接", + "responses": { + "401": { + "description": "Tunnel Token 无效", + "schema": { + "$ref": "#/definitions/response.Any" + } + }, + "403": { + "description": "节点类型不匹配", + "schema": { + "$ref": "#/definitions/response.Any" + } + } + } + } + }, "/api/v1/upload": { "post": { "security": [ @@ -12176,6 +12683,265 @@ } }, "definitions": { + "agent.ActiveConfigMeta": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "agent.ApplyLogPayload": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "main_config_checksum": { + "type": "string" + }, + "message": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "result": { + "type": "string" + }, + "route_config_checksum": { + "type": "string" + }, + "support_file_count": { + "type": "integer" + }, + "version": { + "type": "string" + } + } + }, + "agent.ConfigResponse": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "source_config_json": { + "type": "string" + }, + "support_files": { + "type": "array", + "items": { + "$ref": "#/definitions/agent.SupportFile" + } + }, + "version": { + "type": "string" + } + } + }, + "agent.HeartbeatResponse": { + "type": "object", + "properties": { + "active_config": { + "$ref": "#/definitions/agent.ActiveConfigMeta" + }, + "agent_settings": { + "$ref": "#/definitions/agent.Settings" + }, + "node": { + "$ref": "#/definitions/model.OpenFlareNode" + }, + "waf_ip_groups": { + "type": "array", + "items": { + "$ref": "#/definitions/agent.WAFIPGroup" + } + } + } + }, + "agent.NodePayload": { + "type": "object", + "properties": { + "access_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog" + } + }, + "buffered_observability": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord" + } + }, + "current_version": { + "type": "string" + }, + "ext_version": { + "type": "string" + }, + "health_events": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent" + } + }, + "ip": { + "type": "string" + }, + "last_error": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "openresty_message": { + "type": "string" + }, + "openresty_observation": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation" + }, + "openresty_status": { + "type": "string" + }, + "profile": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile" + }, + "snapshot": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot" + }, + "traffic_report": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport" + }, + "version": { + "type": "string" + }, + "waf_ip_group_checksums": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "agent.RegistrationResponse": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + } + } + }, + "agent.Settings": { + "type": "object", + "properties": { + "auto_update": { + "type": "boolean" + }, + "heartbeat_interval": { + "type": "integer" + }, + "restart_openresty_now": { + "type": "boolean" + }, + "update_channel": { + "type": "string" + }, + "update_now": { + "type": "boolean" + }, + "update_repo": { + "type": "string" + }, + "update_tag": { + "type": "string" + }, + "websocket_upgrade_enabled": { + "type": "boolean" + } + } + }, + "agent.SupportFile": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "agent.WAFIPGroup": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "ip_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "agent.WAFIPGroupSyncInput": { + "type": "object", + "properties": { + "checksums": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "ids": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "agent.WAFIPGroupSyncResult": { + "type": "object", + "properties": { + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup" + } + } + } + }, "apply_log.CleanupInput": { "type": "object", "properties": { @@ -12715,6 +13481,98 @@ } } }, + "flared.ApplyLogPayload": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "main_config_checksum": { + "type": "string" + }, + "message": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "result": { + "type": "string" + }, + "route_config_checksum": { + "type": "string" + }, + "support_file_count": { + "type": "integer" + }, + "version": { + "type": "string" + } + } + }, + "flared.HeartbeatPayload": { + "type": "object", + "properties": { + "client_version": { + "type": "string" + }, + "connected_relays": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredConnectedRelay" + } + }, + "current_checksum": { + "type": "string" + }, + "current_version": { + "type": "string" + }, + "frp_version": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "tunnel_status": { + "type": "string" + } + } + }, + "flared.HeartbeatResponse": { + "type": "object", + "properties": { + "active_config": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta" + }, + "tunnel_settings": { + "$ref": "#/definitions/protocol.RelaySettings" + } + } + }, + "flared.TunnelConfigResponse": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "proxies": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredProxyEntry" + } + }, + "relays": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.FlaredRelayInfo" + } + }, + "version": { + "type": "string" + } + } + }, "github_com_Rain-kl_Wavelet_internal_apps_cap.RedeemResponse": { "type": "object", "properties": { @@ -12732,6 +13590,240 @@ } } }, + "github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord": { + "type": "object", + "properties": { + "access_logs": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog" + } + }, + "openresty_observation": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation" + }, + "snapshot": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot" + }, + "traffic_report": { + "$ref": "#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport" + }, + "window_started_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "logged_at_unix": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "remote_addr": { + "type": "string" + }, + "status_code": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent": { + "type": "object", + "properties": { + "event_type": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "triggered_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "cpu_usage_percent": { + "type": "number" + }, + "disk_read_bytes": { + "type": "integer" + }, + "disk_write_bytes": { + "type": "integer" + }, + "memory_total_bytes": { + "type": "integer" + }, + "memory_used_bytes": { + "type": "integer" + }, + "network_rx_bytes": { + "type": "integer" + }, + "network_tx_bytes": { + "type": "integer" + }, + "storage_total_bytes": { + "type": "integer" + }, + "storage_used_bytes": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "openresty_connections": { + "type": "integer" + }, + "openresty_rx_bytes": { + "type": "integer" + }, + "openresty_tx_bytes": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "cpu_cores": { + "type": "integer" + }, + "cpu_model": { + "type": "string" + }, + "hostname": { + "type": "string" + }, + "kernel_version": { + "type": "string" + }, + "os_name": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "reported_at_unix": { + "type": "integer" + }, + "total_disk_bytes": { + "type": "integer" + }, + "total_memory_bytes": { + "type": "integer" + }, + "uptime_seconds": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport": { + "type": "object", + "properties": { + "error_count": { + "type": "integer" + }, + "request_count": { + "type": "integer" + }, + "source_countries": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "status_codes": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "top_domains": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "unique_visitor_count": { + "type": "integer" + }, + "window_ended_at_unix": { + "type": "integer" + }, + "window_started_at_unix": { + "type": "integer" + } + } + }, + "github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup": { + "type": "object", + "properties": { + "checksum": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "ip_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, "handler.batchDownloadRequest": { "type": "object", "required": [ @@ -13354,6 +14446,110 @@ } } }, + "model.OpenFlareNode": { + "type": "object", + "properties": { + "auto_update_enabled": { + "type": "boolean" + }, + "capabilities_json": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "current_version": { + "type": "string" + }, + "ext_version": { + "type": "string" + }, + "geo_latitude": { + "type": "number" + }, + "geo_longitude": { + "type": "number" + }, + "geo_manual_override": { + "type": "boolean" + }, + "geo_name": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ip": { + "type": "string" + }, + "ip_manual_override": { + "type": "boolean" + }, + "last_error": { + "type": "string" + }, + "last_seen_at": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "node_type": { + "type": "string" + }, + "openresty_message": { + "type": "string" + }, + "openresty_status": { + "type": "string" + }, + "relay_agent_access_addr": { + "type": "string" + }, + "relay_bind_port": { + "type": "integer" + }, + "relay_client_access_addr": { + "type": "string" + }, + "relay_client_proxy_url": { + "type": "string" + }, + "relay_status": { + "type": "string" + }, + "relay_vhost_http_port": { + "type": "integer" + }, + "relay_web_server_enabled": { + "type": "boolean" + }, + "restart_openresty_requested": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "update_channel": { + "type": "string" + }, + "update_requested": { + "type": "boolean" + }, + "update_tag": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "model.OpenFlareNodeSystemProfile": { "type": "object", "properties": { @@ -14449,7 +15645,7 @@ "type": "string" }, "id": { - "type": "integer" + "type": "string" }, "logged_at": { "type": "string" @@ -15247,6 +16443,228 @@ } } }, + "protocol.AgentNodeHealthEvent": { + "type": "object", + "properties": { + "event_type": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "triggered_at_unix": { + "type": "integer" + } + } + }, + "protocol.AgentNodeMetricSnapshot": { + "type": "object", + "properties": { + "captured_at_unix": { + "type": "integer" + }, + "cpu_usage_percent": { + "type": "number" + }, + "disk_read_bytes": { + "type": "integer" + }, + "disk_write_bytes": { + "type": "integer" + }, + "memory_total_bytes": { + "type": "integer" + }, + "memory_used_bytes": { + "type": "integer" + }, + "network_rx_bytes": { + "type": "integer" + }, + "network_tx_bytes": { + "type": "integer" + }, + "storage_total_bytes": { + "type": "integer" + }, + "storage_used_bytes": { + "type": "integer" + } + } + }, + "protocol.AgentNodeSystemProfile": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "cpu_cores": { + "type": "integer" + }, + "cpu_model": { + "type": "string" + }, + "hostname": { + "type": "string" + }, + "kernel_version": { + "type": "string" + }, + "os_name": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "reported_at_unix": { + "type": "integer" + }, + "total_disk_bytes": { + "type": "integer" + }, + "total_memory_bytes": { + "type": "integer" + }, + "uptime_seconds": { + "type": "integer" + } + } + }, + "protocol.FlaredConnectedRelay": { + "type": "object", + "properties": { + "proxy_count": { + "type": "integer" + }, + "relay_node_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "protocol.FlaredProxyEntry": { + "type": "object", + "properties": { + "custom_domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "local_addr": { + "type": "string" + }, + "local_port": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "protocol.FlaredRelayInfo": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "auth_token": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "relay_node_id": { + "type": "string" + } + } + }, + "protocol.RelayConfig": { + "type": "object", + "properties": { + "auth_token": { + "type": "string" + }, + "bind_port": { + "type": "integer" + }, + "log_level": { + "type": "string" + }, + "vhost_http_port": { + "type": "integer" + }, + "web_server_enabled": { + "type": "boolean" + } + } + }, + "protocol.RelayProxyStat": { + "type": "object", + "properties": { + "client_addr": { + "type": "string" + }, + "client_version": { + "type": "string" + }, + "last_close_time": { + "type": "string" + }, + "last_start_time": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "protocol.RelaySettings": { + "type": "object", + "properties": { + "auto_update": { + "type": "boolean" + }, + "heartbeat_interval": { + "type": "integer" + }, + "update_channel": { + "type": "string" + }, + "update_now": { + "type": "boolean" + }, + "update_repo": { + "type": "string" + }, + "update_tag": { + "type": "string" + }, + "websocket_upgrade_enabled": { + "type": "boolean" + } + } + }, "proxy_route.CustomHeaderInput": { "type": "object", "properties": { @@ -15797,6 +17215,64 @@ } } }, + "relay.HeartbeatPayload": { + "type": "object", + "properties": { + "frp_version": { + "type": "string" + }, + "frps_client_count": { + "type": "integer" + }, + "frps_connections": { + "type": "integer" + }, + "frps_proxies": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.RelayProxyStat" + } + }, + "frps_proxy_count": { + "type": "integer" + }, + "health_events": { + "type": "array", + "items": { + "$ref": "#/definitions/protocol.AgentNodeHealthEvent" + } + }, + "ip": { + "type": "string" + }, + "name": { + "type": "string" + }, + "profile": { + "$ref": "#/definitions/protocol.AgentNodeSystemProfile" + }, + "relay_status": { + "type": "string" + }, + "snapshot": { + "$ref": "#/definitions/protocol.AgentNodeMetricSnapshot" + }, + "version": { + "type": "string" + } + } + }, + "relay.HeartbeatResponse": { + "type": "object", + "properties": { + "relay_config": { + "$ref": "#/definitions/protocol.RelayConfig" + }, + "relay_settings": { + "$ref": "#/definitions/protocol.RelaySettings" + } + } + }, "response.Any": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index a29730ad..17ef946c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,174 @@ basePath: / definitions: + agent.ActiveConfigMeta: + properties: + checksum: + type: string + version: + type: string + type: object + agent.ApplyLogPayload: + properties: + checksum: + type: string + main_config_checksum: + type: string + message: + type: string + node_id: + type: string + result: + type: string + route_config_checksum: + type: string + support_file_count: + type: integer + version: + type: string + type: object + agent.ConfigResponse: + properties: + checksum: + type: string + created_at: + type: string + source_config_json: + type: string + support_files: + items: + $ref: '#/definitions/agent.SupportFile' + type: array + version: + type: string + type: object + agent.HeartbeatResponse: + properties: + active_config: + $ref: '#/definitions/agent.ActiveConfigMeta' + agent_settings: + $ref: '#/definitions/agent.Settings' + node: + $ref: '#/definitions/model.OpenFlareNode' + waf_ip_groups: + items: + $ref: '#/definitions/agent.WAFIPGroup' + type: array + type: object + agent.NodePayload: + properties: + access_logs: + items: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog' + type: array + buffered_observability: + items: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord' + type: array + current_version: + type: string + ext_version: + type: string + health_events: + items: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent' + type: array + ip: + type: string + last_error: + type: string + name: + type: string + node_id: + type: string + openresty_message: + type: string + openresty_observation: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation' + openresty_status: + type: string + profile: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile' + snapshot: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot' + traffic_report: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport' + version: + type: string + waf_ip_group_checksums: + additionalProperties: + type: string + type: object + type: object + agent.RegistrationResponse: + properties: + access_token: + type: string + name: + type: string + node_id: + type: string + type: object + agent.Settings: + properties: + auto_update: + type: boolean + heartbeat_interval: + type: integer + restart_openresty_now: + type: boolean + update_channel: + type: string + update_now: + type: boolean + update_repo: + type: string + update_tag: + type: string + websocket_upgrade_enabled: + type: boolean + type: object + agent.SupportFile: + properties: + content: + type: string + path: + type: string + type: object + agent.WAFIPGroup: + properties: + checksum: + type: string + enabled: + type: boolean + id: + type: integer + ip_list: + items: + type: string + type: array + name: + type: string + type: + type: string + type: object + agent.WAFIPGroupSyncInput: + properties: + checksums: + additionalProperties: + type: string + type: object + ids: + items: + type: integer + type: array + type: object + agent.WAFIPGroupSyncResult: + properties: + groups: + items: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup' + type: array + type: object apply_log.CleanupInput: properties: delete_all: @@ -356,6 +525,66 @@ definitions: ttl_minutes: type: integer type: object + flared.ApplyLogPayload: + properties: + checksum: + type: string + main_config_checksum: + type: string + message: + type: string + node_id: + type: string + result: + type: string + route_config_checksum: + type: string + support_file_count: + type: integer + version: + type: string + type: object + flared.HeartbeatPayload: + properties: + client_version: + type: string + connected_relays: + items: + $ref: '#/definitions/protocol.FlaredConnectedRelay' + type: array + current_checksum: + type: string + current_version: + type: string + frp_version: + type: string + ip: + type: string + tunnel_status: + type: string + type: object + flared.HeartbeatResponse: + properties: + active_config: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta' + tunnel_settings: + $ref: '#/definitions/protocol.RelaySettings' + type: object + flared.TunnelConfigResponse: + properties: + checksum: + type: string + proxies: + items: + $ref: '#/definitions/protocol.FlaredProxyEntry' + type: array + relays: + items: + $ref: '#/definitions/protocol.FlaredRelayInfo' + type: array + version: + type: string + type: object github_com_Rain-kl_Wavelet_internal_apps_cap.RedeemResponse: properties: error: @@ -367,6 +596,160 @@ definitions: token: type: string type: object + github_com_Rain-kl_Wavelet_pkg_protocol.ActiveConfigMeta: + properties: + checksum: + type: string + version: + type: string + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.BufferedObservabilityRecord: + properties: + access_logs: + items: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog' + type: array + openresty_observation: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation' + snapshot: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot' + traffic_report: + $ref: '#/definitions/github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport' + window_started_at_unix: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeAccessLog: + properties: + host: + type: string + logged_at_unix: + type: integer + path: + type: string + remote_addr: + type: string + status_code: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeHealthEvent: + properties: + event_type: + type: string + message: + type: string + metadata: + additionalProperties: + type: string + type: object + severity: + type: string + triggered_at_unix: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeMetricSnapshot: + properties: + captured_at_unix: + type: integer + cpu_usage_percent: + type: number + disk_read_bytes: + type: integer + disk_write_bytes: + type: integer + memory_total_bytes: + type: integer + memory_used_bytes: + type: integer + network_rx_bytes: + type: integer + network_tx_bytes: + type: integer + storage_total_bytes: + type: integer + storage_used_bytes: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeOpenrestyObservation: + properties: + captured_at_unix: + type: integer + openresty_connections: + type: integer + openresty_rx_bytes: + type: integer + openresty_tx_bytes: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeSystemProfile: + properties: + architecture: + type: string + cpu_cores: + type: integer + cpu_model: + type: string + hostname: + type: string + kernel_version: + type: string + os_name: + type: string + os_version: + type: string + reported_at_unix: + type: integer + total_disk_bytes: + type: integer + total_memory_bytes: + type: integer + uptime_seconds: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.NodeTrafficReport: + properties: + error_count: + type: integer + request_count: + type: integer + source_countries: + additionalProperties: + format: int64 + type: integer + type: object + status_codes: + additionalProperties: + format: int64 + type: integer + type: object + top_domains: + additionalProperties: + format: int64 + type: integer + type: object + unique_visitor_count: + type: integer + window_ended_at_unix: + type: integer + window_started_at_unix: + type: integer + type: object + github_com_Rain-kl_Wavelet_pkg_protocol.WAFIPGroup: + properties: + checksum: + type: string + enabled: + type: boolean + id: + type: integer + ip_list: + items: + type: string + type: array + name: + type: string + type: + type: string + type: object handler.batchDownloadRequest: properties: ids: @@ -776,6 +1159,75 @@ definitions: storage_used_bytes: type: integer type: object + model.OpenFlareNode: + properties: + auto_update_enabled: + type: boolean + capabilities_json: + type: string + created_at: + type: string + current_version: + type: string + ext_version: + type: string + geo_latitude: + type: number + geo_longitude: + type: number + geo_manual_override: + type: boolean + geo_name: + type: string + id: + type: integer + ip: + type: string + ip_manual_override: + type: boolean + last_error: + type: string + last_seen_at: + type: string + name: + type: string + node_id: + type: string + node_type: + type: string + openresty_message: + type: string + openresty_status: + type: string + relay_agent_access_addr: + type: string + relay_bind_port: + type: integer + relay_client_access_addr: + type: string + relay_client_proxy_url: + type: string + relay_status: + type: string + relay_vhost_http_port: + type: integer + relay_web_server_enabled: + type: boolean + restart_openresty_requested: + type: boolean + status: + type: string + update_channel: + type: string + update_requested: + type: boolean + update_tag: + type: string + updated_at: + type: string + version: + type: string + type: object model.OpenFlareNodeSystemProfile: properties: architecture: @@ -1510,7 +1962,7 @@ definitions: host: type: string id: - type: integer + type: string logged_at: type: string node_id: @@ -2031,6 +2483,151 @@ definitions: updated_at: type: string type: object + protocol.AgentNodeHealthEvent: + properties: + event_type: + type: string + message: + type: string + metadata: + additionalProperties: + type: string + type: object + severity: + type: string + triggered_at_unix: + type: integer + type: object + protocol.AgentNodeMetricSnapshot: + properties: + captured_at_unix: + type: integer + cpu_usage_percent: + type: number + disk_read_bytes: + type: integer + disk_write_bytes: + type: integer + memory_total_bytes: + type: integer + memory_used_bytes: + type: integer + network_rx_bytes: + type: integer + network_tx_bytes: + type: integer + storage_total_bytes: + type: integer + storage_used_bytes: + type: integer + type: object + protocol.AgentNodeSystemProfile: + properties: + architecture: + type: string + cpu_cores: + type: integer + cpu_model: + type: string + hostname: + type: string + kernel_version: + type: string + os_name: + type: string + os_version: + type: string + reported_at_unix: + type: integer + total_disk_bytes: + type: integer + total_memory_bytes: + type: integer + uptime_seconds: + type: integer + type: object + protocol.FlaredConnectedRelay: + properties: + proxy_count: + type: integer + relay_node_id: + type: string + status: + type: string + type: object + protocol.FlaredProxyEntry: + properties: + custom_domains: + items: + type: string + type: array + local_addr: + type: string + local_port: + type: integer + name: + type: string + type: + type: string + type: object + protocol.FlaredRelayInfo: + properties: + address: + type: string + auth_token: + type: string + proxy_url: + type: string + relay_node_id: + type: string + type: object + protocol.RelayConfig: + properties: + auth_token: + type: string + bind_port: + type: integer + log_level: + type: string + vhost_http_port: + type: integer + web_server_enabled: + type: boolean + type: object + protocol.RelayProxyStat: + properties: + client_addr: + type: string + client_version: + type: string + last_close_time: + type: string + last_start_time: + type: string + name: + type: string + status: + type: string + type: + type: string + type: object + protocol.RelaySettings: + properties: + auto_update: + type: boolean + heartbeat_interval: + type: integer + update_channel: + type: string + update_now: + type: boolean + update_repo: + type: string + update_tag: + type: string + websocket_upgrade_enabled: + type: boolean + type: object proxy_route.CustomHeaderInput: properties: key: @@ -2399,6 +2996,44 @@ definitions: total: type: integer type: object + relay.HeartbeatPayload: + properties: + frp_version: + type: string + frps_client_count: + type: integer + frps_connections: + type: integer + frps_proxies: + items: + $ref: '#/definitions/protocol.RelayProxyStat' + type: array + frps_proxy_count: + type: integer + health_events: + items: + $ref: '#/definitions/protocol.AgentNodeHealthEvent' + type: array + ip: + type: string + name: + type: string + profile: + $ref: '#/definitions/protocol.AgentNodeSystemProfile' + relay_status: + type: string + snapshot: + $ref: '#/definitions/protocol.AgentNodeMetricSnapshot' + version: + type: string + type: object + relay.HeartbeatResponse: + properties: + relay_config: + $ref: '#/definitions/protocol.RelayConfig' + relay_settings: + $ref: '#/definitions/protocol.RelaySettings' + type: object response.Any: properties: data: {} @@ -5711,6 +6346,224 @@ paths: summary: 更新用户状态 tags: - admin + /api/v1/agent/apply-logs: + post: + consumes: + - application/json + description: 记录 Agent 配置下发与应用结果 + parameters: + - description: 应用日志 + in: body + name: body + required: true + schema: + $ref: '#/definitions/agent.ApplyLogPayload' + produces: + - application/json + responses: + "200": + description: 日志记录 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/model.OpenFlareApplyLog' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 上报配置应用日志 + tags: + - openflare-agent + /api/v1/agent/config-versions/active: + get: + description: 返回当前生效的完整配置包,供 Agent 拉取并应用 + produces: + - application/json + responses: + "200": + description: 活跃配置 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/agent.ConfigResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 获取活跃配置版本 + tags: + - openflare-agent + /api/v1/agent/nodes/heartbeat: + post: + consumes: + - application/json + description: 上报节点状态、指标与健康事件,返回远程控制配置与活跃配置元信息 + parameters: + - description: 心跳数据 + in: body + name: body + required: true + schema: + $ref: '#/definitions/agent.NodePayload' + produces: + - application/json + responses: + "200": + description: 心跳成功 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/agent.HeartbeatResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: Agent 心跳上报 + tags: + - openflare-agent + /api/v1/agent/nodes/register: + post: + consumes: + - application/json + description: 使用节点 access token 重新注册,或使用全局 discovery token 发现新节点;请求头需携带 X-Agent-Token + parameters: + - description: 节点上报数据 + in: body + name: body + required: true + schema: + $ref: '#/definitions/agent.NodePayload' + produces: + - application/json + responses: + "200": + description: 注册成功 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/agent.RegistrationResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 注册或发现 Agent 节点 + tags: + - openflare-agent + /api/v1/agent/pages/deployments/{deployment_id}/package: + get: + description: 流式下载指定部署的静态资源压缩包,供 Agent 边缘分发 + parameters: + - description: 部署 ID + in: path + name: deployment_id + required: true + type: integer + produces: + - application/octet-stream + responses: + "200": + description: 部署包文件 + schema: + type: file + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 下载 Pages 部署包 + tags: + - openflare-agent + /api/v1/agent/waf/ip-groups/sync: + post: + consumes: + - application/json + description: 按 ID 与校验和增量同步 WAF IP 组定义 + parameters: + - description: 同步请求 + in: body + name: body + required: true + schema: + $ref: '#/definitions/agent.WAFIPGroupSyncInput' + produces: + - application/json + responses: + "200": + description: 同步结果 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/agent.WAFIPGroupSyncResult' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 同步 WAF IP 组 + tags: + - openflare-agent + /api/v1/agent/ws: + get: + description: 升级为 WebSocket 长连接,用于实时推送配置同步、WAF IP 组等指令;需携带 X-Agent-Token + responses: + "401": + description: Token 无效 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: Agent WebSocket 连接 + tags: + - openflare-agent /api/v1/config/public: get: consumes: @@ -5800,8 +6653,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -5845,8 +6698,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -5918,8 +6771,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -5995,8 +6848,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6060,8 +6913,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6117,8 +6970,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6153,10 +7006,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -6567,8 +7416,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6605,8 +7454,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6649,8 +7498,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6686,10 +7535,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -6740,10 +7585,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -6782,8 +7623,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6826,8 +7667,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -6863,10 +7704,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -6917,10 +7754,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -6963,8 +7796,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7488,8 +8321,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7532,8 +8365,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7577,8 +8410,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7617,8 +8450,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7657,8 +8490,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7891,8 +8724,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7935,8 +8768,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -7977,10 +8810,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目不存在 schema: @@ -8018,10 +8847,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目不存在 schema: @@ -8066,10 +8891,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目不存在 schema: @@ -8117,10 +8938,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目或部署不存在 schema: @@ -8163,10 +8980,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目或部署不存在 schema: @@ -8216,10 +9029,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目不存在 schema: @@ -8270,10 +9079,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 项目不存在 schema: @@ -8318,10 +9123,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 部署不存在 schema: @@ -8582,8 +9383,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -8626,8 +9427,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -8668,10 +9469,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8714,10 +9511,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8768,10 +9561,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8809,10 +9598,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8855,10 +9640,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8909,10 +9690,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -8963,10 +9740,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9012,8 +9785,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9069,8 +9842,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9107,8 +9880,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9145,8 +9918,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9189,8 +9962,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9231,10 +10004,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9272,10 +10041,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9318,10 +10083,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9372,10 +10133,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9421,8 +10178,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9459,8 +10216,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9503,8 +10260,8 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 + "404": + description: 无权限或不存在 schema: $ref: '#/definitions/response.Any' "500": @@ -9545,10 +10302,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9586,10 +10339,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9640,10 +10389,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9694,10 +10439,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9740,10 +10481,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -9793,10 +10530,6 @@ paths: description: 未登录 schema: $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' "404": description: 记录不存在 schema: @@ -10049,6 +10782,195 @@ paths: summary: 获取当前登录用户信息 tags: - oauth + /api/v1/relay/heartbeat: + post: + consumes: + - application/json + description: Relay 节点定期上报运行状态与 frps 观测数据,返回运行时配置 + parameters: + - description: 心跳载荷 + in: body + name: body + required: true + schema: + $ref: '#/definitions/relay.HeartbeatPayload' + produces: + - application/json + responses: + "200": + description: 心跳响应 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/relay.HeartbeatResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Agent Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 上报 Relay 心跳 + tags: + - openflare-relay + /api/v1/relay/ws: + get: + description: 将已认证的 Relay 连接升级为 WebSocket 长连接,用于配置推送 + responses: + "401": + description: Agent Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - AgentTokenAuth: [] + summary: 升级 Relay WebSocket 连接 + tags: + - openflare-relay + /api/v1/tunnel/apply-log: + post: + consumes: + - application/json + description: Tunnel 客户端上报配置应用结果,服务端记录下发日志 + parameters: + - description: 下发结果载荷 + in: body + name: body + required: true + schema: + $ref: '#/definitions/flared.ApplyLogPayload' + produces: + - application/json + responses: + "200": + description: 下发日志记录 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/model.OpenFlareApplyLog' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Tunnel Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - TunnelTokenAuth: [] + summary: 上报 Tunnel 配置下发结果 + tags: + - openflare-tunnel + /api/v1/tunnel/config/active: + get: + description: 返回 Tunnel 客户端当前应应用的完整路由配置(含中继列表与代理定义) + produces: + - application/json + responses: + "200": + description: 隧道配置 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/flared.TunnelConfigResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Tunnel Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - TunnelTokenAuth: [] + summary: 获取活跃隧道配置 + tags: + - openflare-tunnel + /api/v1/tunnel/heartbeat: + post: + consumes: + - application/json + description: Tunnel 客户端定期上报运行状态与中继连接信息,返回活跃配置元数据与隧道设置 + parameters: + - description: 心跳载荷 + in: body + name: body + required: true + schema: + $ref: '#/definitions/flared.HeartbeatPayload' + produces: + - application/json + responses: + "200": + description: 心跳响应 + schema: + allOf: + - $ref: '#/definitions/response.Any' + - properties: + data: + $ref: '#/definitions/flared.HeartbeatResponse' + type: object + "400": + description: 参数错误 + schema: + $ref: '#/definitions/response.Any' + "401": + description: Tunnel Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - TunnelTokenAuth: [] + summary: 上报 Tunnel 心跳 + tags: + - openflare-tunnel + /api/v1/tunnel/ws: + get: + description: 将已认证的 Tunnel 客户端连接升级为 WebSocket 长连接,用于配置推送 + responses: + "401": + description: Tunnel Token 无效 + schema: + $ref: '#/definitions/response.Any' + "403": + description: 节点类型不匹配 + schema: + $ref: '#/definitions/response.Any' + security: + - TunnelTokenAuth: [] + summary: 升级 Tunnel WebSocket 连接 + tags: + - openflare-tunnel /api/v1/upload: post: consumes: diff --git a/frontend/lib/services/openflare/types.ts b/frontend/lib/services/openflare/types.ts index fbd87362..6670bea2 100644 --- a/frontend/lib/services/openflare/types.ts +++ b/frontend/lib/services/openflare/types.ts @@ -523,7 +523,7 @@ export interface AccessLogFilters { } export interface AccessLogItem { - id: number; + id: string; node_id: string; node_name: string; logged_at: string; diff --git a/internal/apps/openflare/observability/access_log_logics.go b/internal/apps/openflare/observability/access_log_logics.go index ed19ad9d..e2e8bbff 100644 --- a/internal/apps/openflare/observability/access_log_logics.go +++ b/internal/apps/openflare/observability/access_log_logics.go @@ -4,6 +4,7 @@ package observability import ( "context" "errors" + "strconv" "strings" "time" @@ -41,7 +42,7 @@ type AccessLogQuery struct { // AccessLogView is a single access log row. type AccessLogView struct { - ID uint `json:"id"` + ID string `json:"id"` NodeID string `json:"node_id"` NodeName string `json:"node_name"` LoggedAt time.Time `json:"logged_at"` @@ -209,7 +210,7 @@ func ListAccessLogs(ctx context.Context, input AccessLogQuery) (*AccessLogList, continue } views = append(views, AccessLogView{ - ID: item.ID, + ID: formatAccessLogID(item.ID), NodeID: item.NodeID, NodeName: nodeNames[item.NodeID], LoggedAt: item.LoggedAt, @@ -634,3 +635,7 @@ func normalizeFoldMinutes(value int) (int, error) { return 0, errors.New("fold_minutes 仅支持 3 或 5") } } + +func formatAccessLogID(id uint64) string { + return strconv.FormatUint(id, 10) +} diff --git a/internal/model/openflare_access_log.go b/internal/model/openflare_access_log.go index 338ca74b..08fe99cf 100644 --- a/internal/model/openflare_access_log.go +++ b/internal/model/openflare_access_log.go @@ -476,7 +476,7 @@ func openFlareAccessLogStatusCodeToInt32(code int) int32 { } } -func openFlareAccessLogUintToInt64(value uint) int64 { +func openFlareAccessLogUintToInt64(value uint64) int64 { if value > math.MaxInt64 { return math.MaxInt64 } diff --git a/internal/model/openflare_access_log_store.go b/internal/model/openflare_access_log_store.go index c6ee4e57..9c5f34b1 100644 --- a/internal/model/openflare_access_log_store.go +++ b/internal/model/openflare_access_log_store.go @@ -216,7 +216,7 @@ func toNodeAccessLogFilter(query OpenFlareAccessLogQuery) analyticsrepo.NodeAcce func toAnalyticsNodeAccessLog(record *OpenFlareAccessLog) analyticsmodel.NodeAccessLog { return analyticsmodel.NodeAccessLog{ - ID: uint64(record.ID), + ID: record.ID, NodeID: record.NodeID, LoggedAt: record.LoggedAt, RemoteAddr: record.RemoteAddr, @@ -232,7 +232,7 @@ func fromAnalyticsNodeAccessLogs(rows []analyticsmodel.NodeAccessLog) []*OpenFla result := make([]*OpenFlareAccessLog, len(rows)) for index, row := range rows { result[index] = &OpenFlareAccessLog{ - ID: uint(row.ID), + ID: row.ID, NodeID: row.NodeID, LoggedAt: row.LoggedAt, RemoteAddr: row.RemoteAddr, diff --git a/internal/model/openflare_access_log_store_memory.go b/internal/model/openflare_access_log_store_memory.go index 704b82a9..728a9367 100644 --- a/internal/model/openflare_access_log_store_memory.go +++ b/internal/model/openflare_access_log_store_memory.go @@ -28,7 +28,7 @@ func (s *memoryAccessLogStore) InsertBatch(_ context.Context, records []*OpenFla } copyRecord := *record if copyRecord.ID == 0 { - copyRecord.ID = uint(idgen.NextUint64ID()) + copyRecord.ID = idgen.NextUint64ID() } if copyRecord.CreatedAt.IsZero() { copyRecord.CreatedAt = now diff --git a/internal/model/openflare_observability.go b/internal/model/openflare_observability.go index cacb10e1..2eeb526f 100644 --- a/internal/model/openflare_observability.go +++ b/internal/model/openflare_observability.go @@ -60,7 +60,7 @@ func (OpenFlareRequestReport) TableName() string { // OpenFlareAccessLog stores a single access log row in ClickHouse (database: openflare, table: of_node_access_logs). // ClickHouse DDL is managed by goose; reads/writes go through internal/repository/analytics. type OpenFlareAccessLog struct { - ID uint `json:"id" gorm:"primaryKey;autoIncrement"` + ID uint64 `json:"id,string" gorm:"column:id"` NodeID string `json:"node_id" gorm:"index;size:64;not null"` LoggedAt time.Time `json:"logged_at" gorm:"index"` RemoteAddr string `json:"remote_addr" gorm:"index;size:128"` diff --git a/internal/model/openflare_observability_store_memory.go b/internal/model/openflare_observability_store_memory.go index d31321ef..1674d6b4 100644 --- a/internal/model/openflare_observability_store_memory.go +++ b/internal/model/openflare_observability_store_memory.go @@ -356,7 +356,7 @@ func sortOpenFlareMetricSnapshots(items []*OpenFlareMetricSnapshot) { if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 { return compare > 0 } - return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(left.ID), openFlareAccessLogUintToInt64(right.ID)) > 0 + return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0 }) } @@ -370,7 +370,7 @@ func sortOpenFlareRequestReports(items []*OpenFlareRequestReport) { if compare := openFlareAccessLogCompareInt64(left.WindowEndedAt.Unix(), right.WindowEndedAt.Unix()); compare != 0 { return compare > 0 } - return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(left.ID), openFlareAccessLogUintToInt64(right.ID)) > 0 + return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0 }) } @@ -384,7 +384,7 @@ func sortOpenFlareNodeObservationOpenresty(items []*OpenFlareNodeObservationOpen if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 { return compare > 0 } - return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(left.ID), openFlareAccessLogUintToInt64(right.ID)) > 0 + return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0 }) } @@ -398,7 +398,7 @@ func sortOpenFlareNodeObservationFrps(items []*OpenFlareNodeObservationFrps) { if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 { return compare > 0 } - return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(left.ID), openFlareAccessLogUintToInt64(right.ID)) > 0 + return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0 }) } @@ -412,7 +412,7 @@ func sortOpenFlareNodeObservationFrpc(items []*OpenFlareNodeObservationFrpc) { if compare := openFlareAccessLogCompareInt64(left.CapturedAt.Unix(), right.CapturedAt.Unix()); compare != 0 { return compare > 0 } - return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(left.ID), openFlareAccessLogUintToInt64(right.ID)) > 0 + return openFlareAccessLogCompareInt64(openFlareAccessLogUintToInt64(uint64(left.ID)), openFlareAccessLogUintToInt64(uint64(right.ID))) > 0 }) }