From 53bd451450d4e2970594430c89881c81a7fe1d46 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 19 Jun 2026 11:17:45 +0800 Subject: [PATCH] refactor(openflare): remove legacy update API, use admin updater Drop the migrated /api/v1/openflare/update compatibility layer and point the OpenFlare upgrade UI at Wavelet's /api/v1/admin/update endpoints. --- Wavelet/docs/docs.go | 1663 ----------------- Wavelet/docs/swagger.json | 1663 ----------------- Wavelet/docs/swagger.yaml | 1047 ----------- .../settings/components/openflare-ops.tsx | 15 +- .../(main)/components/upgrade-log-panel.tsx | 71 - .../components/version-upgrade-dialog.tsx | 386 ++-- .../lib/hooks/use-openflare-server-upgrade.ts | 279 +-- Wavelet/frontend/lib/services/index.ts | 4 - .../frontend/lib/services/openflare/index.ts | 7 - .../frontend/lib/services/openflare/types.ts | 39 - .../lib/services/openflare/update.service.ts | 131 -- .../internal/apps/openflare/update/logics.go | 262 --- .../internal/apps/openflare/update/routers.go | 157 -- .../router/v1/openflare/register_update.go | 22 - Wavelet/internal/router/v1/openflare/v1.go | 1 - docs/changelog/index.md | 1 + 16 files changed, 142 insertions(+), 5606 deletions(-) delete mode 100644 Wavelet/frontend/app/(main)/components/upgrade-log-panel.tsx delete mode 100644 Wavelet/frontend/lib/services/openflare/update.service.ts delete mode 100644 Wavelet/internal/apps/openflare/update/logics.go delete mode 100644 Wavelet/internal/apps/openflare/update/routers.go delete mode 100644 Wavelet/internal/router/v1/openflare/register_update.go diff --git a/Wavelet/docs/docs.go b/Wavelet/docs/docs.go index 67fb5d2d..584646c6 100644 --- a/Wavelet/docs/docs.go +++ b/Wavelet/docs/docs.go @@ -928,231 +928,6 @@ const docTemplate = `{ } } }, - "/api/v1/admin/logs": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取系统历史日志,cursor=0 获取最新日志,cursor\u003e0 获取更早日志", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取系统日志", - "parameters": [ - { - "type": "integer", - "default": 0, - "description": "日志游标,0=获取最新", - "name": "cursor", - "in": "query" - }, - { - "type": "integer", - "default": 200, - "description": "每页条数", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "日志列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.logsResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/access": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页并按照用户、接口路径、时间范围等维度检索 ClickHouse 用户访问日志列表(需要管理员权限,ClickHouse 未启用时报错)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取用户访问日志", - "parameters": [ - { - "type": "integer", - "default": 1, - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 20, - "description": "每页条数", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "用户名模糊搜索", - "name": "username", - "in": "query" - }, - { - "type": "string", - "description": "接口路径模糊搜索", - "name": "path", - "in": "query" - }, - { - "type": "string", - "description": "起始时间(RFC3339 或 YYYY-MM-DD HH:MM:SS)", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "结束时间(RFC3339 或 YYYY-MM-DD HH:MM:SS)", - "name": "end_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "访问日志列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.accessLogsResponse" - } - } - } - ] - } - }, - "400": { - "description": "ClickHouse 未启用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/analytics": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "聚合统计最近 7 天的每日访问趋势、浏览器分布以及前 10 名最活跃用户排行(需要管理员权限,ClickHouse 未启用时报错)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取访问日志分析数据", - "responses": { - "200": { - "description": "分析统计数据", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.logsAnalyticsResponse" - } - } - } - ] - } - }, - "400": { - "description": "ClickHouse 未启用", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/ws": { - "get": { - "description": "通过 WebSocket 实时推送系统日志,需要管理员权限", - "tags": [ - "admin" - ], - "summary": "系统日志实时推送", - "responses": {} - } - }, "/api/v1/admin/push/channels": { "get": { "security": [ @@ -3301,362 +3076,6 @@ const docTemplate = `{ } } }, - "/api/v1/admin/uploads": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取系统上传的文件列表,支持文件名关键词、业务类型、扩展名、上传用户ID过滤", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件列表", - "parameters": [ - { - "type": "integer", - "description": "页码(默认 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页数量(默认 20,最大 100)", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "文件名关键词(模糊匹配)", - "name": "keyword", - "in": "query" - }, - { - "type": "string", - "description": "业务分类过滤", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "扩展名过滤", - "name": "extension", - "in": "query" - }, - { - "type": "integer", - "format": "int64", - "description": "上传用户 ID", - "name": "user_id", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.listFilesResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/download/batch": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "传入多个文件 ID,后台实时将其打包压缩为 ZIP 流并输出,自动处理文件名重复冲突", - "consumes": [ - "application/json" - ], - "produces": [ - "application/octet-stream" - ], - "tags": [ - "admin" - ], - "summary": "批量打包下载", - "parameters": [ - { - "description": "包含文件 ID 数组 of string 的请求体", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handler.batchDownloadRequest" - } - } - ], - "responses": { - "200": { - "description": "成功下载打包后的 ZIP", - "schema": { - "type": "file" - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "打包失败", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/download/{id}": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "根据文件 ID 获取文件,以附件形式 (Attachment) 强制开启客户端浏览器下载", - "produces": [ - "application/octet-stream" - ], - "tags": [ - "admin" - ], - "summary": "下载单文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "图片质量 (low, medium, high, origin),默认为 origin", - "name": "quality", - "in": "query" - } - ], - "responses": { - "200": { - "description": "成功下载文件", - "schema": { - "type": "file" - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "服务内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/stats": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "返回系统级的总文件数、占用大小、最近 7 天新增趋势、文件类型/格式分布等数据", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件统计数据", - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.fileStatsResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/types": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "返回数据库中所有已上传文件实际拥有的业务类型列表", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件业务类型列表", - "responses": { - "200": { - "description": "业务类型列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/{id}": { - "delete": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "将文件状态置为 deleted(软删除),不会立即清理底层存储对象", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "删除文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/admin/users": { "get": { "security": [ @@ -10077,272 +9496,6 @@ const docTemplate = `{ } } }, - "/api/v1/openflare/update/latest-release": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "查询 OpenFlare 服务端最新 GitHub Release 及升级状态,需要管理员权限", - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "获取最新服务端发布版本", - "parameters": [ - { - "type": "string", - "description": "发布渠道(stable/preview)", - "name": "channel", - "in": "query" - } - ], - "responses": { - "200": { - "description": "最新发布信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/update.LatestReleaseView" - } - } - } - ] - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/logs/ws": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "通过 WebSocket 推送升级进度快照,需要管理员权限", - "tags": [ - "openflare-update" - ], - "summary": "流式获取服务端升级日志", - "responses": { - "101": { - "description": "WebSocket 升级日志流", - "schema": { - "$ref": "#/definitions/update.StreamSnapshot" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/manual-upgrade": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "确认并执行手动上传的服务端升级(当前功能已禁用),需要管理员权限", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "确认手动服务端升级", - "responses": { - "400": { - "description": "功能已禁用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/manual-upload": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "上传服务端二进制以进行手动升级(当前功能已禁用),需要管理员权限", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "上传手动升级二进制", - "parameters": [ - { - "type": "file", - "description": "服务端二进制文件", - "name": "binary", - "in": "formData", - "required": true - } - ], - "responses": { - "400": { - "description": "功能已禁用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/upgrade": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "从最新 Release 调度 OpenFlare 服务端自动升级,需要管理员权限", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "触发服务端自动升级", - "parameters": [ - { - "description": "升级参数", - "name": "request", - "in": "body", - "schema": { - "$ref": "#/definitions/update.upgradeRequest" - } - } - ], - "responses": { - "200": { - "description": "升级任务已调度", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/update.LatestReleaseView" - } - } - } - ] - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/openflare/uptimekuma/sync": { "post": { "security": [ @@ -11538,274 +10691,6 @@ const docTemplate = `{ } } }, - "/api/v1/upload": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "支持各种类型的通用文件上传,支持自动文件类型检测、哈希计算与“秒传”去重", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "上传文件", - "parameters": [ - { - "type": "file", - "description": "要上传的文件", - "name": "file", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "业务分类 (例如: avatar, attachment, doc,默认为 generic)", - "name": "type", - "in": "formData" - }, - { - "type": "string", - "description": "额外的 JSON 格式元数据", - "name": "metadata", - "in": "formData" - } - ], - "responses": { - "200": { - "description": "上传成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Upload" - } - } - } - ] - } - }, - "400": { - "description": "请求参数错误或文件受限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/upload/my": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取当前登录用户上传的文件,支持文件名关键词、业务类型、扩展名过滤", - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "获取我的文件列表", - "parameters": [ - { - "type": "integer", - "description": "页码(默认 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页数量(默认 20,最大 100)", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "文件名关键词(模糊匹配)", - "name": "keyword", - "in": "query" - }, - { - "type": "string", - "description": "业务分类过滤", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "扩展名过滤", - "name": "extension", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.listMyFilesResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/upload/{id}": { - "put": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "更新当前用户本人的文件名或访问权限模式 (AccessMode)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "更新我的文件信息", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "更新字段", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handler.updateMyFileRequest" - } - } - ], - "responses": { - "200": { - "description": "更新成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Upload" - } - } - } - ] - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - }, - "delete": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "将当前用户本人的文件状态置为 deleted(软删除)", - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "删除我的文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/user-info": { "get": { "security": [ @@ -12412,65 +11297,6 @@ const docTemplate = `{ } } }, - "/f/{id}": { - "get": { - "description": "根据文件 ID 获取并提供已上传的临时或正式文件,若配置了缓存则优先走本地缓存,否则从 S3 等后端存储读取并流式返回", - "produces": [ - "application/octet-stream" - ], - "tags": [ - "upload" - ], - "summary": "获取已上传文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "图片质量 (low, medium, high, origin),默认为 origin", - "name": "quality", - "in": "query" - } - ], - "responses": { - "200": { - "description": "成功获取文件内容", - "schema": { - "type": "file" - } - }, - "400": { - "description": "文件 ID 格式错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件未找到", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "服务内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/robots.txt": { "get": { "description": "根据系统配置决定是否允许搜索引擎检索,并返回相应的 robots.txt 文件内容", @@ -13049,285 +11875,6 @@ const docTemplate = `{ } } }, - "handler.batchDownloadRequest": { - "type": "object", - "required": [ - "ids" - ], - "properties": { - "ids": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - } - }, - "handler.distributionItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "handler.fileStatsResponse": { - "type": "object", - "properties": { - "categories": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.distributionItem" - } - }, - "total_count": { - "type": "integer" - }, - "total_size": { - "type": "integer" - }, - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.trendItem" - } - }, - "types": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.distributionItem" - } - } - } - }, - "handler.listFilesResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Upload" - } - }, - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - } - } - }, - "handler.listMyFilesResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Upload" - } - }, - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - } - } - }, - "handler.trendItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "handler.updateMyFileRequest": { - "type": "object", - "properties": { - "access_mode": { - "type": "integer", - "enum": [ - 0, - 1 - ] - }, - "file_name": { - "type": "string", - "maxLength": 255 - } - } - }, - "logger.LogEntry": { - "type": "object", - "properties": { - "data": { - "description": "一行日志原文(含换行符)", - "type": "string" - }, - "index": { - "description": "全局递增序号", - "type": "integer" - } - } - }, - "logs.accessLogItem": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "headers": { - "type": "string" - }, - "id": { - "type": "string", - "example": "0" - }, - "ip": { - "type": "string" - }, - "latency": { - "type": "integer" - }, - "method": { - "type": "string" - }, - "nickname": { - "type": "string" - }, - "path": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "user_agent": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - }, - "username": { - "type": "string" - } - } - }, - "logs.accessLogsResponse": { - "type": "object", - "properties": { - "list": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.accessLogItem" - } - }, - "total": { - "type": "integer" - } - } - }, - "logs.browserItem": { - "type": "object", - "properties": { - "browser": { - "type": "string" - }, - "count": { - "type": "integer" - } - } - }, - "logs.logsAnalyticsResponse": { - "type": "object", - "properties": { - "browsers": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.browserItem" - } - }, - "top_users": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.topUserItem" - } - }, - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.trendItem" - } - } - } - }, - "logs.logsResponse": { - "type": "object", - "properties": { - "has_more": { - "type": "boolean" - }, - "lines": { - "type": "array", - "items": { - "$ref": "#/definitions/logger.LogEntry" - } - }, - "next_cursor": { - "description": "用于加载更早日志的 cursor", - "type": "integer" - } - } - }, - "logs.topUserItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "nickname": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - }, - "username": { - "type": "string" - } - } - }, - "logs.trendItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "string" - } - } - }, "model.AccessToken": { "type": "object", "properties": { @@ -14127,133 +12674,6 @@ const docTemplate = `{ } } }, - "model.Upload": { - "type": "object", - "properties": { - "access_mode": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "extension": { - "description": "文件后缀名 (不含点,如 png, pdf)", - "type": "string" - }, - "file_name": { - "description": "原始文件名 (例如: image.png)", - "type": "string" - }, - "file_path": { - "description": "文件相对路径 / S3 Key", - "type": "string" - }, - "file_size": { - "description": "文件大小(字节)", - "type": "integer" - }, - "hash": { - "description": "文件哈希 (SHA-256/MD5,可用于排重)", - "type": "string" - }, - "id": { - "type": "string", - "example": "0" - }, - "metadata": { - "description": "业务扩展元数据", - "allOf": [ - { - "$ref": "#/definitions/model.UploadMetadata" - } - ] - }, - "mime_type": { - "description": "媒体类型 (MIME, 如 image/png)", - "type": "string" - }, - "status": { - "description": "状态", - "allOf": [ - { - "$ref": "#/definitions/model.UploadStatus" - } - ] - }, - "type": { - "description": "业务标识类型 (如 avatar, doc, attachment)", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - } - } - }, - "model.UploadMetadata": { - "type": "object", - "properties": { - "bucket": { - "description": "存储桶名称 (适用于 S3 等)", - "type": "string" - }, - "client_ip": { - "description": "上传者 IP", - "type": "string" - }, - "duration": { - "description": "音视频时长 (s)", - "type": "number" - }, - "extra": { - "description": "其它任意业务自定义元数据", - "type": "object", - "additionalProperties": {} - }, - "height": { - "description": "图像/视频高度 (px)", - "type": "integer" - }, - "original_mime": { - "description": "原始 MIME 类型", - "type": "string" - }, - "user_agent": { - "description": "上传者的 UA", - "type": "string" - }, - "width": { - "description": "图像/视频宽度 (px)", - "type": "integer" - } - } - }, - "model.UploadStatus": { - "type": "string", - "enum": [ - "pending", - "used", - "deleted" - ], - "x-enum-comments": { - "UploadStatusDeleted": "已删除", - "UploadStatusPending": "待使用", - "UploadStatusUsed": "已使用" - }, - "x-enum-descriptions": [ - "待使用", - "已使用", - "已删除" - ], - "x-enum-varnames": [ - "UploadStatusPending", - "UploadStatusUsed", - "UploadStatusDeleted" - ] - }, "node.AgentReleaseInfo": { "type": "object", "properties": { @@ -16719,89 +15139,6 @@ const docTemplate = `{ } } }, - "update.LatestReleaseView": { - "type": "object", - "properties": { - "body": { - "type": "string" - }, - "channel": { - "type": "string" - }, - "current_version": { - "type": "string" - }, - "has_update": { - "type": "boolean" - }, - "html_url": { - "type": "string" - }, - "in_progress": { - "type": "boolean" - }, - "prerelease": { - "type": "boolean" - }, - "published_at": { - "type": "string" - }, - "tag_name": { - "type": "string" - }, - "upgrade_logs": { - "type": "array", - "items": { - "$ref": "#/definitions/update.UpgradeLogRecord" - } - }, - "upgrade_status": { - "type": "string" - }, - "upgrade_supported": { - "type": "boolean" - } - } - }, - "update.StreamSnapshot": { - "type": "object", - "properties": { - "in_progress": { - "type": "boolean" - }, - "upgrade_logs": { - "type": "array", - "items": { - "$ref": "#/definitions/update.UpgradeLogRecord" - } - }, - "upgrade_status": { - "type": "string" - } - } - }, - "update.UpgradeLogRecord": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "level": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "update.upgradeRequest": { - "type": "object", - "properties": { - "channel": { - "type": "string" - } - } - }, "updater.Status": { "type": "object", "properties": { diff --git a/Wavelet/docs/swagger.json b/Wavelet/docs/swagger.json index 6798434c..870fc6ce 100644 --- a/Wavelet/docs/swagger.json +++ b/Wavelet/docs/swagger.json @@ -921,231 +921,6 @@ } } }, - "/api/v1/admin/logs": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取系统历史日志,cursor=0 获取最新日志,cursor\u003e0 获取更早日志", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取系统日志", - "parameters": [ - { - "type": "integer", - "default": 0, - "description": "日志游标,0=获取最新", - "name": "cursor", - "in": "query" - }, - { - "type": "integer", - "default": 200, - "description": "每页条数", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "日志列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.logsResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/access": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页并按照用户、接口路径、时间范围等维度检索 ClickHouse 用户访问日志列表(需要管理员权限,ClickHouse 未启用时报错)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取用户访问日志", - "parameters": [ - { - "type": "integer", - "default": 1, - "description": "页码", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 20, - "description": "每页条数", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "用户名模糊搜索", - "name": "username", - "in": "query" - }, - { - "type": "string", - "description": "接口路径模糊搜索", - "name": "path", - "in": "query" - }, - { - "type": "string", - "description": "起始时间(RFC3339 或 YYYY-MM-DD HH:MM:SS)", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "结束时间(RFC3339 或 YYYY-MM-DD HH:MM:SS)", - "name": "end_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "访问日志列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.accessLogsResponse" - } - } - } - ] - } - }, - "400": { - "description": "ClickHouse 未启用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/analytics": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "聚合统计最近 7 天的每日访问趋势、浏览器分布以及前 10 名最活跃用户排行(需要管理员权限,ClickHouse 未启用时报错)", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取访问日志分析数据", - "responses": { - "200": { - "description": "分析统计数据", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/logs.logsAnalyticsResponse" - } - } - } - ] - } - }, - "400": { - "description": "ClickHouse 未启用", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/logs/ws": { - "get": { - "description": "通过 WebSocket 实时推送系统日志,需要管理员权限", - "tags": [ - "admin" - ], - "summary": "系统日志实时推送", - "responses": {} - } - }, "/api/v1/admin/push/channels": { "get": { "security": [ @@ -3294,362 +3069,6 @@ } } }, - "/api/v1/admin/uploads": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取系统上传的文件列表,支持文件名关键词、业务类型、扩展名、上传用户ID过滤", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件列表", - "parameters": [ - { - "type": "integer", - "description": "页码(默认 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页数量(默认 20,最大 100)", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "文件名关键词(模糊匹配)", - "name": "keyword", - "in": "query" - }, - { - "type": "string", - "description": "业务分类过滤", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "扩展名过滤", - "name": "extension", - "in": "query" - }, - { - "type": "integer", - "format": "int64", - "description": "上传用户 ID", - "name": "user_id", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.listFilesResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/download/batch": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "传入多个文件 ID,后台实时将其打包压缩为 ZIP 流并输出,自动处理文件名重复冲突", - "consumes": [ - "application/json" - ], - "produces": [ - "application/octet-stream" - ], - "tags": [ - "admin" - ], - "summary": "批量打包下载", - "parameters": [ - { - "description": "包含文件 ID 数组 of string 的请求体", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handler.batchDownloadRequest" - } - } - ], - "responses": { - "200": { - "description": "成功下载打包后的 ZIP", - "schema": { - "type": "file" - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "打包失败", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/download/{id}": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "根据文件 ID 获取文件,以附件形式 (Attachment) 强制开启客户端浏览器下载", - "produces": [ - "application/octet-stream" - ], - "tags": [ - "admin" - ], - "summary": "下载单文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "图片质量 (low, medium, high, origin),默认为 origin", - "name": "quality", - "in": "query" - } - ], - "responses": { - "200": { - "description": "成功下载文件", - "schema": { - "type": "file" - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "服务内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/stats": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "返回系统级的总文件数、占用大小、最近 7 天新增趋势、文件类型/格式分布等数据", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件统计数据", - "responses": { - "200": { - "description": "获取成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.fileStatsResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/types": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "返回数据库中所有已上传文件实际拥有的业务类型列表", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "获取文件业务类型列表", - "responses": { - "200": { - "description": "业务类型列表", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/admin/uploads/{id}": { - "delete": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "将文件状态置为 deleted(软删除),不会立即清理底层存储对象", - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "删除文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/admin/users": { "get": { "security": [ @@ -10070,272 +9489,6 @@ } } }, - "/api/v1/openflare/update/latest-release": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "查询 OpenFlare 服务端最新 GitHub Release 及升级状态,需要管理员权限", - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "获取最新服务端发布版本", - "parameters": [ - { - "type": "string", - "description": "发布渠道(stable/preview)", - "name": "channel", - "in": "query" - } - ], - "responses": { - "200": { - "description": "最新发布信息", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/update.LatestReleaseView" - } - } - } - ] - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/logs/ws": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "通过 WebSocket 推送升级进度快照,需要管理员权限", - "tags": [ - "openflare-update" - ], - "summary": "流式获取服务端升级日志", - "responses": { - "101": { - "description": "WebSocket 升级日志流", - "schema": { - "$ref": "#/definitions/update.StreamSnapshot" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/manual-upgrade": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "确认并执行手动上传的服务端升级(当前功能已禁用),需要管理员权限", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "确认手动服务端升级", - "responses": { - "400": { - "description": "功能已禁用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/manual-upload": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "上传服务端二进制以进行手动升级(当前功能已禁用),需要管理员权限", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "上传手动升级二进制", - "parameters": [ - { - "type": "file", - "description": "服务端二进制文件", - "name": "binary", - "in": "formData", - "required": true - } - ], - "responses": { - "400": { - "description": "功能已禁用或参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/openflare/update/upgrade": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "从最新 Release 调度 OpenFlare 服务端自动升级,需要管理员权限", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "openflare-update" - ], - "summary": "触发服务端自动升级", - "parameters": [ - { - "description": "升级参数", - "name": "request", - "in": "body", - "schema": { - "$ref": "#/definitions/update.upgradeRequest" - } - } - ], - "responses": { - "200": { - "description": "升级任务已调度", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/update.LatestReleaseView" - } - } - } - ] - } - }, - "400": { - "description": "参数错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无管理员权限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/openflare/uptimekuma/sync": { "post": { "security": [ @@ -11531,274 +10684,6 @@ } } }, - "/api/v1/upload": { - "post": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "支持各种类型的通用文件上传,支持自动文件类型检测、哈希计算与“秒传”去重", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "上传文件", - "parameters": [ - { - "type": "file", - "description": "要上传的文件", - "name": "file", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "业务分类 (例如: avatar, attachment, doc,默认为 generic)", - "name": "type", - "in": "formData" - }, - { - "type": "string", - "description": "额外的 JSON 格式元数据", - "name": "metadata", - "in": "formData" - } - ], - "responses": { - "200": { - "description": "上传成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Upload" - } - } - } - ] - } - }, - "400": { - "description": "请求参数错误或文件受限", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/upload/my": { - "get": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "分页获取当前登录用户上传的文件,支持文件名关键词、业务类型、扩展名过滤", - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "获取我的文件列表", - "parameters": [ - { - "type": "integer", - "description": "页码(默认 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "每页数量(默认 20,最大 100)", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "文件名关键词(模糊匹配)", - "name": "keyword", - "in": "query" - }, - { - "type": "string", - "description": "业务分类过滤", - "name": "type", - "in": "query" - }, - { - "type": "string", - "description": "扩展名过滤", - "name": "extension", - "in": "query" - } - ], - "responses": { - "200": { - "description": "查询成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/handler.listMyFilesResponse" - } - } - } - ] - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, - "/api/v1/upload/{id}": { - "put": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "更新当前用户本人的文件名或访问权限模式 (AccessMode)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "更新我的文件信息", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "更新字段", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handler.updateMyFileRequest" - } - } - ], - "responses": { - "200": { - "description": "更新成功", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Any" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/model.Upload" - } - } - } - ] - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - }, - "delete": { - "security": [ - { - "SessionCookie": [] - } - ], - "description": "将当前用户本人的文件状态置为 deleted(软删除)", - "produces": [ - "application/json" - ], - "tags": [ - "upload" - ], - "summary": "删除我的文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "删除成功", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "403": { - "description": "无权操作", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件不存在", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/api/v1/user-info": { "get": { "security": [ @@ -12405,65 +11290,6 @@ } } }, - "/f/{id}": { - "get": { - "description": "根据文件 ID 获取并提供已上传的临时或正式文件,若配置了缓存则优先走本地缓存,否则从 S3 等后端存储读取并流式返回", - "produces": [ - "application/octet-stream" - ], - "tags": [ - "upload" - ], - "summary": "获取已上传文件", - "parameters": [ - { - "type": "string", - "description": "文件 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "图片质量 (low, medium, high, origin),默认为 origin", - "name": "quality", - "in": "query" - } - ], - "responses": { - "200": { - "description": "成功获取文件内容", - "schema": { - "type": "file" - } - }, - "400": { - "description": "文件 ID 格式错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "401": { - "description": "未登录", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "404": { - "description": "文件未找到", - "schema": { - "$ref": "#/definitions/response.Any" - } - }, - "500": { - "description": "服务内部错误", - "schema": { - "$ref": "#/definitions/response.Any" - } - } - } - } - }, "/robots.txt": { "get": { "description": "根据系统配置决定是否允许搜索引擎检索,并返回相应的 robots.txt 文件内容", @@ -13042,285 +11868,6 @@ } } }, - "handler.batchDownloadRequest": { - "type": "object", - "required": [ - "ids" - ], - "properties": { - "ids": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - } - }, - "handler.distributionItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "handler.fileStatsResponse": { - "type": "object", - "properties": { - "categories": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.distributionItem" - } - }, - "total_count": { - "type": "integer" - }, - "total_size": { - "type": "integer" - }, - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.trendItem" - } - }, - "types": { - "type": "array", - "items": { - "$ref": "#/definitions/handler.distributionItem" - } - } - } - }, - "handler.listFilesResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Upload" - } - }, - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - } - } - }, - "handler.listMyFilesResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Upload" - } - }, - "page": { - "type": "integer" - }, - "page_size": { - "type": "integer" - }, - "total": { - "type": "integer" - } - } - }, - "handler.trendItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "handler.updateMyFileRequest": { - "type": "object", - "properties": { - "access_mode": { - "type": "integer", - "enum": [ - 0, - 1 - ] - }, - "file_name": { - "type": "string", - "maxLength": 255 - } - } - }, - "logger.LogEntry": { - "type": "object", - "properties": { - "data": { - "description": "一行日志原文(含换行符)", - "type": "string" - }, - "index": { - "description": "全局递增序号", - "type": "integer" - } - } - }, - "logs.accessLogItem": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "headers": { - "type": "string" - }, - "id": { - "type": "string", - "example": "0" - }, - "ip": { - "type": "string" - }, - "latency": { - "type": "integer" - }, - "method": { - "type": "string" - }, - "nickname": { - "type": "string" - }, - "path": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "user_agent": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - }, - "username": { - "type": "string" - } - } - }, - "logs.accessLogsResponse": { - "type": "object", - "properties": { - "list": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.accessLogItem" - } - }, - "total": { - "type": "integer" - } - } - }, - "logs.browserItem": { - "type": "object", - "properties": { - "browser": { - "type": "string" - }, - "count": { - "type": "integer" - } - } - }, - "logs.logsAnalyticsResponse": { - "type": "object", - "properties": { - "browsers": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.browserItem" - } - }, - "top_users": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.topUserItem" - } - }, - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/logs.trendItem" - } - } - } - }, - "logs.logsResponse": { - "type": "object", - "properties": { - "has_more": { - "type": "boolean" - }, - "lines": { - "type": "array", - "items": { - "$ref": "#/definitions/logger.LogEntry" - } - }, - "next_cursor": { - "description": "用于加载更早日志的 cursor", - "type": "integer" - } - } - }, - "logs.topUserItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "nickname": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - }, - "username": { - "type": "string" - } - } - }, - "logs.trendItem": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "string" - } - } - }, "model.AccessToken": { "type": "object", "properties": { @@ -14120,133 +12667,6 @@ } } }, - "model.Upload": { - "type": "object", - "properties": { - "access_mode": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "extension": { - "description": "文件后缀名 (不含点,如 png, pdf)", - "type": "string" - }, - "file_name": { - "description": "原始文件名 (例如: image.png)", - "type": "string" - }, - "file_path": { - "description": "文件相对路径 / S3 Key", - "type": "string" - }, - "file_size": { - "description": "文件大小(字节)", - "type": "integer" - }, - "hash": { - "description": "文件哈希 (SHA-256/MD5,可用于排重)", - "type": "string" - }, - "id": { - "type": "string", - "example": "0" - }, - "metadata": { - "description": "业务扩展元数据", - "allOf": [ - { - "$ref": "#/definitions/model.UploadMetadata" - } - ] - }, - "mime_type": { - "description": "媒体类型 (MIME, 如 image/png)", - "type": "string" - }, - "status": { - "description": "状态", - "allOf": [ - { - "$ref": "#/definitions/model.UploadStatus" - } - ] - }, - "type": { - "description": "业务标识类型 (如 avatar, doc, attachment)", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "string", - "example": "0" - } - } - }, - "model.UploadMetadata": { - "type": "object", - "properties": { - "bucket": { - "description": "存储桶名称 (适用于 S3 等)", - "type": "string" - }, - "client_ip": { - "description": "上传者 IP", - "type": "string" - }, - "duration": { - "description": "音视频时长 (s)", - "type": "number" - }, - "extra": { - "description": "其它任意业务自定义元数据", - "type": "object", - "additionalProperties": {} - }, - "height": { - "description": "图像/视频高度 (px)", - "type": "integer" - }, - "original_mime": { - "description": "原始 MIME 类型", - "type": "string" - }, - "user_agent": { - "description": "上传者的 UA", - "type": "string" - }, - "width": { - "description": "图像/视频宽度 (px)", - "type": "integer" - } - } - }, - "model.UploadStatus": { - "type": "string", - "enum": [ - "pending", - "used", - "deleted" - ], - "x-enum-comments": { - "UploadStatusDeleted": "已删除", - "UploadStatusPending": "待使用", - "UploadStatusUsed": "已使用" - }, - "x-enum-descriptions": [ - "待使用", - "已使用", - "已删除" - ], - "x-enum-varnames": [ - "UploadStatusPending", - "UploadStatusUsed", - "UploadStatusDeleted" - ] - }, "node.AgentReleaseInfo": { "type": "object", "properties": { @@ -16712,89 +15132,6 @@ } } }, - "update.LatestReleaseView": { - "type": "object", - "properties": { - "body": { - "type": "string" - }, - "channel": { - "type": "string" - }, - "current_version": { - "type": "string" - }, - "has_update": { - "type": "boolean" - }, - "html_url": { - "type": "string" - }, - "in_progress": { - "type": "boolean" - }, - "prerelease": { - "type": "boolean" - }, - "published_at": { - "type": "string" - }, - "tag_name": { - "type": "string" - }, - "upgrade_logs": { - "type": "array", - "items": { - "$ref": "#/definitions/update.UpgradeLogRecord" - } - }, - "upgrade_status": { - "type": "string" - }, - "upgrade_supported": { - "type": "boolean" - } - } - }, - "update.StreamSnapshot": { - "type": "object", - "properties": { - "in_progress": { - "type": "boolean" - }, - "upgrade_logs": { - "type": "array", - "items": { - "$ref": "#/definitions/update.UpgradeLogRecord" - } - }, - "upgrade_status": { - "type": "string" - } - } - }, - "update.UpgradeLogRecord": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "level": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "update.upgradeRequest": { - "type": "object", - "properties": { - "channel": { - "type": "string" - } - } - }, "updater.Status": { "type": "object", "properties": { diff --git a/Wavelet/docs/swagger.yaml b/Wavelet/docs/swagger.yaml index 18e854d6..5da9c0a4 100644 --- a/Wavelet/docs/swagger.yaml +++ b/Wavelet/docs/swagger.yaml @@ -367,190 +367,6 @@ definitions: token: type: string type: object - handler.batchDownloadRequest: - properties: - ids: - items: - type: string - minItems: 1 - type: array - required: - - ids - type: object - handler.distributionItem: - properties: - count: - type: integer - name: - type: string - size: - type: integer - type: object - handler.fileStatsResponse: - properties: - categories: - items: - $ref: '#/definitions/handler.distributionItem' - type: array - total_count: - type: integer - total_size: - type: integer - trend: - items: - $ref: '#/definitions/handler.trendItem' - type: array - types: - items: - $ref: '#/definitions/handler.distributionItem' - type: array - type: object - handler.listFilesResponse: - properties: - items: - items: - $ref: '#/definitions/model.Upload' - type: array - page: - type: integer - page_size: - type: integer - total: - type: integer - type: object - handler.listMyFilesResponse: - properties: - items: - items: - $ref: '#/definitions/model.Upload' - type: array - page: - type: integer - page_size: - type: integer - total: - type: integer - type: object - handler.trendItem: - properties: - count: - type: integer - date: - type: string - size: - type: integer - type: object - handler.updateMyFileRequest: - properties: - access_mode: - enum: - - 0 - - 1 - type: integer - file_name: - maxLength: 255 - type: string - type: object - logger.LogEntry: - properties: - data: - description: 一行日志原文(含换行符) - type: string - index: - description: 全局递增序号 - type: integer - type: object - logs.accessLogItem: - properties: - created_at: - type: string - headers: - type: string - id: - example: "0" - type: string - ip: - type: string - latency: - type: integer - method: - type: string - nickname: - type: string - path: - type: string - status: - type: integer - user_agent: - type: string - user_id: - example: "0" - type: string - username: - type: string - type: object - logs.accessLogsResponse: - properties: - list: - items: - $ref: '#/definitions/logs.accessLogItem' - type: array - total: - type: integer - type: object - logs.browserItem: - properties: - browser: - type: string - count: - type: integer - type: object - logs.logsAnalyticsResponse: - properties: - browsers: - items: - $ref: '#/definitions/logs.browserItem' - type: array - top_users: - items: - $ref: '#/definitions/logs.topUserItem' - type: array - trend: - items: - $ref: '#/definitions/logs.trendItem' - type: array - type: object - logs.logsResponse: - properties: - has_more: - type: boolean - lines: - items: - $ref: '#/definitions/logger.LogEntry' - type: array - next_cursor: - description: 用于加载更早日志的 cursor - type: integer - type: object - logs.topUserItem: - properties: - count: - type: integer - nickname: - type: string - user_id: - example: "0" - type: string - username: - type: string - type: object - logs.trendItem: - properties: - count: - type: integer - date: - type: string - type: object model.AccessToken: properties: created_at: @@ -1084,96 +900,6 @@ definitions: updated_at: type: string type: object - model.Upload: - properties: - access_mode: - type: integer - created_at: - type: string - extension: - description: 文件后缀名 (不含点,如 png, pdf) - type: string - file_name: - description: '原始文件名 (例如: image.png)' - type: string - file_path: - description: 文件相对路径 / S3 Key - type: string - file_size: - description: 文件大小(字节) - type: integer - hash: - description: 文件哈希 (SHA-256/MD5,可用于排重) - type: string - id: - example: "0" - type: string - metadata: - allOf: - - $ref: '#/definitions/model.UploadMetadata' - description: 业务扩展元数据 - mime_type: - description: 媒体类型 (MIME, 如 image/png) - type: string - status: - allOf: - - $ref: '#/definitions/model.UploadStatus' - description: 状态 - type: - description: 业务标识类型 (如 avatar, doc, attachment) - type: string - updated_at: - type: string - user_id: - example: "0" - type: string - type: object - model.UploadMetadata: - properties: - bucket: - description: 存储桶名称 (适用于 S3 等) - type: string - client_ip: - description: 上传者 IP - type: string - duration: - description: 音视频时长 (s) - type: number - extra: - additionalProperties: {} - description: 其它任意业务自定义元数据 - type: object - height: - description: 图像/视频高度 (px) - type: integer - original_mime: - description: 原始 MIME 类型 - type: string - user_agent: - description: 上传者的 UA - type: string - width: - description: 图像/视频宽度 (px) - type: integer - type: object - model.UploadStatus: - enum: - - pending - - used - - deleted - type: string - x-enum-comments: - UploadStatusDeleted: 已删除 - UploadStatusPending: 待使用 - UploadStatusUsed: 已使用 - x-enum-descriptions: - - 待使用 - - 已使用 - - 已删除 - x-enum-varnames: - - UploadStatusPending - - UploadStatusUsed - - UploadStatusDeleted node.AgentReleaseInfo: properties: body: @@ -2810,60 +2536,6 @@ definitions: matched: type: boolean type: object - update.LatestReleaseView: - properties: - body: - type: string - channel: - type: string - current_version: - type: string - has_update: - type: boolean - html_url: - type: string - in_progress: - type: boolean - prerelease: - type: boolean - published_at: - type: string - tag_name: - type: string - upgrade_logs: - items: - $ref: '#/definitions/update.UpgradeLogRecord' - type: array - upgrade_status: - type: string - upgrade_supported: - type: boolean - type: object - update.StreamSnapshot: - properties: - in_progress: - type: boolean - upgrade_logs: - items: - $ref: '#/definitions/update.UpgradeLogRecord' - type: array - upgrade_status: - type: string - type: object - update.UpgradeLogRecord: - properties: - created_at: - type: string - level: - type: string - message: - type: string - type: object - update.upgradeRequest: - properties: - channel: - type: string - type: object updater.Status: properties: asset_name: @@ -3895,143 +3567,6 @@ paths: summary: 获取数据库所有表名 tags: - admin - /api/v1/admin/logs: - get: - description: 分页获取系统历史日志,cursor=0 获取最新日志,cursor>0 获取更早日志 - parameters: - - default: 0 - description: 日志游标,0=获取最新 - in: query - name: cursor - type: integer - - default: 200 - description: 每页条数 - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: 日志列表 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/logs.logsResponse' - type: object - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取系统日志 - tags: - - admin - /api/v1/admin/logs/access: - get: - description: 分页并按照用户、接口路径、时间范围等维度检索 ClickHouse 用户访问日志列表(需要管理员权限,ClickHouse 未启用时报错) - parameters: - - default: 1 - description: 页码 - in: query - name: page - type: integer - - default: 20 - description: 每页条数 - in: query - name: page_size - type: integer - - description: 用户名模糊搜索 - in: query - name: username - type: string - - description: 接口路径模糊搜索 - in: query - name: path - type: string - - description: 起始时间(RFC3339 或 YYYY-MM-DD HH:MM:SS) - in: query - name: start_time - type: string - - description: 结束时间(RFC3339 或 YYYY-MM-DD HH:MM:SS) - in: query - name: end_time - type: string - produces: - - application/json - responses: - "200": - description: 访问日志列表 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/logs.accessLogsResponse' - type: object - "400": - description: ClickHouse 未启用或参数错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取用户访问日志 - tags: - - admin - /api/v1/admin/logs/analytics: - get: - description: 聚合统计最近 7 天的每日访问趋势、浏览器分布以及前 10 名最活跃用户排行(需要管理员权限,ClickHouse 未启用时报错) - produces: - - application/json - responses: - "200": - description: 分析统计数据 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/logs.logsAnalyticsResponse' - type: object - "400": - description: ClickHouse 未启用 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取访问日志分析数据 - tags: - - admin - /api/v1/admin/logs/ws: - get: - description: 通过 WebSocket 实时推送系统日志,需要管理员权限 - responses: {} - summary: 系统日志实时推送 - tags: - - admin /api/v1/admin/push/channels: get: description: 返回系统配置的所有消息通道列表,需要管理员权限 @@ -5306,224 +4841,6 @@ paths: summary: 下载并应用应用更新 tags: - admin - /api/v1/admin/uploads: - get: - description: 分页获取系统上传的文件列表,支持文件名关键词、业务类型、扩展名、上传用户ID过滤 - parameters: - - description: 页码(默认 1) - in: query - name: page - type: integer - - description: 每页数量(默认 20,最大 100) - in: query - name: page_size - type: integer - - description: 文件名关键词(模糊匹配) - in: query - name: keyword - type: string - - description: 业务分类过滤 - in: query - name: type - type: string - - description: 扩展名过滤 - in: query - name: extension - type: string - - description: 上传用户 ID - format: int64 - in: query - name: user_id - type: integer - produces: - - application/json - responses: - "200": - description: 查询成功 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/handler.listFilesResponse' - type: object - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取文件列表 - tags: - - admin - /api/v1/admin/uploads/{id}: - delete: - description: 将文件状态置为 deleted(软删除),不会立即清理底层存储对象 - parameters: - - description: 文件 ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: 删除成功 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无权操作 - schema: - $ref: '#/definitions/response.Any' - "404": - description: 文件不存在 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 删除文件 - tags: - - admin - /api/v1/admin/uploads/download/{id}: - get: - description: 根据文件 ID 获取文件,以附件形式 (Attachment) 强制开启客户端浏览器下载 - parameters: - - description: 文件 ID - in: path - name: id - required: true - type: string - - description: 图片质量 (low, medium, high, origin),默认为 origin - in: query - name: quality - type: string - produces: - - application/octet-stream - responses: - "200": - description: 成功下载文件 - schema: - type: file - "400": - description: 参数错误 - schema: - $ref: '#/definitions/response.Any' - "404": - description: 文件不存在 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 服务内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 下载单文件 - tags: - - admin - /api/v1/admin/uploads/download/batch: - post: - consumes: - - application/json - description: 传入多个文件 ID,后台实时将其打包压缩为 ZIP 流并输出,自动处理文件名重复冲突 - parameters: - - description: 包含文件 ID 数组 of string 的请求体 - in: body - name: request - required: true - schema: - $ref: '#/definitions/handler.batchDownloadRequest' - produces: - - application/octet-stream - responses: - "200": - description: 成功下载打包后的 ZIP - schema: - type: file - "400": - description: 参数错误 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 打包失败 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 批量打包下载 - tags: - - admin - /api/v1/admin/uploads/stats: - get: - description: 返回系统级的总文件数、占用大小、最近 7 天新增趋势、文件类型/格式分布等数据 - produces: - - application/json - responses: - "200": - description: 获取成功 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/handler.fileStatsResponse' - type: object - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取文件统计数据 - tags: - - admin - /api/v1/admin/uploads/types: - get: - description: 返回数据库中所有已上传文件实际拥有的业务类型列表 - produces: - - application/json - responses: - "200": - description: 业务类型列表 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - items: - type: string - type: array - type: object - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取文件业务类型列表 - tags: - - admin /api/v1/admin/users: get: description: 分页返回用户列表,支持按用户 ID 和用户名筛选,需要管理员权限 @@ -9401,168 +8718,6 @@ paths: summary: 从文件导入 TLS 证书 tags: - openflare-tls - /api/v1/openflare/update/latest-release: - get: - description: 查询 OpenFlare 服务端最新 GitHub Release 及升级状态,需要管理员权限 - parameters: - - description: 发布渠道(stable/preview) - in: query - name: channel - type: string - produces: - - application/json - responses: - "200": - description: 最新发布信息 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/update.LatestReleaseView' - type: object - "400": - description: 参数错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取最新服务端发布版本 - tags: - - openflare-update - /api/v1/openflare/update/logs/ws: - get: - description: 通过 WebSocket 推送升级进度快照,需要管理员权限 - responses: - "101": - description: WebSocket 升级日志流 - schema: - $ref: '#/definitions/update.StreamSnapshot' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 流式获取服务端升级日志 - tags: - - openflare-update - /api/v1/openflare/update/manual-upgrade: - post: - consumes: - - application/json - description: 确认并执行手动上传的服务端升级(当前功能已禁用),需要管理员权限 - produces: - - application/json - responses: - "400": - description: 功能已禁用或参数错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 确认手动服务端升级 - tags: - - openflare-update - /api/v1/openflare/update/manual-upload: - post: - consumes: - - multipart/form-data - description: 上传服务端二进制以进行手动升级(当前功能已禁用),需要管理员权限 - parameters: - - description: 服务端二进制文件 - in: formData - name: binary - required: true - type: file - produces: - - application/json - responses: - "400": - description: 功能已禁用或参数错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 上传手动升级二进制 - tags: - - openflare-update - /api/v1/openflare/update/upgrade: - post: - consumes: - - application/json - description: 从最新 Release 调度 OpenFlare 服务端自动升级,需要管理员权限 - parameters: - - description: 升级参数 - in: body - name: request - schema: - $ref: '#/definitions/update.upgradeRequest' - produces: - - application/json - responses: - "200": - description: 升级任务已调度 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/update.LatestReleaseView' - type: object - "400": - description: 参数错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无管理员权限 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 触发服务端自动升级 - tags: - - openflare-update /api/v1/openflare/uptimekuma/sync: post: consumes: @@ -10291,169 +9446,6 @@ paths: summary: 替换站点 WAF 规则组 tags: - openflare-waf - /api/v1/upload: - post: - consumes: - - multipart/form-data - description: 支持各种类型的通用文件上传,支持自动文件类型检测、哈希计算与“秒传”去重 - parameters: - - description: 要上传的文件 - in: formData - name: file - required: true - type: file - - description: '业务分类 (例如: avatar, attachment, doc,默认为 generic)' - in: formData - name: type - type: string - - description: 额外的 JSON 格式元数据 - in: formData - name: metadata - type: string - produces: - - application/json - responses: - "200": - description: 上传成功 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/model.Upload' - type: object - "400": - description: 请求参数错误或文件受限 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 内部错误 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 上传文件 - tags: - - upload - /api/v1/upload/{id}: - delete: - description: 将当前用户本人的文件状态置为 deleted(软删除) - parameters: - - description: 文件 ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: 删除成功 - schema: - $ref: '#/definitions/response.Any' - "403": - description: 无权操作 - schema: - $ref: '#/definitions/response.Any' - "404": - description: 文件不存在 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 删除我的文件 - tags: - - upload - put: - consumes: - - application/json - description: 更新当前用户本人的文件名或访问权限模式 (AccessMode) - parameters: - - description: 文件 ID - in: path - name: id - required: true - type: string - - description: 更新字段 - in: body - name: request - required: true - schema: - $ref: '#/definitions/handler.updateMyFileRequest' - produces: - - application/json - responses: - "200": - description: 更新成功 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/model.Upload' - type: object - "403": - description: 无权操作 - schema: - $ref: '#/definitions/response.Any' - "404": - description: 文件不存在 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 更新我的文件信息 - tags: - - upload - /api/v1/upload/my: - get: - description: 分页获取当前登录用户上传的文件,支持文件名关键词、业务类型、扩展名过滤 - parameters: - - description: 页码(默认 1) - in: query - name: page - type: integer - - description: 每页数量(默认 20,最大 100) - in: query - name: page_size - type: integer - - description: 文件名关键词(模糊匹配) - in: query - name: keyword - type: string - - description: 业务分类过滤 - in: query - name: type - type: string - - description: 扩展名过滤 - in: query - name: extension - type: string - produces: - - application/json - responses: - "200": - description: 查询成功 - schema: - allOf: - - $ref: '#/definitions/response.Any' - - properties: - data: - $ref: '#/definitions/handler.listMyFilesResponse' - type: object - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - security: - - SessionCookie: [] - summary: 获取我的文件列表 - tags: - - upload /api/v1/user-info: get: description: 返回当前登录用户的基本信息及余额数据,需要登录。包括用户 ID、用户名、信任等级、各类余额信息等。 @@ -10810,45 +9802,6 @@ paths: summary: 发送邮箱验证码 tags: - user - /f/{id}: - get: - description: 根据文件 ID 获取并提供已上传的临时或正式文件,若配置了缓存则优先走本地缓存,否则从 S3 等后端存储读取并流式返回 - parameters: - - description: 文件 ID - in: path - name: id - required: true - type: string - - description: 图片质量 (low, medium, high, origin),默认为 origin - in: query - name: quality - type: string - produces: - - application/octet-stream - responses: - "200": - description: 成功获取文件内容 - schema: - type: file - "400": - description: 文件 ID 格式错误 - schema: - $ref: '#/definitions/response.Any' - "401": - description: 未登录 - schema: - $ref: '#/definitions/response.Any' - "404": - description: 文件未找到 - schema: - $ref: '#/definitions/response.Any' - "500": - description: 服务内部错误 - schema: - $ref: '#/definitions/response.Any' - summary: 获取已上传文件 - tags: - - upload /robots.txt: get: description: 根据系统配置决定是否允许搜索引擎检索,并返回相应的 robots.txt 文件内容 diff --git a/Wavelet/frontend/app/(main)/admin/settings/components/openflare-ops.tsx b/Wavelet/frontend/app/(main)/admin/settings/components/openflare-ops.tsx index 21834f2a..aac38819 100644 --- a/Wavelet/frontend/app/(main)/admin/settings/components/openflare-ops.tsx +++ b/Wavelet/frontend/app/(main)/admin/settings/components/openflare-ops.tsx @@ -27,9 +27,10 @@ import {Textarea} from "@/components/ui/textarea" import {ErrorInline} from "@/components/layout/error" import {LoadingStateWithBorder} from "@/components/layout/loading" import type {DatabaseCleanupTarget} from "@/lib/services/openflare" -import {NodeService, OptionService, StatusService, UpdateService, UptimeKumaService,} from "@/lib/services/openflare" +import {AdminStatusService} from "@/lib/services/admin" +import {NodeService, OptionService, StatusService, UptimeKumaService,} from "@/lib/services/openflare" import {VersionUpgradeDialog} from "@/app/(main)/components/version-upgrade-dialog" -import {openflareLatestReleaseQueryKey, openflarePublicStatusQueryKey,} from "@/lib/hooks/use-openflare-server-upgrade" +import {adminUpdateStatusQueryKey, openflarePublicStatusQueryKey,} from "@/lib/hooks/use-openflare-server-upgrade" import { agentOptionEntries, @@ -102,8 +103,8 @@ export function OpenFlareOpsSettings() { }) const releaseQuery = useQuery({ - queryKey: openflareLatestReleaseQueryKey("stable"), - queryFn: () => UpdateService.getLatestRelease("stable"), + queryKey: adminUpdateStatusQueryKey, + queryFn: () => AdminStatusService.getUpdateStatus(), }) useEffect(() => { @@ -609,7 +610,7 @@ export function OpenFlareOpsSettings() {
版本信息 - 检查 GitHub Release、在线升级或手动上传服务端二进制包。 + 检查上游 GitHub Release 并升级当前服务。
- ) : null} - +

