From 34c3251c6d7e48e18bd1151737e39905d22bb0f7 Mon Sep 17 00:00:00 2001 From: chensipeng Date: Fri, 15 May 2026 09:59:25 +0800 Subject: [PATCH] =?UTF-8?q?docs(api):=20=E8=A1=A5=E5=85=A8=20OpenAPI=20?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=B8=8E=E7=B3=BB=E7=BB=9F=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为文件上传、静态资源和文件存储设置接口补齐注释,并同步更新生成的 Swagger 文档。 --- apps/api/docs/swagger.json | 710 ++++++++++++++++++ apps/api/docs/swagger.yaml | 462 ++++++++++++ .../internal/httpapi/file_upload_handlers.go | 16 + apps/api/internal/httpapi/openapi_models.go | 13 + apps/api/internal/httpapi/static_assets.go | 18 + .../httpapi/system_settings_handlers.go | 81 ++ 6 files changed, 1300 insertions(+) diff --git a/apps/api/docs/swagger.json b/apps/api/docs/swagger.json index d132c95..653288e 100644 --- a/apps/api/docs/swagger.json +++ b/apps/api/docs/swagger.json @@ -2357,6 +2357,355 @@ } } }, + "/api/admin/system/file-storage/channels": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回所有未删除的文件存储通道,用于管理上传与生成资源回传策略。", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "列出文件存储通道", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httpapi.FileStorageChannelListResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建文件存储通道,当前主要用于配置 server-main OpenAPI 上传通道。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "创建文件存储通道", + "parameters": [ + { + "description": "文件存储通道", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/store.FileStorageChannelInput" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/store.FileStorageChannel" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + } + }, + "/api/admin/system/file-storage/channels/{channelID}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "软删除指定文件存储通道。", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "删除文件存储通道", + "parameters": [ + { + "type": "string", + "description": "文件存储通道 ID", + "name": "channelID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + }, + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新指定文件存储通道的名称、凭证、场景、优先级、状态和重试策略。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "更新文件存储通道", + "parameters": [ + { + "type": "string", + "description": "文件存储通道 ID", + "name": "channelID", + "in": "path", + "required": true + }, + { + "description": "文件存储通道", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/store.FileStorageChannelInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.FileStorageChannel" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + } + }, + "/api/admin/system/file-storage/settings": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回文件存储系统设置;数据库对象尚未创建时返回默认设置。", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "获取文件存储设置", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.FileStorageSettings" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + }, + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "更新生成资源上传策略等文件存储系统设置。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "更新文件存储设置", + "parameters": [ + { + "description": "文件存储设置", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/store.FileStorageSettingsInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/store.FileStorageSettings" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + } + }, "/api/admin/tenants": { "get": { "security": [ @@ -3737,6 +4086,74 @@ } } }, + "/api/v1/files/upload": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "上传文件到配置的文件存储通道;没有启用通道时回退到本地静态上传目录。单文件最大 256MiB。", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "上传文件", + "parameters": [ + { + "type": "file", + "description": "要上传的文件", + "name": "file", + "in": "formData", + "required": true + }, + { + "type": "string", + "default": "ai-gateway-openapi", + "description": "上传来源标识", + "name": "source", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httpapi.FileUploadResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "502": { + "description": "Bad Gateway", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + } + }, "/api/v1/images/edits": { "post": { "security": [ @@ -5446,6 +5863,41 @@ } } }, + "/static/generated/{asset}": { + "get": { + "description": "从本地生成资源目录读取图片、视频等任务产物;不存在时返回 404。", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "static" + ], + "summary": "获取本地生成资源", + "parameters": [ + { + "type": "string", + "description": "资源文件名", + "name": "asset", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + } + }, "/static/simulation/{asset}": { "get": { "description": "返回本地模拟模式使用的图片、视频封面或短视频资源。", @@ -5482,6 +5934,41 @@ } } }, + "/static/uploaded/{asset}": { + "get": { + "description": "从本地上传资源目录读取用户上传文件;不存在时返回 404。", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "static" + ], + "summary": "获取本地上传资源", + "parameters": [ + { + "type": "string", + "description": "资源文件名", + "name": "asset", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "string" + } + } + } + } + }, "/v1/chat/completions": { "post": { "security": [ @@ -5575,6 +6062,74 @@ } } }, + "/v1/files/upload": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "上传文件到配置的文件存储通道;没有启用通道时回退到本地静态上传目录。单文件最大 256MiB。", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "files" + ], + "summary": "上传文件", + "parameters": [ + { + "type": "file", + "description": "要上传的文件", + "name": "file", + "in": "formData", + "required": true + }, + { + "type": "string", + "default": "ai-gateway-openapi", + "description": "上传来源标识", + "name": "source", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httpapi.FileUploadResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "502": { + "description": "Bad Gateway", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/httpapi.ErrorEnvelope" + } + } + } + } + }, "/v1/images/edits": { "post": { "security": [ @@ -6062,6 +6617,46 @@ } } }, + "httpapi.FileStorageChannelListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/store.FileStorageChannel" + } + } + } + }, + "httpapi.FileUploadResponse": { + "type": "object", + "properties": { + "assetStorage": { + "type": "object", + "additionalProperties": true + }, + "contentType": { + "type": "string", + "example": "image/png" + }, + "filename": { + "type": "string", + "example": "image.png" + }, + "id": { + "type": "string", + "example": "file_abc123" + }, + "size": { + "type": "integer", + "example": 1024 + }, + "url": { + "type": "string", + "example": "/static/uploaded/upload-abc123.png" + } + } + }, "httpapi.HealthResponse": { "type": "object", "properties": { @@ -7407,6 +8002,121 @@ } } }, + "store.FileStorageChannel": { + "type": "object", + "properties": { + "channelKey": { + "type": "string" + }, + "config": { + "type": "object", + "additionalProperties": {} + }, + "createdAt": { + "type": "string" + }, + "credentialsPreview": { + "type": "object", + "additionalProperties": {} + }, + "id": { + "type": "string" + }, + "lastError": { + "type": "string" + }, + "lastFailedAt": { + "type": "string" + }, + "lastSucceededAt": { + "type": "string" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "integer" + }, + "provider": { + "type": "string" + }, + "retryPolicy": { + "type": "object", + "additionalProperties": {} + }, + "scenes": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "uploadUrl": { + "type": "string" + } + } + }, + "store.FileStorageChannelInput": { + "type": "object", + "properties": { + "apiKey": { + "type": "string" + }, + "channelKey": { + "type": "string" + }, + "config": { + "type": "object", + "additionalProperties": {} + }, + "name": { + "type": "string" + }, + "priority": { + "type": "integer" + }, + "provider": { + "type": "string" + }, + "retryPolicy": { + "type": "object", + "additionalProperties": {} + }, + "scenes": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "uploadUrl": { + "type": "string" + } + } + }, + "store.FileStorageSettings": { + "type": "object", + "properties": { + "resultUploadPolicy": { + "type": "string" + } + } + }, + "store.FileStorageSettingsInput": { + "type": "object", + "properties": { + "resultUploadPolicy": { + "type": "string" + } + } + }, "store.GatewayTask": { "type": "object", "properties": { diff --git a/apps/api/docs/swagger.yaml b/apps/api/docs/swagger.yaml index 8e71851..f60925b 100644 --- a/apps/api/docs/swagger.yaml +++ b/apps/api/docs/swagger.yaml @@ -138,6 +138,34 @@ definitions: example: 400 type: integer type: object + httpapi.FileStorageChannelListResponse: + properties: + items: + items: + $ref: '#/definitions/store.FileStorageChannel' + type: array + type: object + httpapi.FileUploadResponse: + properties: + assetStorage: + additionalProperties: true + type: object + contentType: + example: image/png + type: string + filename: + example: image.png + type: string + id: + example: file_abc123 + type: string + size: + example: 1024 + type: integer + url: + example: /static/uploaded/upload-abc123.png + type: string + type: object httpapi.HealthResponse: properties: env: @@ -1045,6 +1073,83 @@ definitions: secret: type: string type: object + store.FileStorageChannel: + properties: + channelKey: + type: string + config: + additionalProperties: {} + type: object + createdAt: + type: string + credentialsPreview: + additionalProperties: {} + type: object + id: + type: string + lastError: + type: string + lastFailedAt: + type: string + lastSucceededAt: + type: string + name: + type: string + priority: + type: integer + provider: + type: string + retryPolicy: + additionalProperties: {} + type: object + scenes: + items: + type: string + type: array + status: + type: string + updatedAt: + type: string + uploadUrl: + type: string + type: object + store.FileStorageChannelInput: + properties: + apiKey: + type: string + channelKey: + type: string + config: + additionalProperties: {} + type: object + name: + type: string + priority: + type: integer + provider: + type: string + retryPolicy: + additionalProperties: {} + type: object + scenes: + items: + type: string + type: array + status: + type: string + uploadUrl: + type: string + type: object + store.FileStorageSettings: + properties: + resultUploadPolicy: + type: string + type: object + store.FileStorageSettingsInput: + properties: + resultUploadPolicy: + type: string + type: object store.GatewayTask: properties: apiKeyId: @@ -3644,6 +3749,229 @@ paths: summary: 更新 Runner 策略 tags: - runtime + /api/admin/system/file-storage/channels: + get: + description: 返回所有未删除的文件存储通道,用于管理上传与生成资源回传策略。 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httpapi.FileStorageChannelListResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 列出文件存储通道 + tags: + - system + post: + consumes: + - application/json + description: 创建文件存储通道,当前主要用于配置 server-main OpenAPI 上传通道。 + parameters: + - description: 文件存储通道 + in: body + name: body + required: true + schema: + $ref: '#/definitions/store.FileStorageChannelInput' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/store.FileStorageChannel' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "409": + description: Conflict + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 创建文件存储通道 + tags: + - system + /api/admin/system/file-storage/channels/{channelID}: + delete: + description: 软删除指定文件存储通道。 + parameters: + - description: 文件存储通道 ID + in: path + name: channelID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "404": + description: Not Found + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 删除文件存储通道 + tags: + - system + patch: + consumes: + - application/json + description: 更新指定文件存储通道的名称、凭证、场景、优先级、状态和重试策略。 + parameters: + - description: 文件存储通道 ID + in: path + name: channelID + required: true + type: string + - description: 文件存储通道 + in: body + name: body + required: true + schema: + $ref: '#/definitions/store.FileStorageChannelInput' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/store.FileStorageChannel' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "404": + description: Not Found + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "409": + description: Conflict + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 更新文件存储通道 + tags: + - system + /api/admin/system/file-storage/settings: + get: + description: 返回文件存储系统设置;数据库对象尚未创建时返回默认设置。 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/store.FileStorageSettings' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 获取文件存储设置 + tags: + - system + patch: + consumes: + - application/json + description: 更新生成资源上传策略等文件存储系统设置。 + parameters: + - description: 文件存储设置 + in: body + name: body + required: true + schema: + $ref: '#/definitions/store.FileStorageSettingsInput' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/store.FileStorageSettings' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "403": + description: Forbidden + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 更新文件存储设置 + tags: + - system /api/admin/tenants: get: description: 管理端返回网关租户列表。 @@ -4529,6 +4857,50 @@ paths: summary: 创建或执行 AI 任务 tags: - tasks + /api/v1/files/upload: + post: + consumes: + - multipart/form-data + description: 上传文件到配置的文件存储通道;没有启用通道时回退到本地静态上传目录。单文件最大 256MiB。 + parameters: + - description: 要上传的文件 + in: formData + name: file + required: true + type: file + - default: ai-gateway-openapi + description: 上传来源标识 + in: formData + name: source + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httpapi.FileUploadResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "502": + description: Bad Gateway + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "503": + description: Service Unavailable + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 上传文件 + tags: + - files /api/v1/images/edits: post: consumes: @@ -5633,6 +6005,29 @@ paths: summary: 创建或执行 AI 任务 tags: - tasks + /static/generated/{asset}: + get: + description: 从本地生成资源目录读取图片、视频等任务产物;不存在时返回 404。 + parameters: + - description: 资源文件名 + in: path + name: asset + required: true + type: string + produces: + - application/octet-stream + responses: + "200": + description: OK + schema: + type: file + "404": + description: Not Found + schema: + type: string + summary: 获取本地生成资源 + tags: + - static /static/simulation/{asset}: get: description: 返回本地模拟模式使用的图片、视频封面或短视频资源。 @@ -5657,6 +6052,29 @@ paths: summary: 获取模拟资源 tags: - simulation + /static/uploaded/{asset}: + get: + description: 从本地上传资源目录读取用户上传文件;不存在时返回 404。 + parameters: + - description: 资源文件名 + in: path + name: asset + required: true + type: string + produces: + - application/octet-stream + responses: + "200": + description: OK + schema: + type: file + "404": + description: Not Found + schema: + type: string + summary: 获取本地上传资源 + tags: + - static /v1/chat/completions: post: consumes: @@ -5718,6 +6136,50 @@ paths: summary: 创建或执行 AI 任务 tags: - tasks + /v1/files/upload: + post: + consumes: + - multipart/form-data + description: 上传文件到配置的文件存储通道;没有启用通道时回退到本地静态上传目录。单文件最大 256MiB。 + parameters: + - description: 要上传的文件 + in: formData + name: file + required: true + type: file + - default: ai-gateway-openapi + description: 上传来源标识 + in: formData + name: source + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httpapi.FileUploadResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "502": + description: Bad Gateway + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + "503": + description: Service Unavailable + schema: + $ref: '#/definitions/httpapi.ErrorEnvelope' + security: + - BearerAuth: [] + summary: 上传文件 + tags: + - files /v1/images/edits: post: consumes: diff --git a/apps/api/internal/httpapi/file_upload_handlers.go b/apps/api/internal/httpapi/file_upload_handlers.go index 68db37a..ee807e6 100644 --- a/apps/api/internal/httpapi/file_upload_handlers.go +++ b/apps/api/internal/httpapi/file_upload_handlers.go @@ -11,6 +11,22 @@ import ( const maxGatewayUploadBytes = 256 << 20 +// uploadFile godoc +// @Summary 上传文件 +// @Description 上传文件到配置的文件存储通道;没有启用通道时回退到本地静态上传目录。单文件最大 256MiB。 +// @Tags files +// @Accept multipart/form-data +// @Produce json +// @Security BearerAuth +// @Param file formData file true "要上传的文件" +// @Param source formData string false "上传来源标识" default(ai-gateway-openapi) +// @Success 200 {object} FileUploadResponse +// @Failure 400 {object} ErrorEnvelope +// @Failure 401 {object} ErrorEnvelope +// @Failure 502 {object} ErrorEnvelope +// @Failure 503 {object} ErrorEnvelope +// @Router /api/v1/files/upload [post] +// @Router /v1/files/upload [post] func (s *Server) uploadFile(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxGatewayUploadBytes) if err := r.ParseMultipartForm(32 << 20); err != nil { diff --git a/apps/api/internal/httpapi/openapi_models.go b/apps/api/internal/httpapi/openapi_models.go index 05e9fa9..614d22b 100644 --- a/apps/api/internal/httpapi/openapi_models.go +++ b/apps/api/internal/httpapi/openapi_models.go @@ -123,6 +123,19 @@ type TaskEventListResponse struct { Items []store.TaskEvent `json:"items"` } +type FileStorageChannelListResponse struct { + Items []store.FileStorageChannel `json:"items"` +} + +type FileUploadResponse struct { + ID string `json:"id,omitempty" example:"file_abc123"` + URL string `json:"url,omitempty" example:"/static/uploaded/upload-abc123.png"` + Filename string `json:"filename,omitempty" example:"image.png"` + ContentType string `json:"contentType,omitempty" example:"image/png"` + Size int `json:"size,omitempty" example:"1024"` + AssetStorage map[string]interface{} `json:"assetStorage,omitempty"` +} + type ReplacePlatformModelsRequest struct { Models []store.CreatePlatformModelInput `json:"models"` } diff --git a/apps/api/internal/httpapi/static_assets.go b/apps/api/internal/httpapi/static_assets.go index 72d8c3d..00984a9 100644 --- a/apps/api/internal/httpapi/static_assets.go +++ b/apps/api/internal/httpapi/static_assets.go @@ -9,10 +9,28 @@ import ( "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" ) +// serveGeneratedStaticAsset godoc +// @Summary 获取本地生成资源 +// @Description 从本地生成资源目录读取图片、视频等任务产物;不存在时返回 404。 +// @Tags static +// @Produce octet-stream +// @Param asset path string true "资源文件名" +// @Success 200 {file} file +// @Failure 404 {string} string "Not Found" +// @Router /static/generated/{asset} [get] func (s *Server) serveGeneratedStaticAsset(w http.ResponseWriter, r *http.Request) { s.serveLocalStaticAsset(w, r, s.cfg.LocalGeneratedStorageDir, config.DefaultLocalGeneratedStorageDir) } +// serveUploadedStaticAsset godoc +// @Summary 获取本地上传资源 +// @Description 从本地上传资源目录读取用户上传文件;不存在时返回 404。 +// @Tags static +// @Produce octet-stream +// @Param asset path string true "资源文件名" +// @Success 200 {file} file +// @Failure 404 {string} string "Not Found" +// @Router /static/uploaded/{asset} [get] func (s *Server) serveUploadedStaticAsset(w http.ResponseWriter, r *http.Request) { s.serveLocalStaticAsset(w, r, s.cfg.LocalUploadedStorageDir, config.DefaultLocalUploadedStorageDir) } diff --git a/apps/api/internal/httpapi/system_settings_handlers.go b/apps/api/internal/httpapi/system_settings_handlers.go index 649315d..a497c7e 100644 --- a/apps/api/internal/httpapi/system_settings_handlers.go +++ b/apps/api/internal/httpapi/system_settings_handlers.go @@ -8,6 +8,17 @@ import ( "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) +// listFileStorageChannels godoc +// @Summary 列出文件存储通道 +// @Description 返回所有未删除的文件存储通道,用于管理上传与生成资源回传策略。 +// @Tags system +// @Produce json +// @Security BearerAuth +// @Success 200 {object} FileStorageChannelListResponse +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/channels [get] func (s *Server) listFileStorageChannels(w http.ResponseWriter, r *http.Request) { items, err := s.store.ListFileStorageChannels(r.Context()) if err != nil { @@ -18,6 +29,17 @@ func (s *Server) listFileStorageChannels(w http.ResponseWriter, r *http.Request) writeJSON(w, http.StatusOK, map[string]any{"items": items}) } +// getFileStorageSettings godoc +// @Summary 获取文件存储设置 +// @Description 返回文件存储系统设置;数据库对象尚未创建时返回默认设置。 +// @Tags system +// @Produce json +// @Security BearerAuth +// @Success 200 {object} store.FileStorageSettings +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/settings [get] func (s *Server) getFileStorageSettings(w http.ResponseWriter, r *http.Request) { settings, err := s.store.GetFileStorageSettings(r.Context()) if err != nil { @@ -32,6 +54,20 @@ func (s *Server) getFileStorageSettings(w http.ResponseWriter, r *http.Request) writeJSON(w, http.StatusOK, settings) } +// updateFileStorageSettings godoc +// @Summary 更新文件存储设置 +// @Description 更新生成资源上传策略等文件存储系统设置。 +// @Tags system +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param body body store.FileStorageSettingsInput true "文件存储设置" +// @Success 200 {object} store.FileStorageSettings +// @Failure 400 {object} ErrorEnvelope +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/settings [patch] func (s *Server) updateFileStorageSettings(w http.ResponseWriter, r *http.Request) { var input store.FileStorageSettingsInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { @@ -47,6 +83,21 @@ func (s *Server) updateFileStorageSettings(w http.ResponseWriter, r *http.Reques writeJSON(w, http.StatusOK, settings) } +// createFileStorageChannel godoc +// @Summary 创建文件存储通道 +// @Description 创建文件存储通道,当前主要用于配置 server-main OpenAPI 上传通道。 +// @Tags system +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param body body store.FileStorageChannelInput true "文件存储通道" +// @Success 201 {object} store.FileStorageChannel +// @Failure 400 {object} ErrorEnvelope +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 409 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/channels [post] func (s *Server) createFileStorageChannel(w http.ResponseWriter, r *http.Request) { var input store.FileStorageChannelInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { @@ -70,6 +121,23 @@ func (s *Server) createFileStorageChannel(w http.ResponseWriter, r *http.Request writeJSON(w, http.StatusCreated, item) } +// updateFileStorageChannel godoc +// @Summary 更新文件存储通道 +// @Description 更新指定文件存储通道的名称、凭证、场景、优先级、状态和重试策略。 +// @Tags system +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param channelID path string true "文件存储通道 ID" +// @Param body body store.FileStorageChannelInput true "文件存储通道" +// @Success 200 {object} store.FileStorageChannel +// @Failure 400 {object} ErrorEnvelope +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 404 {object} ErrorEnvelope +// @Failure 409 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/channels/{channelID} [patch] func (s *Server) updateFileStorageChannel(w http.ResponseWriter, r *http.Request) { var input store.FileStorageChannelInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { @@ -107,6 +175,19 @@ func (s *Server) updateFileStorageChannel(w http.ResponseWriter, r *http.Request writeJSON(w, http.StatusOK, item) } +// deleteFileStorageChannel godoc +// @Summary 删除文件存储通道 +// @Description 软删除指定文件存储通道。 +// @Tags system +// @Produce json +// @Security BearerAuth +// @Param channelID path string true "文件存储通道 ID" +// @Success 204 "No Content" +// @Failure 401 {object} ErrorEnvelope +// @Failure 403 {object} ErrorEnvelope +// @Failure 404 {object} ErrorEnvelope +// @Failure 500 {object} ErrorEnvelope +// @Router /api/admin/system/file-storage/channels/{channelID} [delete] func (s *Server) deleteFileStorageChannel(w http.ResponseWriter, r *http.Request) { if err := s.store.DeleteFileStorageChannel(r.Context(), r.PathValue("channelID")); err != nil { if store.IsNotFound(err) {