{update?.latest_version || '未检查'}

{canUpgrade ? ( ) : null} @@ -162,240 +123,101 @@ export function VersionUpgradeDialog({ ) : null} - - - 在线升级 - {canUpgrade ? 手动升级 : null} - {release ? 升级日志 : null} - + {!isInitialLoading && !releaseErrorMessage && !update ? ( +
+ 尚未检查更新,点击「检查更新」后展示 GitHub Release 信息。 +
+ ) : null} - - {!isInitialLoading && !releaseErrorMessage && !release ? ( -
- 尚未检查{selectedChannelLabel},点击「检查{selectedChannelLabel}」后展示 GitHub Release 信息。 + {update ? ( + + + GitHub Release · {update.latest_version} + + {update.published_at + ? `发布时间:${formatRelativeTime(update.published_at)} · ${formatDateTime(update.published_at)}` + : '未提供发布时间'} + + + +
+ + {update.update_available ? '发现新版本' : '已经是最新版本'} + + {update.prerelease ? ( + Preview 发布 + ) : ( + 正式发布 + )} + {!update.can_upgrade ? ( + 当前平台不支持自动升级 + ) : null}
- ) : null} - {release ? ( - - - - GitHub {selectedChannelLabel} · {release.tag_name} - - - {release.published_at - ? `发布时间:${formatRelativeTime(release.published_at)} · ${formatDateTime(release.published_at)}` - : '未提供发布时间'} - - - -
- - {release.has_update ? '发现新版本' : '已经是最新版本'} - - {release.prerelease ? ( - Preview 发布 - ) : ( - 正式发布 - )} - {!release.upgrade_supported ? ( - 当前平台不支持自动升级 - ) : null} - {release.in_progress ? ( - 升级任务执行中 - ) : null} -
+
+ + {update.release_notes || '暂无更新说明'} + +
-
- - {release.body || '暂无更新说明'} - -
+ {update.release_url ? ( + + 查看发布详情 + + + ) : null} - {release.html_url ? ( - - 查看发布详情 - - - ) : null} - - {canUpgrade ? ( -
+ {canUpgrade ? ( +
+ + -
- ) : null} - - - ) : null} - + + + + 升级到 {update.latest_version}? + + 服务将下载并校验 {update.asset_name},随后替换当前二进制并重启。请确保安装目录可写,且服务允许原地重启。 + + + + 取消 + 确认升级 + + + +
+ ) : null} - {canUpgrade ? ( - - - - 手动升级 - - 支持上传已编译好的当前平台 Server 可执行文件。 - - - -
- - { - setSelectedBinary(event.target.files?.[0] ?? null); - }} - /> -
- -
- {canConfirmManualUpgrade ? ( - - ) : ( - - )} -
- - {isUploadingBinary ? ( -
-
- {uploadPhaseLabel} - {uploadProgress}% -
- -
- ) : null} - - {manualErrorMessage ? ( -
- {manualErrorMessage} -
- ) : null} - - {!manualErrorMessage && manualStatusMessage ? ( -
- {manualStatusMessage} -
- ) : null} - - {uploadedBinary ? ( -
-
- - {uploadedBinary.ready_to_upgrade - ? '可确认升级' - : uploadedBinary.has_update - ? '待确认' - : '不可升级'} - - {!uploadedBinary.upgrade_supported ? ( - 当前版本不支持手动升级 - ) : null} -
- -
- - - - -
- -

- {uploadedBinary.comparison_message} -

-
- ) : ( -
- 尚未上传升级包 -
- )} -
-
-
- ) : null} - - {release ? ( - - - - ) : null} - + {!update.can_upgrade ? ( +

+ {update.current_version === 'dev' + ? '开发构建没有可比较的 Release 版本,不能执行自动升级。' + : update.update_available + ? '当前平台暂不支持自动替换二进制,请从 Release 页面手动升级。' + : '当前版本无需升级。'} +

+ ) : null} +
+
+ ) : null}
); -} - -function InfoCell({label, value}: {label: string; value: string}) { - return ( -
-

{label}

-

{value}

-
- ); -} - -export type {ReleaseChannel}; +} \ No newline at end of file diff --git a/Wavelet/frontend/lib/hooks/use-openflare-server-upgrade.ts b/Wavelet/frontend/lib/hooks/use-openflare-server-upgrade.ts index f49df76d..46f2e8bc 100644 --- a/Wavelet/frontend/lib/hooks/use-openflare-server-upgrade.ts +++ b/Wavelet/frontend/lib/hooks/use-openflare-server-upgrade.ts @@ -3,36 +3,13 @@ import {useMutation, useQuery} from '@tanstack/react-query'; import {useCallback, useEffect, useRef, useState} from 'react'; -import type { - LatestReleaseInfo, - ReleaseChannel, - UpgradeStreamSnapshot, - UploadedServerBinaryInfo, -} from '@/lib/services/openflare'; -import {StatusService, UpdateService} from '@/lib/services/openflare'; - -const UPGRADE_STATUS_POLL_INTERVAL = 3000; +import {AdminStatusService} from '@/lib/services/admin'; +import type {AppUpdateStatus} from '@/lib/services/admin/types'; +import {StatusService} from '@/lib/services/openflare'; export const openflarePublicStatusQueryKey = ['openflare', 'public-status'] as const; -export const openflareLatestReleaseQueryKey = (channel: ReleaseChannel = 'stable') => - ['openflare', 'latest-release', channel] as const; - -export function mergeReleaseWithUpgradeStream( - release: LatestReleaseInfo | null | undefined, - stream: UpgradeStreamSnapshot | null, -) { - if (!release || !stream) { - return release; - } - - return { - ...release, - in_progress: stream.in_progress, - upgrade_status: stream.upgrade_status, - upgrade_logs: stream.upgrade_logs, - }; -} +export const adminUpdateStatusQueryKey = ['admin', 'update'] as const; export function useOpenFlareServerUpgrade({ open, @@ -41,15 +18,8 @@ export function useOpenFlareServerUpgrade({ open: boolean; canUpgrade: boolean; }) { - const [selectedChannel, setSelectedChannel] = useState('stable'); const [feedback, setFeedback] = useState(null); - const [manualStatusMessage, setManualStatusMessage] = useState(null); - const [manualErrorMessage, setManualErrorMessage] = useState(null); - const [uploadedBinary, setUploadedBinary] = useState(null); - const [uploadProgress, setUploadProgress] = useState(0); - const [upgradeStream, setUpgradeStream] = useState(null); - const upgradeRefreshPendingRef = useRef(false); const upgradeReloadStartedRef = useRef(false); const upgradeReloadTimerRef = useRef(null); @@ -59,30 +29,11 @@ export function useOpenFlareServerUpgrade({ enabled: open, }); - const stableReleaseQuery = useQuery({ - queryKey: openflareLatestReleaseQueryKey('stable'), - queryFn: () => UpdateService.getLatestRelease('stable'), + const updateQuery = useQuery({ + queryKey: adminUpdateStatusQueryKey, + queryFn: () => AdminStatusService.getUpdateStatus(), enabled: open && canUpgrade, - refetchInterval: (query) => { - const release = query.state.data; - if (open && release?.in_progress) { - return UPGRADE_STATUS_POLL_INTERVAL; - } - return 60 * 60 * 1000; - }, - }); - - const previewReleaseQuery = useQuery({ - queryKey: openflareLatestReleaseQueryKey('preview'), - queryFn: () => UpdateService.getLatestRelease('preview'), - enabled: false, - refetchInterval: (query) => { - const release = query.state.data; - if (open && release?.in_progress) { - return UPGRADE_STATUS_POLL_INTERVAL; - } - return false; - }, + staleTime: 5 * 60 * 1000, }); const scheduleUpgradePageReload = useCallback(() => { @@ -113,236 +64,64 @@ export function useOpenFlareServerUpgrade({ }; }, []); - useEffect(() => { - if (!open || !canUpgrade) { - setUpgradeStream(null); - return; - } - - let closed = false; - let reconnectTimer: number | null = null; - let socket: WebSocket | null = null; - - const connect = () => { - if (closed) { - return; - } - - socket = UpdateService.createUpgradeLogsWebSocket(); - if (!socket) { - return; - } - - socket.onmessage = (event) => { - const snapshot = UpdateService.parseUpgradeStreamSnapshot(String(event.data)); - if (snapshot) { - if (snapshot.in_progress || snapshot.upgrade_status === 'succeeded') { - upgradeRefreshPendingRef.current = true; - } - if (snapshot.upgrade_status === 'failed') { - upgradeRefreshPendingRef.current = false; - } - setUpgradeStream(snapshot); - } - }; - - socket.onclose = () => { - if (closed) { - return; - } - if (upgradeRefreshPendingRef.current) { - scheduleUpgradePageReload(); - return; - } - reconnectTimer = window.setTimeout(connect, 1500); - }; - }; - - connect(); - - return () => { - closed = true; - if (reconnectTimer !== null) { - window.clearTimeout(reconnectTimer); - } - socket?.close(); - }; - }, [canUpgrade, open, scheduleUpgradePageReload]); - const upgradeMutation = useMutation({ - mutationFn: (channel: ReleaseChannel) => UpdateService.upgradeServer(channel), - onSuccess: (release) => { - upgradeRefreshPendingRef.current = true; - setUploadedBinary(null); - setManualStatusMessage(null); - setManualErrorMessage(null); - setFeedback( - `服务升级任务已启动,目标版本 ${release.tag_name}(${release.channel === 'preview' ? '预览版' : '正式版'})。页面可能短暂不可用。`, - ); - void stableReleaseQuery.refetch(); - if (release.channel === 'preview') { - void previewReleaseQuery.refetch(); - } + mutationFn: () => AdminStatusService.applyUpdate(), + onSuccess: () => { + scheduleUpgradePageReload(); + setFeedback('升级包已校验完成,服务正在重启。'); }, onError: (error) => { setFeedback(error instanceof Error ? error.message : '升级失败,请稍后重试。'); }, }); - const uploadBinaryMutation = useMutation({ - mutationFn: (binary: File) => - UpdateService.uploadServerBinary(binary, (progress) => { - setUploadProgress(progress); - }), - onSuccess: (candidate) => { - setUploadProgress(0); - setFeedback(null); - setManualErrorMessage(null); - setUploadedBinary(candidate); - setManualStatusMessage(candidate.comparison_message); - }, - onError: (error) => { - setUploadProgress(0); - setUploadedBinary(null); - setManualStatusMessage(null); - setManualErrorMessage( - error instanceof Error ? error.message : '上传升级包失败,请稍后重试。', - ); - }, - }); - - const confirmManualUpgradeMutation = useMutation({ - mutationFn: (uploadToken: string) => UpdateService.confirmManualServerUpgrade(uploadToken), - onSuccess: (candidate) => { - upgradeRefreshPendingRef.current = true; - setFeedback(null); - setManualErrorMessage(null); - setUploadedBinary(candidate); - setManualStatusMessage( - `手动升级任务已启动,目标版本 ${candidate.detected_version}。页面可能短暂不可用。`, - ); - void stableReleaseQuery.refetch(); - void previewReleaseQuery.refetch(); - }, - onError: (error) => { - setManualStatusMessage(null); - setManualErrorMessage( - error instanceof Error ? error.message : '确认手动升级失败,请稍后重试。', - ); - }, - }); - const resetTransientState = useCallback(() => { - setSelectedChannel('stable'); setFeedback(null); - setManualStatusMessage(null); - setManualErrorMessage(null); - setUploadedBinary(null); - setUploadProgress(0); - setUpgradeStream(null); - upgradeRefreshPendingRef.current = false; upgradeReloadStartedRef.current = false; }, []); const handleOpen = useCallback(() => { resetTransientState(); if (canUpgrade) { - void stableReleaseQuery.refetch(); + void updateQuery.refetch(); } - }, [canUpgrade, resetTransientState, stableReleaseQuery]); + }, [canUpgrade, resetTransientState, updateQuery]); const handleCheckRelease = useCallback(() => { setFeedback(null); if (!canUpgrade) { return; } - if (selectedChannel === 'preview') { - void previewReleaseQuery.refetch(); - return; - } - void stableReleaseQuery.refetch(); - }, [canUpgrade, previewReleaseQuery, selectedChannel, stableReleaseQuery]); - - const handleChannelChange = useCallback((channel: ReleaseChannel) => { - setSelectedChannel(channel); - setFeedback(null); - }, []); + void updateQuery.refetch(); + }, [canUpgrade, updateQuery]); const handleUpgrade = useCallback(() => { setFeedback(null); - setManualStatusMessage(null); - setManualErrorMessage(null); - upgradeMutation.mutate(selectedChannel); - }, [selectedChannel, upgradeMutation]); - - const handleUploadBinary = useCallback( - (binary: File) => { - setUploadProgress(0); - setManualStatusMessage(null); - setManualErrorMessage(null); - uploadBinaryMutation.mutate(binary); - }, - [uploadBinaryMutation], - ); - - const handleConfirmManualUpgrade = useCallback(() => { - if (!uploadedBinary?.upload_token) { - setManualStatusMessage(null); - setManualErrorMessage('请先上传并检查升级包。'); - return; - } - setFeedback(null); - setManualStatusMessage(null); - setManualErrorMessage(null); - confirmManualUpgradeMutation.mutate(uploadedBinary.upload_token); - }, [confirmManualUpgradeMutation, uploadedBinary?.upload_token]); - - const selectedRelease = - selectedChannel === 'preview' ? previewReleaseQuery.data : stableReleaseQuery.data; - const releaseWithStream = mergeReleaseWithUpgradeStream(selectedRelease, upgradeStream); - const selectedReleaseError = - selectedChannel === 'preview' ? previewReleaseQuery.error : stableReleaseQuery.error; - const isSelectedReleaseError = - selectedChannel === 'preview' ? previewReleaseQuery.isError : stableReleaseQuery.isError; - const isChecking = - selectedChannel === 'preview' - ? previewReleaseQuery.isFetching - : stableReleaseQuery.isFetching; - const isInitialLoading = - (selectedChannel === 'preview' - ? previewReleaseQuery.isLoading && !previewReleaseQuery.data - : stableReleaseQuery.isLoading && !stableReleaseQuery.data) && canUpgrade; + upgradeMutation.mutate(); + }, [upgradeMutation]); + const update = updateQuery.data; const releaseErrorMessage = feedback || - (isSelectedReleaseError - ? selectedReleaseError instanceof Error - ? selectedReleaseError.message + (updateQuery.isError + ? updateQuery.error instanceof Error + ? updateQuery.error.message : '版本检查失败,请稍后重试。' : undefined); - const currentVersion = statusQuery.data?.version || releaseWithStream?.current_version || 'unknown'; + const currentVersion = statusQuery.data?.version || update?.current_version || 'unknown'; return { currentVersion, - selectedChannel, - release: releaseWithStream, - uploadedBinary, + update, releaseErrorMessage, - manualStatusMessage, - manualErrorMessage, - isInitialLoading, - isChecking, + isInitialLoading: updateQuery.isLoading && !updateQuery.data && canUpgrade, + isChecking: updateQuery.isFetching, isUpgrading: upgradeMutation.isPending, - isUploadingBinary: uploadBinaryMutation.isPending, - uploadProgress, - isConfirmingManualUpgrade: confirmManualUpgradeMutation.isPending, - stableRelease: stableReleaseQuery.data, handleOpen, handleCheckRelease, - handleChannelChange, handleUpgrade, - handleUploadBinary, - handleConfirmManualUpgrade, }; -} \ No newline at end of file +} + +export type {AppUpdateStatus}; \ No newline at end of file diff --git a/Wavelet/frontend/lib/services/index.ts b/Wavelet/frontend/lib/services/index.ts index 1bc67f3e..b4ab4ff2 100644 --- a/Wavelet/frontend/lib/services/index.ts +++ b/Wavelet/frontend/lib/services/index.ts @@ -40,7 +40,6 @@ import { ProxyRouteService, StatusService, TlsCertificateService, - UpdateService, UptimeKumaService, WafService, WebsiteService, @@ -77,7 +76,6 @@ const services = { openflareAccessLog: AccessLogService, openflareOption: OptionService, openflareUptimeKuma: UptimeKumaService, - openflareUpdate: UpdateService, openflareStatus: StatusService, } as const; @@ -199,7 +197,6 @@ export { AccessLogService, OptionService, StatusService, - UpdateService, UptimeKumaService, } from './openflare'; @@ -227,6 +224,5 @@ export type { OptionItem, GeoIPLookupResult, DatabaseCleanupResult, - LatestReleaseInfo, OpenFlarePublicStatus, } from './openflare'; diff --git a/Wavelet/frontend/lib/services/openflare/index.ts b/Wavelet/frontend/lib/services/openflare/index.ts index 71e8bef5..79d03ff6 100644 --- a/Wavelet/frontend/lib/services/openflare/index.ts +++ b/Wavelet/frontend/lib/services/openflare/index.ts @@ -15,7 +15,6 @@ export { OriginService } from './origin.service'; export { AccessLogService } from './access-log.service'; export { OptionService } from './option.service'; export { UptimeKumaService } from './uptimekuma.service'; -export { UpdateService } from './update.service'; export { StatusService } from './status.service'; export type { @@ -93,11 +92,7 @@ export type { DatabaseCleanupPayload, DatabaseCleanupResult, DatabaseCleanupTarget, - LatestReleaseInfo, OpenFlarePublicStatus, - UpgradeLogItem, - UpgradeStreamSnapshot, - UploadedServerBinaryInfo, OriginDetail, OriginItem, OriginMutationPayload, @@ -128,7 +123,6 @@ import {DnsAccountService} from './dns-account.service'; import {NodeService} from './node.service'; import {OptionService} from './option.service'; import {StatusService} from './status.service'; -import {UpdateService} from './update.service'; import {UptimeKumaService} from './uptimekuma.service'; import {OriginService} from './origin.service'; import {PagesService} from './pages.service'; @@ -153,6 +147,5 @@ export const openflareServices = { accessLog: AccessLogService, option: OptionService, uptimeKuma: UptimeKumaService, - update: UpdateService, status: StatusService, } as const; diff --git a/Wavelet/frontend/lib/services/openflare/types.ts b/Wavelet/frontend/lib/services/openflare/types.ts index b287fc09..fbd87362 100644 --- a/Wavelet/frontend/lib/services/openflare/types.ts +++ b/Wavelet/frontend/lib/services/openflare/types.ts @@ -696,45 +696,6 @@ export interface OpenFlarePublicStatus { system_name: string; } -export interface UpgradeLogItem { - level: string; - message: string; - created_at: string; -} - -export interface LatestReleaseInfo { - tag_name: string; - body: string; - html_url: string; - published_at: string; - channel: ReleaseChannel; - prerelease: boolean; - current_version: string; - has_update: boolean; - upgrade_supported: boolean; - in_progress: boolean; - upgrade_status: 'idle' | 'running' | 'succeeded' | 'failed' | string; - upgrade_logs: UpgradeLogItem[]; -} - -export interface UpgradeStreamSnapshot { - in_progress: boolean; - upgrade_status: 'idle' | 'running' | 'succeeded' | 'failed' | string; - upgrade_logs: UpgradeLogItem[]; -} - -export interface UploadedServerBinaryInfo { - upload_token: string; - file_name: string; - detected_version: string; - current_version: string; - has_update: boolean; - upgrade_supported: boolean; - ready_to_upgrade: boolean; - comparison_message: string; - uploaded_at: string; -} - export interface WAFRuleGroup { id: number; name: string; diff --git a/Wavelet/frontend/lib/services/openflare/update.service.ts b/Wavelet/frontend/lib/services/openflare/update.service.ts deleted file mode 100644 index 4e2c6941..00000000 --- a/Wavelet/frontend/lib/services/openflare/update.service.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type {InternalAxiosRequestConfig} from 'axios'; - -import {getApiBaseUrl} from '@/lib/services/core/config'; -import {ApiErrorBase} from '@/lib/services/core/errors'; -import type {ApiResponse} from '@/lib/services/core'; - -import {OpenFlareBaseService} from './base.service'; -import type {LatestReleaseInfo, ReleaseChannel, UpgradeStreamSnapshot, UploadedServerBinaryInfo} from './types'; - -/** - * OpenFlare 服务端升级 API(`/api/v1/openflare/update/*`)。 - */ -export class UpdateService extends OpenFlareBaseService { - protected static override readonly basePath: string = '/api/v1/openflare/update'; - - private static getApiUrl(path: string): string { - const baseURL = getApiBaseUrl(); - const fullPath = this.getFullPath(path); - if (!baseURL) { - return fullPath; - } - return `${baseURL.replace(/\/$/, '')}${fullPath}`; - } - - static getLatestRelease(channel: ReleaseChannel = 'stable'): Promise { - return this.get('/latest-release', { channel }); - } - - static upgradeServer(channel: ReleaseChannel = 'stable'): Promise { - return this.post('/upgrade', { channel }); - } - - static uploadServerBinary( - binary: File, - onProgress?: (progress: number) => void, - ): Promise { - const formData = new FormData(); - formData.append('binary', binary); - - if (!onProgress) { - return this.post('/manual-upload', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - } as InternalAxiosRequestConfig); - } - - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.open('POST', this.getApiUrl('/manual-upload')); - xhr.withCredentials = true; - - xhr.upload.addEventListener('progress', (event) => { - if (event.lengthComputable) { - onProgress(Math.round((event.loaded / event.total) * 100)); - } - }); - - xhr.addEventListener('load', () => { - let payload: ApiResponse | null = null; - try { - payload = JSON.parse( - xhr.responseText, - ) as ApiResponse; - } catch { - payload = null; - } - if (xhr.status < 200 || xhr.status >= 300) { - reject( - new ApiErrorBase( - payload?.message || `请求失败(${xhr.status})`, - undefined, - xhr.status, - ), - ); - return; - } - if (!payload) { - reject(new ApiErrorBase('响应格式无效', undefined, xhr.status)); - return; - } - resolve(payload.data); - }); - - xhr.addEventListener('error', () => { - reject(new ApiErrorBase('上传过程中网络连接中断,请检查网络后重试', undefined, 0)); - }); - - xhr.send(formData); - }); - } - - static confirmManualServerUpgrade(uploadToken: string): Promise { - return this.post('/manual-upgrade', { - upload_token: uploadToken, - }); - } - - static createUpgradeLogsWebSocket(): WebSocket | null { - if (typeof window === 'undefined') { - return null; - } - - const apiUrl = this.getApiUrl('/logs/ws'); - const resolvedUrl = apiUrl.startsWith('http://') - ? `ws://${apiUrl.slice('http://'.length)}` - : apiUrl.startsWith('https://') - ? `wss://${apiUrl.slice('https://'.length)}` - : `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}${apiUrl}`; - - return new WebSocket(resolvedUrl); - } - - static parseUpgradeStreamSnapshot(rawMessage: string): UpgradeStreamSnapshot | null { - try { - const parsed = JSON.parse(rawMessage) as Partial; - if ( - typeof parsed.in_progress !== 'boolean' || - typeof parsed.upgrade_status !== 'string' || - !Array.isArray(parsed.upgrade_logs) - ) { - return null; - } - return { - in_progress: parsed.in_progress, - upgrade_status: parsed.upgrade_status, - upgrade_logs: parsed.upgrade_logs, - }; - } catch { - return null; - } - } -} \ No newline at end of file diff --git a/Wavelet/internal/apps/openflare/update/logics.go b/Wavelet/internal/apps/openflare/update/logics.go deleted file mode 100644 index 7eb1d4c8..00000000 --- a/Wavelet/internal/apps/openflare/update/logics.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2026 Arctel.net -// SPDX-License-Identifier: Apache-2.0 - -package update - -import ( - "context" - "errors" - "fmt" - "runtime" - "strings" - "sync" - "time" - - "github.com/Rain-kl/Wavelet/internal/apps/admin/updater" - "github.com/Rain-kl/Wavelet/pkg/logger" -) - -const ( - channelStable = "stable" - channelPreview = "preview" -) - -// UpgradeLogRecord is a single upgrade log entry for the legacy update API. -type UpgradeLogRecord struct { - Level string `json:"level"` - Message string `json:"message"` - CreatedAt time.Time `json:"created_at"` -} - -// LatestReleaseView mirrors the legacy OpenFlare latest-release payload. -type LatestReleaseView struct { - TagName string `json:"tag_name"` - Body string `json:"body"` - HTMLURL string `json:"html_url"` - PublishedAt string `json:"published_at"` - Channel string `json:"channel"` - Prerelease bool `json:"prerelease"` - CurrentVersion string `json:"current_version"` - HasUpdate bool `json:"has_update"` - UpgradeSupported bool `json:"upgrade_supported"` - InProgress bool `json:"in_progress"` - UpgradeStatus string `json:"upgrade_status"` - UpgradeLogs []UpgradeLogRecord `json:"upgrade_logs"` -} - -// StreamSnapshot is pushed over the upgrade logs websocket. -type StreamSnapshot struct { - InProgress bool `json:"in_progress"` - UpgradeStatus string `json:"upgrade_status"` - UpgradeLogs []UpgradeLogRecord `json:"upgrade_logs"` -} - -type upgradeRequest struct { - Channel string `json:"channel"` -} - -var upgradeState struct { - sync.Mutex - inProgress bool - status string - logs []UpgradeLogRecord -} - -var upgradeSubscribers struct { - sync.Mutex - nextID int - listeners map[int]chan StreamSnapshot -} - -func init() { - upgradeSubscribers.listeners = make(map[int]chan StreamSnapshot) - upgradeState.status = "idle" -} - -func normalizeChannel(channel string) string { - switch strings.ToLower(strings.TrimSpace(channel)) { - case channelPreview: - return channelPreview - default: - return channelStable - } -} - -func isDevBuild(version string) bool { - version = strings.TrimSpace(version) - return version == "" || strings.EqualFold(version, "dev") -} - -func mapStatusToLatestRelease(status updater.Status, channel string) *LatestReleaseView { - inProgress, upgradeStatus, logs := snapshotUpgradeState() - view := &LatestReleaseView{ - TagName: status.LatestVersion, - Body: status.ReleaseNotes, - HTMLURL: status.ReleaseURL, - PublishedAt: status.PublishedAt, - Channel: channel, - Prerelease: status.Prerelease, - CurrentVersion: status.CurrentVersion, - HasUpdate: status.UpdateAvailable, - UpgradeSupported: !isDevBuild(status.CurrentVersion) && runtime.GOOS != "windows", - InProgress: inProgress || updater.IsUpgrading(), - UpgradeStatus: upgradeStatus, - UpgradeLogs: logs, - } - if channel == channelPreview && status.Prerelease { - view.HasUpdate = !isDevBuild(status.CurrentVersion) - } - return view -} - -// GetLatestRelease returns the newest upstream release for the requested channel. -func GetLatestRelease(ctx context.Context, channel string) (*LatestReleaseView, error) { - normalizedChannel := normalizeChannel(channel) - status, err := updater.GetStatus(ctx) - if err != nil { - return nil, err - } - return mapStatusToLatestRelease(status, normalizedChannel), nil -} - -// ScheduleUpgrade downloads the latest release and restarts with the staged binary. -func ScheduleUpgrade(ctx context.Context, channel string) (*LatestReleaseView, error) { - normalizedChannel := normalizeChannel(channel) - - upgradeState.Lock() - if upgradeState.inProgress || updater.IsUpgrading() { - upgradeState.Unlock() - return nil, errors.New("服务升级正在执行中,请稍后再试") - } - resetUpgradeLogsLocked() - upgradeState.inProgress = true - upgradeState.status = "running" - appendUpgradeLogLocked("info", fmt.Sprintf("Automatic upgrade scheduled for channel: %s.", normalizedChannel)) - upgradeState.Unlock() - broadcastUpgradeSnapshot() - - executable, stagedBinary, status, err := updater.PrepareUpgrade(ctx) - if err != nil { - recordUpgradeFailure(err) - return nil, err - } - - view := mapStatusToLatestRelease(status, normalizedChannel) - view.InProgress = true - view.UpgradeStatus = "running" - view.UpgradeLogs = snapshotUpgradeLogs() - - appendUpgradeLogLocked("info", fmt.Sprintf("Upgrade package prepared: %s.", status.LatestVersion)) - broadcastUpgradeSnapshot() - - go func() { - time.Sleep(time.Second) - if err := updater.ApplyPreparedUpgrade(executable, stagedBinary); err != nil { - updater.FinishUpgrade() - recordUpgradeFailure(err) - logger.ErrorF(context.Background(), "[Update] replace and restart failed: %v", err) - } - }() - - return view, nil -} - -// UploadManualBinary is disabled in the legacy OpenFlare server. -func UploadManualBinary() error { - return errors.New("手动升级功能已禁用") -} - -// ConfirmManualUpgrade is disabled in the legacy OpenFlare server. -func ConfirmManualUpgrade() error { - return errors.New("手动升级功能已禁用") -} - -// SubscribeUpgradeStream registers a listener for upgrade websocket snapshots. -func SubscribeUpgradeStream() (<-chan StreamSnapshot, func()) { - upgradeSubscribers.Lock() - defer upgradeSubscribers.Unlock() - - id := upgradeSubscribers.nextID - upgradeSubscribers.nextID++ - ch := make(chan StreamSnapshot, 1) - upgradeSubscribers.listeners[id] = ch - - unsubscribe := func() { - upgradeSubscribers.Lock() - defer upgradeSubscribers.Unlock() - if listener, ok := upgradeSubscribers.listeners[id]; ok { - delete(upgradeSubscribers.listeners, id) - close(listener) - } - } - - select { - case ch <- currentUpgradeSnapshot(): - default: - } - - return ch, unsubscribe -} - -func snapshotUpgradeState() (bool, string, []UpgradeLogRecord) { - upgradeState.Lock() - defer upgradeState.Unlock() - return upgradeState.inProgress || updater.IsUpgrading(), upgradeState.status, cloneUpgradeLogsLocked() -} - -func snapshotUpgradeLogs() []UpgradeLogRecord { - upgradeState.Lock() - defer upgradeState.Unlock() - return cloneUpgradeLogsLocked() -} - -func currentUpgradeSnapshot() StreamSnapshot { - inProgress, status, logs := snapshotUpgradeState() - return StreamSnapshot{ - InProgress: inProgress, - UpgradeStatus: status, - UpgradeLogs: logs, - } -} - -func resetUpgradeLogsLocked() { - upgradeState.logs = nil -} - -func appendUpgradeLogLocked(level, message string) { - upgradeState.logs = append(upgradeState.logs, UpgradeLogRecord{ - Level: level, - Message: message, - CreatedAt: time.Now().UTC(), - }) -} - -func cloneUpgradeLogsLocked() []UpgradeLogRecord { - if len(upgradeState.logs) == 0 { - return []UpgradeLogRecord{} - } - cloned := make([]UpgradeLogRecord, len(upgradeState.logs)) - copy(cloned, upgradeState.logs) - return cloned -} - -func recordUpgradeFailure(err error) { - upgradeState.Lock() - upgradeState.inProgress = false - upgradeState.status = "failed" - appendUpgradeLogLocked("error", err.Error()) - upgradeState.Unlock() - broadcastUpgradeSnapshot() -} - -func broadcastUpgradeSnapshot() { - snapshot := currentUpgradeSnapshot() - upgradeSubscribers.Lock() - defer upgradeSubscribers.Unlock() - for _, listener := range upgradeSubscribers.listeners { - select { - case listener <- snapshot: - default: - } - } -} diff --git a/Wavelet/internal/apps/openflare/update/routers.go b/Wavelet/internal/apps/openflare/update/routers.go deleted file mode 100644 index 84e4dab6..00000000 --- a/Wavelet/internal/apps/openflare/update/routers.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2026 Arctel.net -// SPDX-License-Identifier: Apache-2.0 - -package update - -import ( - "encoding/json" - "errors" - "io" - "net/http" - "time" - - "github.com/Rain-kl/Wavelet/internal/apps/openflare/apiutil" - "github.com/Rain-kl/Wavelet/internal/common/response" - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" -) - -var upgradeLogsUpgrader = websocket.Upgrader{ - CheckOrigin: func(_ *http.Request) bool { return true }, -} - - -// GetLatestReleaseHandler 获取最新 GitHub 发布版本。 -// @Summary 获取最新服务端发布版本 -// @Description 查询 OpenFlare 服务端最新 GitHub Release 及升级状态,需要管理员权限 -// @Tags openflare-update -// @Produce json -// @Security SessionCookie -// @Param channel query string false "发布渠道(stable/preview)" -// @Success 200 {object} response.Any{data=update.LatestReleaseView} "最新发布信息" -// @Failure 400 {object} response.Any "参数错误" -// @Failure 401 {object} response.Any "未登录" -// @Failure 403 {object} response.Any "无管理员权限" -// @Failure 500 {object} response.Any "内部错误" -// @Router /api/v1/openflare/update/latest-release [get] -func GetLatestReleaseHandler(c *gin.Context) { - release, err := GetLatestRelease(c.Request.Context(), c.Query("channel")) - if apiutil.AbortBadRequestOnError(c, err) { - return - } - c.JSON(http.StatusOK, response.OK(release)) -} - -// UpgradeServerHandler 调度自动升级任务。 -// @Summary 触发服务端自动升级 -// @Description 从最新 Release 调度 OpenFlare 服务端自动升级,需要管理员权限 -// @Tags openflare-update -// @Accept json -// @Produce json -// @Security SessionCookie -// @Param request body update.upgradeRequest false "升级参数" -// @Success 200 {object} response.Any{data=update.LatestReleaseView} "升级任务已调度" -// @Failure 400 {object} response.Any "参数错误" -// @Failure 401 {object} response.Any "未登录" -// @Failure 403 {object} response.Any "无管理员权限" -// @Failure 500 {object} response.Any "内部错误" -// @Router /api/v1/openflare/update/upgrade [post] -func UpgradeServerHandler(c *gin.Context) { - var request upgradeRequest - if err := bindOptionalJSON(c.Request.Body, &request); err != nil { - response.AbortBadRequest(c, "无效的参数") - return - } - - release, err := ScheduleUpgrade(c.Request.Context(), request.Channel) - if apiutil.AbortBadRequestOnError(c, err) { - return - } - - c.JSON(http.StatusOK, response.OK(release)) -} - -// UploadManualServerBinaryHandler 上传手动升级二进制(已禁用)。 -// @Summary 上传手动升级二进制 -// @Description 上传服务端二进制以进行手动升级(当前功能已禁用),需要管理员权限 -// @Tags openflare-update -// @Accept multipart/form-data -// @Produce json -// @Security SessionCookie -// @Param binary formData file true "服务端二进制文件" -// @Failure 400 {object} response.Any "功能已禁用或参数错误" -// @Failure 401 {object} response.Any "未登录" -// @Failure 403 {object} response.Any "无管理员权限" -// @Router /api/v1/openflare/update/manual-upload [post] -func UploadManualServerBinaryHandler(c *gin.Context) { - if apiutil.AbortBadRequestOnError(c, UploadManualBinary()) { - return - } -} - -// ConfirmManualServerUpgradeHandler 确认手动升级(已禁用)。 -// @Summary 确认手动服务端升级 -// @Description 确认并执行手动上传的服务端升级(当前功能已禁用),需要管理员权限 -// @Tags openflare-update -// @Accept json -// @Produce json -// @Security SessionCookie -// @Failure 400 {object} response.Any "功能已禁用或参数错误" -// @Failure 401 {object} response.Any "未登录" -// @Failure 403 {object} response.Any "无管理员权限" -// @Router /api/v1/openflare/update/manual-upgrade [post] -func ConfirmManualServerUpgradeHandler(c *gin.Context) { - if apiutil.AbortBadRequestOnError(c, ConfirmManualUpgrade()) { - return - } -} - -// StreamServerUpgradeLogsHandler 通过 WebSocket 推送升级日志。 -// @Summary 流式获取服务端升级日志 -// @Description 通过 WebSocket 推送升级进度快照,需要管理员权限 -// @Tags openflare-update -// @Security SessionCookie -// @Success 101 {object} update.StreamSnapshot "WebSocket 升级日志流" -// @Failure 401 {object} response.Any "未登录" -// @Failure 403 {object} response.Any "无管理员权限" -// @Router /api/v1/openflare/update/logs/ws [get] -func StreamServerUpgradeLogsHandler(c *gin.Context) { - conn, err := upgradeLogsUpgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - return - } - defer func() { - _ = conn.Close() - }() - - updates, unsubscribe := SubscribeUpgradeStream() - defer unsubscribe() - - heartbeatTicker := time.NewTicker(15 * time.Second) - defer heartbeatTicker.Stop() - - for { - select { - case snapshot, ok := <-updates: - if !ok { - return - } - if err := conn.WriteJSON(snapshot); err != nil { - return - } - case <-heartbeatTicker.C: - if err := conn.WriteJSON(StreamSnapshot{}); err != nil { - return - } - case <-c.Request.Context().Done(): - return - } - } -} - -func bindOptionalJSON(body io.Reader, target any) error { - if err := json.NewDecoder(body).Decode(target); err != nil && !errors.Is(err, io.EOF) { - return err - } - return nil -} \ No newline at end of file diff --git a/Wavelet/internal/router/v1/openflare/register_update.go b/Wavelet/internal/router/v1/openflare/register_update.go deleted file mode 100644 index a5370d66..00000000 --- a/Wavelet/internal/router/v1/openflare/register_update.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2026 Arctel.net -// SPDX-License-Identifier: Apache-2.0 - -package openflare - -import ( - "github.com/Rain-kl/Wavelet/internal/apps/openflare/apiutil" - "github.com/Rain-kl/Wavelet/internal/apps/openflare/update" - "github.com/gin-gonic/gin" -) - -func registerUpdateRoutes(apiGroup *gin.RouterGroup) { - updateRoute := apiGroup.Group("/update") - updateRoute.Use(apiutil.AdminRequired()) - { - updateRoute.GET("/latest-release", update.GetLatestReleaseHandler) - updateRoute.GET("/logs/ws", update.StreamServerUpgradeLogsHandler) - updateRoute.POST("/manual-upload", update.UploadManualServerBinaryHandler) - updateRoute.POST("/manual-upgrade", update.ConfirmManualServerUpgradeHandler) - updateRoute.POST("/upgrade", update.UpgradeServerHandler) - } -} diff --git a/Wavelet/internal/router/v1/openflare/v1.go b/Wavelet/internal/router/v1/openflare/v1.go index 8654d3e5..05c33578 100644 --- a/Wavelet/internal/router/v1/openflare/v1.go +++ b/Wavelet/internal/router/v1/openflare/v1.go @@ -22,5 +22,4 @@ func RegisterV1Routes(apiV1Router *gin.RouterGroup) { registerPagesRoutes(group) registerDashboardRoutes(group) registerObservabilityRoutes(group) - registerUpdateRoutes(group) } diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 0ec70198..a7993f08 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -19,6 +19,7 @@ sidebar: false ### 移除 - 移除 Wavelet 中从旧系统迁移但未实装的全局 API / Web / 敏感接口限流选项(`GlobalApiRateLimit*`、`GlobalWebRateLimit*`、`CriticalRateLimit*`)及相关校验与数据库种子。 +- 移除旧 OpenFlare 服务升级兼容层(`/api/v1/openflare/update/*`);统一使用 Wavelet 框架 `admin/updater`(`/api/v1/admin/update`)。 ### 新增