feat(storage): add storage backend management API and UI enhancements
Implement a comprehensive API for managing storage backends, including endpoints for listing, creating, updating, and deleting storage instances. Enhance the UI to support multi-instance configurations, allowing users to manage multiple storage types effectively. Update localization files for new storage backend features and improve error handling for connectivity tests. This update also includes detailed documentation for the storage backend API.
This commit is contained in:
@@ -89,5 +89,6 @@ WeKnora API 按功能分为以下几类:
|
||||
| Skills | 预装智能体技能 | [skill.md](./skill.md) |
|
||||
| 网络搜索 | 网络搜索服务商 | [web-search.md](./web-search.md) |
|
||||
| 向量存储 | 向量数据库连接管理 | [vector-store.md](./vector-store.md) |
|
||||
| 存储后端 | 对象/文件存储实例(多实例)管理 | [storage-backend.md](./storage-backend.md) |
|
||||
| IM 渠道 | 企业微信 / 飞书 / Slack 等 IM 平台对接,含渠道 CRUD 与回调 | [../IM集成开发文档.md](../IM集成开发文档.md) |
|
||||
| 数据源导入 | 飞书 / 企微 / Notion / Confluence 等外部数据源接入与同步 | [../数据源导入开发文档.md](../数据源导入开发文档.md) |
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
# Storage Backend API
|
||||
|
||||
[返回目录](./README.md)
|
||||
|
||||
存储后端(StorageBackend)API 用于管理空间的对象/文件存储实例。一个空间可以注册多个存储实例(`local`、`minio`、`cos`、`tos`、`s3`、`oss`、`ks3`、`obs`),并将不同知识库绑定到不同实例;空间维度还有一个默认实例(`default_storage_backend_id`),未显式绑定的新知识库使用该默认实例。
|
||||
|
||||
接口同时管理用户创建的实例(`source: "user"`)以及从环境变量快照生成的只读实例(`source: "env"`)。存储后端 CRUD 需要 **Admin+** 角色;对 API Key 需具备 `manage_storage_backends` capability(或 full-access)。
|
||||
|
||||
| 方法 | 路径 | 描述 | 最低权限 |
|
||||
| ------ | --------------------------------- | -------------------------------------- | -------- |
|
||||
| GET | `/storage-backends/types` | 获取 `STORAGE_ALLOW_LIST` 允许的存储类型 | Viewer+ |
|
||||
| POST | `/storage-backends/test` | 使用原始配置测试连通性(不落库) | Admin+ |
|
||||
| POST | `/storage-backends` | 创建存储实例 | Admin+ |
|
||||
| GET | `/storage-backends` | 获取存储实例列表 | Viewer+ |
|
||||
| GET | `/storage-backends/:id` | 获取存储实例详情 | Viewer+ |
|
||||
| PUT | `/storage-backends/:id` | 更新存储实例(名称/凭据/状态) | Admin+ |
|
||||
| DELETE | `/storage-backends/:id` | 删除存储实例(软删除) | Admin+ |
|
||||
| POST | `/storage-backends/:id/test` | 测试已保存实例的连通性 | Admin+ |
|
||||
| PUT | `/storage-backends/:id/default` | 设为空间默认存储实例 | Admin+ |
|
||||
|
||||
> 所有响应中的敏感字段(`access_key_id`、`secret_access_key`)都会被掩码。更新时若提交掩码占位符,则保留库中原有的真实凭据,不会被占位符覆盖。
|
||||
|
||||
## 存储配置字段(`config`)
|
||||
|
||||
不同 provider 使用同一套归一化的配置对象,按 provider 取用其中的子集:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ------------------- | ------- | ------------------------------------------------------------- |
|
||||
| mode | string | MinIO 模式:`docker`(复用环境变量凭据)或 `remote` |
|
||||
| endpoint | string | 对象存储 endpoint(COS 使用 region,不需要 endpoint) |
|
||||
| region | string | 区域 |
|
||||
| access_key_id | string | 访问密钥 ID(COS 对应 SecretID);响应中掩码 |
|
||||
| secret_access_key | string | 访问密钥 Secret(COS 对应 SecretKey);响应中掩码 |
|
||||
| bucket_name | string | Bucket 名称 |
|
||||
| path_prefix | string | 对象前缀,必须为相对路径,禁止 `/` 开头或 `..` 上跳 |
|
||||
| app_id | string | 腾讯云 COS AppID |
|
||||
| use_ssl | boolean | 是否使用 SSL |
|
||||
| force_path_style | boolean | S3 是否使用 path-style 寻址 |
|
||||
| use_temp_bucket | boolean | OSS 是否使用临时 bucket |
|
||||
| temp_bucket_name | string | 临时 bucket 名称 |
|
||||
| temp_region | string | 临时 bucket 区域 |
|
||||
|
||||
> `endpoint`、`region`、`bucket_name`、`path_prefix` 决定对象的物理位置,**创建后不可变更**(更新时会被拒绝);如需迁移请使用存储迁移流程。凭据可通过更新单独轮换。
|
||||
|
||||
## GET `/storage-backends/types` - 获取允许的存储类型
|
||||
|
||||
返回 `STORAGE_ALLOW_LIST` 允许的 provider 列表,可用于前端动态表单生成。
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location 'http://localhost:8080/api/v1/storage-backends/types' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": ["local", "minio", "cos", "s3"]
|
||||
}
|
||||
```
|
||||
|
||||
## POST `/storage-backends/test` - 使用原始配置测试连通性
|
||||
|
||||
用前端表单中尚未保存的配置执行一次连通性测试,不会写入数据库。
|
||||
|
||||
**参数说明(请求体)**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| -------- | ------ | ---- | ------------------------------------------- |
|
||||
| name | string | 是 | 实例显示名 |
|
||||
| provider | string | 是 | 存储类型,取自 `/storage-backends/types` |
|
||||
| config | object | 否 | 该 provider 对应的存储配置字段 |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location --request POST 'http://localhost:8080/api/v1/storage-backends/test' \
|
||||
--header 'X-API-Key: sk-xxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "s3-hot",
|
||||
"provider": "s3",
|
||||
"config": {
|
||||
"endpoint": "https://s3.example.com",
|
||||
"region": "ap-test-1",
|
||||
"access_key_id": "AKID",
|
||||
"secret_access_key": "SECRET",
|
||||
"bucket_name": "weknora"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**响应(成功)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**响应(失败)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "连接被拒绝,请确认服务已启动且端口正确"
|
||||
}
|
||||
```
|
||||
|
||||
> 测试失败时 HTTP 状态码仍为 `200`,错误信息通过 `success: false` + `error` 返回;`error` 已脱敏,不会泄漏内部主机名、IP、端口或 TLS 细节。
|
||||
|
||||
## POST `/storage-backends` - 创建存储实例
|
||||
|
||||
为当前空间创建一个新的存储实例。创建前会先校验配置、执行 SSRF 校验(本地存储与 docker 模式 MinIO 除外),并执行一次连通性测试;任一环节失败都会返回 `400`。同一空间内实例名称不允许重复。
|
||||
|
||||
**参数说明(请求体)**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| -------- | ------ | ---- | ------------------------------------------- |
|
||||
| name | string | 是 | 实例显示名(空间内唯一) |
|
||||
| provider | string | 是 | 存储类型,取自 `/storage-backends/types` |
|
||||
| config | object | 否 | 该 provider 对应的存储配置字段 |
|
||||
| status | string | 否 | `active`(默认)或 `disabled` |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location 'http://localhost:8080/api/v1/storage-backends' \
|
||||
--header 'X-API-Key: sk-xxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "s3-hot",
|
||||
"provider": "s3",
|
||||
"config": {
|
||||
"endpoint": "https://s3.example.com",
|
||||
"region": "ap-test-1",
|
||||
"access_key_id": "AKID",
|
||||
"secret_access_key": "SECRET",
|
||||
"bucket_name": "weknora",
|
||||
"path_prefix": "prod"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**响应** (201):
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "s3-hot",
|
||||
"provider": "s3",
|
||||
"config": {
|
||||
"endpoint": "https://s3.example.com",
|
||||
"region": "ap-test-1",
|
||||
"access_key_id": "***",
|
||||
"secret_access_key": "***",
|
||||
"bucket_name": "weknora",
|
||||
"path_prefix": "prod"
|
||||
},
|
||||
"source": "user",
|
||||
"status": "active",
|
||||
"legacy_alias": false,
|
||||
"created_at": "2026-07-15T10:00:00Z",
|
||||
"updated_at": "2026-07-15T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## GET `/storage-backends` - 获取存储实例列表
|
||||
|
||||
返回当前空间的所有存储实例(凭据已掩码),并在顶层返回空间默认实例 id。
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location 'http://localhost:8080/api/v1/storage-backends' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "s3-hot",
|
||||
"provider": "s3",
|
||||
"config": { "endpoint": "https://s3.example.com", "access_key_id": "***", "secret_access_key": "***", "bucket_name": "weknora" },
|
||||
"source": "user",
|
||||
"status": "active",
|
||||
"legacy_alias": false
|
||||
}
|
||||
],
|
||||
"default_storage_backend_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
## GET `/storage-backends/:id` - 获取存储实例详情
|
||||
|
||||
根据 ID 获取当前空间下的单个存储实例,凭据已掩码。
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------------- |
|
||||
| id | string | 是 | 存储实例 ID |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location 'http://localhost:8080/api/v1/storage-backends/550e8400-e29b-41d4-a716-446655440000' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
## PUT `/storage-backends/:id` - 更新存储实例
|
||||
|
||||
更新实例的可变字段(`name`、凭据、`status`)。`provider` 与物理位置字段(`endpoint`、`region`、`bucket_name`、`path_prefix`)不可变更,尝试更改会返回 `400`。环境变量来源(`source: "env"`)的实例只读,不可更新。更新同样会执行校验与连通性测试。
|
||||
|
||||
> 若 `access_key_id` / `secret_access_key` 提交为掩码占位符(`***`),则保留库中原有真实凭据。
|
||||
|
||||
**禁用保护**:将当前为默认实例、或仍有知识库绑定的实例改为 `disabled` 会被拒绝(`400`)。
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------------- |
|
||||
| id | string | 是 | 存储实例 ID |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location --request PUT 'http://localhost:8080/api/v1/storage-backends/550e8400-e29b-41d4-a716-446655440000' \
|
||||
--header 'X-API-Key: sk-xxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "s3-hot-renamed",
|
||||
"provider": "s3",
|
||||
"config": {
|
||||
"access_key_id": "***",
|
||||
"secret_access_key": "NEW_SECRET"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## DELETE `/storage-backends/:id` - 删除存储实例
|
||||
|
||||
对存储实例执行软删除。以下情况删除会被拒绝(`400`):实例是空间默认实例、仍有知识库绑定、环境变量来源(只读)、或为 legacy 别名(旧文件路径可能仍引用它)。删除在事务中执行;PostgreSQL 上对目标行加行锁以避免并发绑定竞态。
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------------- |
|
||||
| id | string | 是 | 存储实例 ID |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location --request DELETE 'http://localhost:8080/api/v1/storage-backends/550e8400-e29b-41d4-a716-446655440000' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
**响应(成功)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
## POST `/storage-backends/:id/test` - 测试已保存实例的连通性
|
||||
|
||||
对已保存的存储实例用其存储的凭据执行一次连通性测试。
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------------- |
|
||||
| id | string | 是 | 存储实例 ID |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location --request POST 'http://localhost:8080/api/v1/storage-backends/550e8400-e29b-41d4-a716-446655440000/test' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
**响应(成功)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> 与 `/storage-backends/test` 一致,测试失败时 HTTP 状态码仍为 `200`,错误经脱敏后通过 `success: false` + `error` 返回。
|
||||
|
||||
## PUT `/storage-backends/:id/default` - 设为空间默认实例
|
||||
|
||||
将某个存储实例标记为空间默认实例。仅 `active` 状态的实例可以设为默认。未显式绑定存储实例的新知识库将使用默认实例。
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------------- |
|
||||
| id | string | 是 | 存储实例 ID |
|
||||
|
||||
**请求**:
|
||||
|
||||
```curl
|
||||
curl --location --request PUT 'http://localhost:8080/api/v1/storage-backends/550e8400-e29b-41d4-a716-446655440000/default' \
|
||||
--header 'X-API-Key: sk-xxxxx'
|
||||
```
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
## 环境变量存储实例
|
||||
|
||||
通过 `STORAGE_TYPE` 等环境变量配置的存储会以只读实例(`source: "env"`、`legacy_alias: true`)形式参与实例解析,使 env-only 部署与用户管理的实例走同一套解析路径。这类实例在每次启动时按环境变量刷新,且不可通过 API 更新或删除。
|
||||
|
||||
## 错误码
|
||||
|
||||
| HTTP 状态码 | 含义 |
|
||||
| ----------- | ---------------------------------------------------------------- |
|
||||
| 400 | 请求参数错误、校验失败、SSRF 校验失败、连通性测试失败、尝试更改不可变字段、修改只读实例、删除受保护实例、禁用被引用实例、设为默认时实例非 active |
|
||||
| 401 | 未认证(缺少空间上下文或 API Key) |
|
||||
| 403 | 权限不足(需 Admin+ 或 API Key `manage_storage_backends` capability) |
|
||||
| 404 | 存储实例不存在 |
|
||||
| 409 | 同名存储实例已存在 |
|
||||
+551
-1
@@ -11077,6 +11077,468 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "List all storage backend instances for the current workspace, with credentials masked. The workspace default backend id is returned alongside the list.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "List storage backends",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of storage backends and default_storage_backend_id",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Register a new object/file storage instance for the current workspace. The configuration is validated and a connectivity test is run before the backend is persisted.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Create storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Storage backend configuration",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created storage backend",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request, validation, or connectivity test failure",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "A storage backend with this name already exists",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Test connectivity for the provided storage configuration without persisting it. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Test storage backend with raw config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Storage backend configuration to test",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Connectivity test result (success, error)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request or validation error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/types": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Return the storage provider types allowed by STORAGE_ALLOW_LIST for UI form generation (e.g. local, minio, cos, tos, s3, oss, ks3, obs).",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "List allowed storage provider types",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of allowed storage provider types",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Retrieve a single storage backend by ID for the current workspace. Credentials are masked.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Get storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Storage backend details",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Update a storage backend's mutable fields (name, credentials, status). Provider and physical location (endpoint, region, bucket, path prefix) are immutable; use storage migration to move data. Environment-sourced backends are read-only. Redacted secret placeholders preserve the stored credentials.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Update storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Updated storage backend fields",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Updated storage backend",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Immutable field change, read-only backend, validation, or connectivity failure",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Soft-delete a storage backend. A backend that is the workspace default, still bound to knowledge bases, environment-sourced, or a legacy alias cannot be deleted.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Delete storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Deletion success",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Backend is default, bound, read-only, or legacy alias",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}/default": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Mark a storage backend as the workspace default. Only an active backend can become the default. New knowledge bases without an explicit binding use the default.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Set default storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default set successfully",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Backend is not active",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Test connectivity of an existing saved storage backend using its stored credentials. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Test storage backend by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Connectivity test result (success, error)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/system/admin/audit-log": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -14387,6 +14849,12 @@ const docTemplate = `{
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15718,6 +16186,10 @@ const docTemplate = `{
|
||||
"description": "ShareCount indicates the number of organizations this knowledge base is shared with (not stored in database)",
|
||||
"type": "integer"
|
||||
},
|
||||
"storage_backend_id": {
|
||||
"description": "StorageBackendID binds this KB to one concrete storage instance. The\nlegacy provider field remains readable during migration only.",
|
||||
"type": "string"
|
||||
},
|
||||
"storage_config": {
|
||||
"description": "Deprecated: legacy COS config column. Kept for backward compatibility with old data.",
|
||||
"allOf": [
|
||||
@@ -17888,6 +18360,50 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_Tencent_WeKnora_internal_types.StorageBackendConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_key_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"force_path_style": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"path_prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"region": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret_access_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
},
|
||||
"use_ssl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_temp_bucket": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_Tencent_WeKnora_internal_types.StorageConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -18159,6 +18675,12 @@ const docTemplate = `{
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18197,6 +18719,10 @@ const docTemplate = `{
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_storage_backend_id": {
|
||||
"description": "DefaultStorageBackendID is the workspace default concrete storage instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"deleted_at": {
|
||||
"description": "Deletion time",
|
||||
"allOf": [
|
||||
@@ -19993,6 +20519,9 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"storageBackendId": {
|
||||
"type": "string"
|
||||
},
|
||||
"storageProvider": {
|
||||
"description": "存储引擎选择(\"local\" | \"minio\" | \"cos\"),影响文档上传与文档内图片存储,参数从全局设置读取",
|
||||
"type": "string"
|
||||
@@ -20516,7 +21045,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "\"local\", \"minio\", \"cos\", \"tos\", \"s3\", \"oss\", \"ks3\"",
|
||||
"description": "\"local\", \"minio\", \"cos\", \"tos\", \"s3\", \"oss\", \"ks3\", \"obs\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -20923,6 +21452,27 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_handler.storageBackendRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"provider"
|
||||
],
|
||||
"properties": {
|
||||
"config": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageBackendConfig"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_handler.updateLastFAQImportResultDisplayStatusRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
+551
-1
@@ -11070,6 +11070,468 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "List all storage backend instances for the current workspace, with credentials masked. The workspace default backend id is returned alongside the list.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "List storage backends",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of storage backends and default_storage_backend_id",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Register a new object/file storage instance for the current workspace. The configuration is validated and a connectivity test is run before the backend is persisted.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Create storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Storage backend configuration",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created storage backend",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request, validation, or connectivity test failure",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "A storage backend with this name already exists",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Test connectivity for the provided storage configuration without persisting it. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Test storage backend with raw config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Storage backend configuration to test",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Connectivity test result (success, error)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request or validation error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/types": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Return the storage provider types allowed by STORAGE_ALLOW_LIST for UI form generation (e.g. local, minio, cos, tos, s3, oss, ks3, obs).",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "List allowed storage provider types",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of allowed storage provider types",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Retrieve a single storage backend by ID for the current workspace. Credentials are masked.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Get storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Storage backend details",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Update a storage backend's mutable fields (name, credentials, status). Provider and physical location (endpoint, region, bucket, path prefix) are immutable; use storage migration to move data. Environment-sourced backends are read-only. Redacted secret placeholders preserve the stored credentials.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Update storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Updated storage backend fields",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_handler.storageBackendRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Updated storage backend",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Immutable field change, read-only backend, validation, or connectivity failure",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Soft-delete a storage backend. A backend that is the workspace default, still bound to knowledge bases, environment-sourced, or a legacy alias cannot be deleted.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Delete storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Deletion success",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Backend is default, bound, read-only, or legacy alias",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}/default": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Mark a storage backend as the workspace default. Only an active backend can become the default. New knowledge bases without an explicit binding use the default.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Set default storage backend",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Default set successfully",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Backend is not active",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/storage-backends/{id}/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
},
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Test connectivity of an existing saved storage backend using its stored credentials. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"StorageBackend"
|
||||
],
|
||||
"summary": "Test storage backend by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Storage backend ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Connectivity test result (success, error)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Storage backend not found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/system/admin/audit-log": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -14380,6 +14842,12 @@
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15711,6 +16179,10 @@
|
||||
"description": "ShareCount indicates the number of organizations this knowledge base is shared with (not stored in database)",
|
||||
"type": "integer"
|
||||
},
|
||||
"storage_backend_id": {
|
||||
"description": "StorageBackendID binds this KB to one concrete storage instance. The\nlegacy provider field remains readable during migration only.",
|
||||
"type": "string"
|
||||
},
|
||||
"storage_config": {
|
||||
"description": "Deprecated: legacy COS config column. Kept for backward compatibility with old data.",
|
||||
"allOf": [
|
||||
@@ -17881,6 +18353,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_Tencent_WeKnora_internal_types.StorageBackendConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_key_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"force_path_style": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"path_prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"region": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret_access_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
},
|
||||
"use_ssl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_temp_bucket": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_Tencent_WeKnora_internal_types.StorageConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -18152,6 +18668,12 @@
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"temp_region": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18190,6 +18712,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_storage_backend_id": {
|
||||
"description": "DefaultStorageBackendID is the workspace default concrete storage instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"deleted_at": {
|
||||
"description": "Deletion time",
|
||||
"allOf": [
|
||||
@@ -19986,6 +20512,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"storageBackendId": {
|
||||
"type": "string"
|
||||
},
|
||||
"storageProvider": {
|
||||
"description": "存储引擎选择(\"local\" | \"minio\" | \"cos\"),影响文档上传与文档内图片存储,参数从全局设置读取",
|
||||
"type": "string"
|
||||
@@ -20509,7 +21038,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "\"local\", \"minio\", \"cos\", \"tos\", \"s3\", \"oss\", \"ks3\"",
|
||||
"description": "\"local\", \"minio\", \"cos\", \"tos\", \"s3\", \"oss\", \"ks3\", \"obs\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -20916,6 +21445,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_handler.storageBackendRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"provider"
|
||||
],
|
||||
"properties": {
|
||||
"config": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageBackendConfig"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_handler.updateLastFAQImportResultDisplayStatusRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
+371
-1
@@ -377,6 +377,10 @@ definitions:
|
||||
type: string
|
||||
secret_key:
|
||||
type: string
|
||||
temp_bucket_name:
|
||||
type: string
|
||||
temp_region:
|
||||
type: string
|
||||
type: object
|
||||
github_com_Tencent_WeKnora_internal_types.ChatHistoryConfig:
|
||||
properties:
|
||||
@@ -1461,6 +1465,11 @@ definitions:
|
||||
description: ShareCount indicates the number of organizations this knowledge
|
||||
base is shared with (not stored in database)
|
||||
type: integer
|
||||
storage_backend_id:
|
||||
description: |-
|
||||
StorageBackendID binds this KB to one concrete storage instance. The
|
||||
legacy provider field remains readable during migration only.
|
||||
type: string
|
||||
storage_config:
|
||||
allOf:
|
||||
- $ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.StorageConfig'
|
||||
@@ -3080,6 +3089,35 @@ definitions:
|
||||
mode:
|
||||
type: string
|
||||
type: object
|
||||
github_com_Tencent_WeKnora_internal_types.StorageBackendConfig:
|
||||
properties:
|
||||
access_key_id:
|
||||
type: string
|
||||
app_id:
|
||||
type: string
|
||||
bucket_name:
|
||||
type: string
|
||||
endpoint:
|
||||
type: string
|
||||
force_path_style:
|
||||
type: boolean
|
||||
mode:
|
||||
type: string
|
||||
path_prefix:
|
||||
type: string
|
||||
region:
|
||||
type: string
|
||||
secret_access_key:
|
||||
type: string
|
||||
temp_bucket_name:
|
||||
type: string
|
||||
temp_region:
|
||||
type: string
|
||||
use_ssl:
|
||||
type: boolean
|
||||
use_temp_bucket:
|
||||
type: boolean
|
||||
type: object
|
||||
github_com_Tencent_WeKnora_internal_types.StorageConfig:
|
||||
properties:
|
||||
app_id:
|
||||
@@ -3292,6 +3330,10 @@ definitions:
|
||||
type: string
|
||||
secret_key:
|
||||
type: string
|
||||
temp_bucket_name:
|
||||
type: string
|
||||
temp_region:
|
||||
type: string
|
||||
type: object
|
||||
github_com_Tencent_WeKnora_internal_types.Tenant:
|
||||
properties:
|
||||
@@ -3316,6 +3358,10 @@ definitions:
|
||||
- $ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.CredentialsConfig'
|
||||
description: 'Credentials config: third-party provider credentials (e.g. WeKnoraCloud
|
||||
AppID/AppSecret)'
|
||||
default_storage_backend_id:
|
||||
description: DefaultStorageBackendID is the workspace default concrete storage
|
||||
instance.
|
||||
type: string
|
||||
deleted_at:
|
||||
allOf:
|
||||
- $ref: '#/definitions/gorm.DeletedAt'
|
||||
@@ -4651,6 +4697,8 @@ definitions:
|
||||
questionCount:
|
||||
type: integer
|
||||
type: object
|
||||
storageBackendId:
|
||||
type: string
|
||||
storageProvider:
|
||||
description: 存储引擎选择("local" | "minio" | "cos"),影响文档上传与文档内图片存储,参数从全局设置读取
|
||||
type: string
|
||||
@@ -5024,7 +5072,7 @@ definitions:
|
||||
description: short description for UI
|
||||
type: string
|
||||
name:
|
||||
description: '"local", "minio", "cos", "tos", "s3", "oss", "ks3"'
|
||||
description: '"local", "minio", "cos", "tos", "s3", "oss", "ks3", "obs"'
|
||||
type: string
|
||||
type: object
|
||||
internal_handler.SuggestionEventRequest:
|
||||
@@ -5295,6 +5343,20 @@ definitions:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
internal_handler.storageBackendRequest:
|
||||
properties:
|
||||
config:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.StorageBackendConfig'
|
||||
name:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- provider
|
||||
type: object
|
||||
internal_handler.updateLastFAQImportResultDisplayStatusRequest:
|
||||
properties:
|
||||
display_status:
|
||||
@@ -12553,6 +12615,314 @@ paths:
|
||||
summary: 获取预装Skills列表
|
||||
tags:
|
||||
- Skills
|
||||
/storage-backends:
|
||||
get:
|
||||
description: List all storage backend instances for the current workspace, with
|
||||
credentials masked. The workspace default backend id is returned alongside
|
||||
the list.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: List of storage backends and default_storage_backend_id
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: List storage backends
|
||||
tags:
|
||||
- StorageBackend
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Register a new object/file storage instance for the current workspace.
|
||||
The configuration is validated and a connectivity test is run before the backend
|
||||
is persisted.
|
||||
parameters:
|
||||
- description: Storage backend configuration
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/internal_handler.storageBackendRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created storage backend
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid request, validation, or connectivity test failure
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"409":
|
||||
description: A storage backend with this name already exists
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Create storage backend
|
||||
tags:
|
||||
- StorageBackend
|
||||
/storage-backends/{id}:
|
||||
delete:
|
||||
description: Soft-delete a storage backend. A backend that is the workspace
|
||||
default, still bound to knowledge bases, environment-sourced, or a legacy
|
||||
alias cannot be deleted.
|
||||
parameters:
|
||||
- description: Storage backend ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Deletion success
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: Backend is default, bound, read-only, or legacy alias
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"404":
|
||||
description: Storage backend not found
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Delete storage backend
|
||||
tags:
|
||||
- StorageBackend
|
||||
get:
|
||||
description: Retrieve a single storage backend by ID for the current workspace.
|
||||
Credentials are masked.
|
||||
parameters:
|
||||
- description: Storage backend ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Storage backend details
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"404":
|
||||
description: Storage backend not found
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Get storage backend
|
||||
tags:
|
||||
- StorageBackend
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Update a storage backend's mutable fields (name, credentials, status).
|
||||
Provider and physical location (endpoint, region, bucket, path prefix) are
|
||||
immutable; use storage migration to move data. Environment-sourced backends
|
||||
are read-only. Redacted secret placeholders preserve the stored credentials.
|
||||
parameters:
|
||||
- description: Storage backend ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Updated storage backend fields
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/internal_handler.storageBackendRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Updated storage backend
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: Immutable field change, read-only backend, validation, or connectivity
|
||||
failure
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"404":
|
||||
description: Storage backend not found
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Update storage backend
|
||||
tags:
|
||||
- StorageBackend
|
||||
/storage-backends/{id}/default:
|
||||
put:
|
||||
description: Mark a storage backend as the workspace default. Only an active
|
||||
backend can become the default. New knowledge bases without an explicit binding
|
||||
use the default.
|
||||
parameters:
|
||||
- description: Storage backend ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Default set successfully
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: Backend is not active
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"404":
|
||||
description: Storage backend not found
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Set default storage backend
|
||||
tags:
|
||||
- StorageBackend
|
||||
/storage-backends/{id}/test:
|
||||
post:
|
||||
description: Test connectivity of an existing saved storage backend using its
|
||||
stored credentials. Returns success=false with a sanitized error message on
|
||||
failure (the HTTP status stays 200).
|
||||
parameters:
|
||||
- description: Storage backend ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Connectivity test result (success, error)
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"404":
|
||||
description: Storage backend not found
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Test storage backend by ID
|
||||
tags:
|
||||
- StorageBackend
|
||||
/storage-backends/test:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Test connectivity for the provided storage configuration without
|
||||
persisting it. Returns success=false with a sanitized error message on failure
|
||||
(the HTTP status stays 200).
|
||||
parameters:
|
||||
- description: Storage backend configuration to test
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/internal_handler.storageBackendRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Connectivity test result (success, error)
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid request or validation error
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: Test storage backend with raw config
|
||||
tags:
|
||||
- StorageBackend
|
||||
/storage-backends/types:
|
||||
get:
|
||||
description: Return the storage provider types allowed by STORAGE_ALLOW_LIST
|
||||
for UI form generation (e.g. local, minio, cos, tos, s3, oss, ks3, obs).
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: List of allowed storage provider types
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
- ApiKeyAuth: []
|
||||
summary: List allowed storage provider types
|
||||
tags:
|
||||
- StorageBackend
|
||||
/system/admin/audit-log:
|
||||
get:
|
||||
description: 返回 system-scope(tenant_id=0)的审计事件,覆盖 system.setting_changed / system.admin_promoted
|
||||
|
||||
@@ -1371,6 +1371,49 @@ export default {
|
||||
paddleocrVlEndpointHint: 'Base URL of the full PaddleOCR-VL pipeline service; no /layout-parsing suffix needed',
|
||||
paddleocrVlCloudTokenPlaceholder: 'PaddleOCR-VL AI Studio Token',
|
||||
},
|
||||
storageBackend: {
|
||||
description: 'Manage the storage instances used for files and images; multiple instances of the same type can be configured.',
|
||||
empty: 'No storage instances configured yet',
|
||||
defaultTag: 'Default',
|
||||
add: 'Add storage instance',
|
||||
editTitle: 'Edit storage instance',
|
||||
createTitle: 'Add storage instance',
|
||||
editSubtitle: 'Update the connection configuration of this storage instance.',
|
||||
createSubtitle: 'Add a new storage instance for files and images.',
|
||||
basicSection: 'Basic information',
|
||||
nameLabel: 'Name',
|
||||
namePlaceholder: 'e.g. Production COS, Archive COS',
|
||||
providerLabel: 'Storage type',
|
||||
modeLabel: 'Deployment mode',
|
||||
modeRemote: 'Remote instance',
|
||||
modeEnv: 'Environment variables',
|
||||
connectionSection: 'Connection settings',
|
||||
optionalPlaceholder: 'Optional',
|
||||
advancedSection: 'Advanced options',
|
||||
pathPrefixLabel: 'Path prefix',
|
||||
useSslDesc: 'Access MinIO over HTTPS',
|
||||
forcePathStyleDesc: 'Use path style',
|
||||
useTempBucketDesc: 'Use temporary bucket',
|
||||
tempBucketLabel: 'Temporary bucket',
|
||||
tempBucketPlaceholder: 'Optional, for temporary files',
|
||||
tempRegionLabel: 'Temporary bucket region',
|
||||
tempRegionPlaceholder: 'Leave empty to use the main region',
|
||||
testConnection: 'Test connection',
|
||||
localStorage: 'Local storage',
|
||||
setDefault: 'Set as default',
|
||||
edit: 'Edit',
|
||||
delete: 'Delete',
|
||||
testSuccess: 'Connection succeeded',
|
||||
testFailed: 'Connection failed',
|
||||
nameRequired: 'Please enter a name',
|
||||
saveSuccess: 'Saved successfully',
|
||||
saveFailed: 'Failed to save',
|
||||
defaultUpdated: 'Default storage updated',
|
||||
deleteTitle: 'Delete storage instance',
|
||||
deleteConfirm: 'Are you sure you want to delete "{name}"?',
|
||||
deleted: 'Deleted',
|
||||
deleteFailed: 'Failed to delete',
|
||||
},
|
||||
storage: {
|
||||
title: 'Storage Engine',
|
||||
description: 'Configure document and image storage. Set engine parameters here; knowledge bases only select which engine to use.',
|
||||
@@ -5327,6 +5370,13 @@ export default {
|
||||
storage: {
|
||||
title: 'Storage Engine',
|
||||
description: 'Select the file storage engine. This affects how uploaded documents and images within documents are stored. Parameters are configured in global settings.',
|
||||
selectDescription: 'Select the specific storage instance to bind this knowledge base to.',
|
||||
defaultTag: 'Default',
|
||||
instanceLabel: 'Storage instance',
|
||||
instanceDesc: 'The same storage type can have multiple instances with different endpoints, buckets, or credentials.',
|
||||
migrateHint: 'This knowledge base already contains files; use the storage migration flow to switch instances.',
|
||||
manageInstances: 'Manage storage instances',
|
||||
localStorage: 'Local storage',
|
||||
loading: 'Loading...',
|
||||
engineLabel: 'Storage Engine',
|
||||
engineDesc: 'Select the storage engine for this knowledge base. The corresponding engine must be configured in global settings.',
|
||||
|
||||
@@ -1229,6 +1229,49 @@ export default {
|
||||
paddleocrVlEndpointHint: 'PaddleOCR-VL 전체 서비스(pipeline) 주소를 입력하세요. /layout-parsing 접미사는 불필요합니다',
|
||||
paddleocrVlCloudTokenPlaceholder: 'PaddleOCR-VL AI Studio Token',
|
||||
},
|
||||
storageBackend: {
|
||||
description: '파일과 이미지에 사용하는 스토리지 인스턴스를 관리합니다. 같은 유형의 인스턴스를 여러 개 구성할 수 있습니다.',
|
||||
empty: '아직 구성된 스토리지 인스턴스가 없습니다',
|
||||
defaultTag: '기본',
|
||||
add: '스토리지 인스턴스 추가',
|
||||
editTitle: '스토리지 인스턴스 편집',
|
||||
createTitle: '스토리지 인스턴스 추가',
|
||||
editSubtitle: '이 스토리지 인스턴스의 연결 구성을 수정합니다.',
|
||||
createSubtitle: '파일과 이미지를 위한 새 스토리지 인스턴스를 추가합니다.',
|
||||
basicSection: '기본 정보',
|
||||
nameLabel: '이름',
|
||||
namePlaceholder: '예: 프로덕션 COS, 아카이브 COS',
|
||||
providerLabel: '스토리지 유형',
|
||||
modeLabel: '배포 모드',
|
||||
modeRemote: '원격 인스턴스',
|
||||
modeEnv: '환경 변수',
|
||||
connectionSection: '연결 설정',
|
||||
optionalPlaceholder: '선택 사항',
|
||||
advancedSection: '고급 옵션',
|
||||
pathPrefixLabel: '경로 접두사',
|
||||
useSslDesc: 'HTTPS로 MinIO 접속',
|
||||
forcePathStyleDesc: 'Path Style 사용',
|
||||
useTempBucketDesc: '임시 버킷 사용',
|
||||
tempBucketLabel: '임시 버킷',
|
||||
tempBucketPlaceholder: '선택 사항, 임시 파일용',
|
||||
tempRegionLabel: '임시 버킷 Region',
|
||||
tempRegionPlaceholder: '비워 두면 기본 Region을 사용합니다',
|
||||
testConnection: '연결 테스트',
|
||||
localStorage: '로컬 스토리지',
|
||||
setDefault: '기본으로 설정',
|
||||
edit: '편집',
|
||||
delete: '삭제',
|
||||
testSuccess: '연결에 성공했습니다',
|
||||
testFailed: '연결에 실패했습니다',
|
||||
nameRequired: '이름을 입력하세요',
|
||||
saveSuccess: '저장되었습니다',
|
||||
saveFailed: '저장하지 못했습니다',
|
||||
defaultUpdated: '기본 스토리지가 업데이트되었습니다',
|
||||
deleteTitle: '스토리지 인스턴스 삭제',
|
||||
deleteConfirm: '"{name}"을(를) 삭제하시겠습니까?',
|
||||
deleted: '삭제되었습니다',
|
||||
deleteFailed: '삭제하지 못했습니다',
|
||||
},
|
||||
storage: {
|
||||
title: '스토리지 엔진',
|
||||
description: '문서 및 이미지 저장 방식을 구성합니다. 엔진 파라미터를 설정하면 지식베이스에서 사용할 엔진만 선택합니다.',
|
||||
@@ -5354,6 +5397,13 @@ export default {
|
||||
storage: {
|
||||
title: '스토리지 엔진',
|
||||
description: '파일 스토리지 엔진을 선택합니다. 문서 업로드 및 문서 내 이미지 저장 방식에 영향을 미칩니다. 파라미터는 전역 설정에서 구성합니다.',
|
||||
selectDescription: '이 지식베이스를 바인딩할 구체적인 스토리지 인스턴스를 선택하세요.',
|
||||
defaultTag: '기본',
|
||||
instanceLabel: '스토리지 인스턴스',
|
||||
instanceDesc: '같은 스토리지 유형에 서로 다른 Endpoint, Bucket 또는 자격 증명을 가진 여러 인스턴스를 구성할 수 있습니다.',
|
||||
migrateHint: '이 지식베이스에는 이미 파일이 있어 스토리지 마이그레이션 절차를 통해서만 인스턴스를 변경할 수 있습니다.',
|
||||
manageInstances: '스토리지 인스턴스 관리',
|
||||
localStorage: '로컬 스토리지',
|
||||
loading: '로딩 중...',
|
||||
engineLabel: '스토리지 엔진',
|
||||
engineDesc: '이 지식베이스에 사용할 스토리지 엔진을 선택하세요. 전역 설정에서 해당 엔진이 구성되어 있어야 합니다.',
|
||||
|
||||
@@ -1280,6 +1280,49 @@ export default {
|
||||
paddleocrVlEndpointHint: 'Адрес полного сервиса PaddleOCR-VL (pipeline); суффикс /layout-parsing не требуется',
|
||||
paddleocrVlCloudTokenPlaceholder: 'Токен PaddleOCR-VL AI Studio'
|
||||
},
|
||||
storageBackend: {
|
||||
description: 'Управляйте экземплярами хранилища для файлов и изображений; можно настроить несколько экземпляров одного типа.',
|
||||
empty: 'Экземпляры хранилища ещё не настроены',
|
||||
defaultTag: 'По умолчанию',
|
||||
add: 'Добавить экземпляр хранилища',
|
||||
editTitle: 'Изменить экземпляр хранилища',
|
||||
createTitle: 'Добавить экземпляр хранилища',
|
||||
editSubtitle: 'Измените настройки подключения этого экземпляра хранилища.',
|
||||
createSubtitle: 'Добавьте новый экземпляр хранилища для файлов и изображений.',
|
||||
basicSection: 'Основная информация',
|
||||
nameLabel: 'Название',
|
||||
namePlaceholder: 'напр. Production COS, Archive COS',
|
||||
providerLabel: 'Тип хранилища',
|
||||
modeLabel: 'Режим развёртывания',
|
||||
modeRemote: 'Удалённый экземпляр',
|
||||
modeEnv: 'Переменные окружения',
|
||||
connectionSection: 'Настройки подключения',
|
||||
optionalPlaceholder: 'Необязательно',
|
||||
advancedSection: 'Дополнительные параметры',
|
||||
pathPrefixLabel: 'Префикс пути',
|
||||
useSslDesc: 'Доступ к MinIO по HTTPS',
|
||||
forcePathStyleDesc: 'Использовать path style',
|
||||
useTempBucketDesc: 'Использовать временный бакет',
|
||||
tempBucketLabel: 'Временный бакет',
|
||||
tempBucketPlaceholder: 'Необязательно, для временных файлов',
|
||||
tempRegionLabel: 'Регион временного бакета',
|
||||
tempRegionPlaceholder: 'Оставьте пустым, чтобы использовать основной регион',
|
||||
testConnection: 'Проверить подключение',
|
||||
localStorage: 'Локальное хранилище',
|
||||
setDefault: 'Сделать по умолчанию',
|
||||
edit: 'Изменить',
|
||||
delete: 'Удалить',
|
||||
testSuccess: 'Подключение успешно',
|
||||
testFailed: 'Не удалось подключиться',
|
||||
nameRequired: 'Введите название',
|
||||
saveSuccess: 'Успешно сохранено',
|
||||
saveFailed: 'Не удалось сохранить',
|
||||
defaultUpdated: 'Хранилище по умолчанию обновлено',
|
||||
deleteTitle: 'Удалить экземпляр хранилища',
|
||||
deleteConfirm: 'Вы уверены, что хотите удалить «{name}»?',
|
||||
deleted: 'Удалено',
|
||||
deleteFailed: 'Не удалось удалить',
|
||||
},
|
||||
storage: {
|
||||
title: 'Хранилище',
|
||||
description: 'Настройте хранение документов и изображений. Здесь задаются параметры хранилищ; в базе знаний выбирается только тип хранилища.',
|
||||
@@ -4854,6 +4897,13 @@ export default {
|
||||
storage: {
|
||||
title: 'Хранилище',
|
||||
description: 'Выберите хранилище файлов. Это влияет на способ хранения загруженных документов и изображений в документах. Параметры настраиваются в глобальных настройках.',
|
||||
selectDescription: 'Выберите конкретный экземпляр хранилища для привязки этой базы знаний.',
|
||||
defaultTag: 'По умолчанию',
|
||||
instanceLabel: 'Экземпляр хранилища',
|
||||
instanceDesc: 'Для одного типа хранилища можно настроить несколько экземпляров с разными endpoint, бакетами или учётными данными.',
|
||||
migrateHint: 'В этой базе знаний уже есть файлы; сменить экземпляр можно только через процедуру миграции хранилища.',
|
||||
manageInstances: 'Управление экземплярами хранилища',
|
||||
localStorage: 'Локальное хранилище',
|
||||
loading: 'Загрузка...',
|
||||
engineLabel: 'Хранилище',
|
||||
engineDesc: 'Выберите хранилище для этой базы знаний. Соответствующее хранилище должно быть настроено в глобальных настройках.',
|
||||
|
||||
@@ -1237,6 +1237,49 @@ export default {
|
||||
paddleocrVlEndpointHint: "填写 PaddleOCR-VL 完整服务(pipeline)地址,无需 /layout-parsing 后缀",
|
||||
paddleocrVlCloudTokenPlaceholder: "PaddleOCR-VL 飞桨星河社区 Token",
|
||||
},
|
||||
storageBackend: {
|
||||
description: "管理文件与图片使用的存储实例;同一种类型可以配置多个实例。",
|
||||
empty: "尚未配置存储实例",
|
||||
defaultTag: "默认",
|
||||
add: "添加存储实例",
|
||||
editTitle: "编辑存储实例",
|
||||
createTitle: "添加存储实例",
|
||||
editSubtitle: "修改此存储实例的连接配置。",
|
||||
createSubtitle: "为文件与图片新增一个存储实例。",
|
||||
basicSection: "基本信息",
|
||||
nameLabel: "名称",
|
||||
namePlaceholder: "例如:生产 COS、归档 COS",
|
||||
providerLabel: "存储类型",
|
||||
modeLabel: "部署模式",
|
||||
modeRemote: "远程实例",
|
||||
modeEnv: "环境变量",
|
||||
connectionSection: "连接配置",
|
||||
optionalPlaceholder: "可选",
|
||||
advancedSection: "高级选项",
|
||||
pathPrefixLabel: "路径前缀",
|
||||
useSslDesc: "使用 HTTPS 访问 MinIO",
|
||||
forcePathStyleDesc: "使用 Path Style",
|
||||
useTempBucketDesc: "使用临时桶",
|
||||
tempBucketLabel: "临时桶",
|
||||
tempBucketPlaceholder: "可选,用于临时文件",
|
||||
tempRegionLabel: "临时桶 Region",
|
||||
tempRegionPlaceholder: "留空时使用主 Region",
|
||||
testConnection: "测试连接",
|
||||
localStorage: "本地存储",
|
||||
setDefault: "设为默认",
|
||||
edit: "编辑",
|
||||
delete: "删除",
|
||||
testSuccess: "连接成功",
|
||||
testFailed: "连接失败",
|
||||
nameRequired: "请输入名称",
|
||||
saveSuccess: "保存成功",
|
||||
saveFailed: "保存失败",
|
||||
defaultUpdated: "默认存储已更新",
|
||||
deleteTitle: "删除存储实例",
|
||||
deleteConfirm: "确定删除“{name}”吗?",
|
||||
deleted: "已删除",
|
||||
deleteFailed: "删除失败",
|
||||
},
|
||||
storage: {
|
||||
title: "存储引擎",
|
||||
description: "配置文档与图片的存储方式。此处设置各引擎参数,知识库中仅选择使用哪个引擎。",
|
||||
@@ -5352,6 +5395,13 @@ export default {
|
||||
storage: {
|
||||
title: "存储引擎",
|
||||
description: "选择文件存储引擎,影响文档上传存储和文档中图片的存储方式。参数在全局设置中配置。",
|
||||
selectDescription: "选择此知识库绑定的具体存储实例。",
|
||||
defaultTag: "默认",
|
||||
instanceLabel: "存储实例",
|
||||
instanceDesc: "同一种存储类型可以配置多个不同 Endpoint、Bucket 或凭据的实例。",
|
||||
migrateHint: "知识库中已有文件,需通过存储迁移流程才能更换实例。",
|
||||
manageInstances: "管理存储实例",
|
||||
localStorage: "本地存储",
|
||||
loading: "加载中...",
|
||||
engineLabel: "存储引擎",
|
||||
engineDesc: "选择该知识库使用的存储引擎,需在全局设置中已配置对应引擎。",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div class="kb-storage-settings">
|
||||
<div class="section-header">
|
||||
<h2>{{ $t('kbSettings.storage.title') }}</h2>
|
||||
<p class="section-description">选择此知识库绑定的具体存储实例。</p>
|
||||
<p class="section-description">{{ $t('kbSettings.storage.selectDescription') }}</p>
|
||||
</div>
|
||||
<div v-if="loading" class="loading-inline"><t-loading size="small" /><span>加载中...</span></div>
|
||||
<div v-if="loading" class="loading-inline"><t-loading size="small" /><span>{{ $t('kbSettings.storage.loading') }}</span></div>
|
||||
<div v-else class="settings-group">
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label>存储实例</label>
|
||||
<p class="desc">同一种存储类型可以配置多个不同 Endpoint、Bucket 或凭据的实例。</p>
|
||||
<label>{{ $t('kbSettings.storage.instanceLabel') }}</label>
|
||||
<p class="desc">{{ $t('kbSettings.storage.instanceDesc') }}</p>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<t-select v-model="localID" style="width:100%;min-width:260px" :disabled="!!props.hasFiles" @change="handleChange">
|
||||
@@ -17,13 +17,13 @@
|
||||
<span class="select-option">
|
||||
<span>{{ backend.name }}</span>
|
||||
<t-tag theme="primary" variant="light" size="small">{{ backend.provider.toUpperCase() }}</t-tag>
|
||||
<t-tag v-if="backend.id === defaultID" variant="light" size="small">默认</t-tag>
|
||||
<t-tag v-if="backend.id === defaultID" variant="light" size="small">{{ $t('kbSettings.storage.defaultTag') }}</t-tag>
|
||||
</span>
|
||||
</t-option>
|
||||
</t-select>
|
||||
<p v-if="props.hasFiles" class="option-hint change-warning">知识库中已有文件,需通过存储迁移流程才能更换实例。</p>
|
||||
<p v-else-if="selected" class="option-hint">{{ selected.config.endpoint || selected.config.bucket_name || selected.config.path_prefix || '本地存储' }}</p>
|
||||
<a href="javascript:void(0)" class="go-settings" @click.prevent="goToSettings">管理存储实例</a>
|
||||
<p v-if="props.hasFiles" class="option-hint change-warning">{{ $t('kbSettings.storage.migrateHint') }}</p>
|
||||
<p v-else-if="selected" class="option-hint">{{ selected.config.endpoint || selected.config.bucket_name || selected.config.path_prefix || $t('kbSettings.storage.localStorage') }}</p>
|
||||
<a href="javascript:void(0)" class="go-settings" @click.prevent="goToSettings">{{ $t('kbSettings.storage.manageInstances') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="section-header__top">
|
||||
<div>
|
||||
<h2>{{ t('settings.storage.title') }}</h2>
|
||||
<p class="section-description">管理文件与图片使用的存储实例;同一种类型可以配置多个实例。</p>
|
||||
<p class="section-description">{{ t('settings.storageBackend.description') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<t-loading :loading="loading" size="small" class="backend-list-loading">
|
||||
<t-empty
|
||||
v-if="!loading && backends.length === 0 && !authStore.hasRole('admin')"
|
||||
description="尚未配置存储实例"
|
||||
:description="t('settings.storageBackend.empty')"
|
||||
/>
|
||||
<div v-else-if="!loading" class="backend-grid">
|
||||
<div
|
||||
@@ -45,7 +45,7 @@
|
||||
<div class="backend-card__body">
|
||||
<div class="backend-card__header">
|
||||
<h3 class="backend-card__title">{{ backend.name }}</h3>
|
||||
<t-tag v-if="backend.id === defaultID" theme="primary" variant="light" size="small">默认</t-tag>
|
||||
<t-tag v-if="backend.id === defaultID" theme="primary" variant="light" size="small">{{ t('settings.storageBackend.defaultTag') }}</t-tag>
|
||||
<div v-if="hasActions(backend)" class="backend-card__actions" @click.stop>
|
||||
<t-dropdown
|
||||
:options="getBackendOptions(backend)"
|
||||
@@ -79,14 +79,14 @@
|
||||
<span class="backend-card--add__icon" aria-hidden="true">
|
||||
<add-icon />
|
||||
</span>
|
||||
<span class="backend-card--add__label">添加存储实例</span>
|
||||
<span class="backend-card--add__label">{{ t('settings.storageBackend.add') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</t-loading>
|
||||
|
||||
<SettingDrawer
|
||||
v-model:visible="visible"
|
||||
:title="editing ? '编辑存储实例' : '添加存储实例'"
|
||||
:title="editing ? t('settings.storageBackend.editTitle') : t('settings.storageBackend.createTitle')"
|
||||
:class="`storage-backend-drawer storage-backend-drawer--${form.provider}`"
|
||||
:confirm-loading="saving"
|
||||
@confirm="save"
|
||||
@@ -107,18 +107,18 @@
|
||||
<span v-else class="header-icon__text">{{ providerInitial(form.provider) }}</span>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<span>{{ editing ? '修改此存储实例的连接配置。' : '为文件与图片新增一个存储实例。' }}</span>
|
||||
<span>{{ editing ? t('settings.storageBackend.editSubtitle') : t('settings.storageBackend.createSubtitle') }}</span>
|
||||
</template>
|
||||
|
||||
<t-form :data="form" layout="vertical">
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">基本信息</h4>
|
||||
<h4 class="setting-drawer__section-title">{{ t('settings.storageBackend.basicSection') }}</h4>
|
||||
<div class="form-item">
|
||||
<label class="form-label required">名称</label>
|
||||
<t-input v-model="form.name" placeholder="例如:生产 COS、归档 COS" clearable />
|
||||
<label class="form-label required">{{ t('settings.storageBackend.nameLabel') }}</label>
|
||||
<t-input v-model="form.name" :placeholder="t('settings.storageBackend.namePlaceholder')" clearable />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label class="form-label required">存储类型</label>
|
||||
<label class="form-label required">{{ t('settings.storageBackend.providerLabel') }}</label>
|
||||
<t-select v-model="form.provider" :disabled="!!editing" @change="resetConfig">
|
||||
<t-option
|
||||
v-for="provider in providers"
|
||||
@@ -129,7 +129,7 @@
|
||||
</t-select>
|
||||
</div>
|
||||
<div v-if="form.provider === 'minio'" class="form-item">
|
||||
<label class="form-label">部署模式</label>
|
||||
<label class="form-label">{{ t('settings.storageBackend.modeLabel') }}</label>
|
||||
<div class="source-options" role="radiogroup">
|
||||
<button
|
||||
type="button"
|
||||
@@ -139,7 +139,7 @@
|
||||
@click="form.config.mode = 'remote'"
|
||||
>
|
||||
<t-icon name="cloud" class="source-option__icon" />
|
||||
<span class="source-option__label">远程实例</span>
|
||||
<span class="source-option__label">{{ t('settings.storageBackend.modeRemote') }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -149,14 +149,14 @@
|
||||
@click="form.config.mode = 'docker'"
|
||||
>
|
||||
<t-icon name="server" class="source-option__icon" />
|
||||
<span class="source-option__label">环境变量</span>
|
||||
<span class="source-option__label">{{ t('settings.storageBackend.modeEnv') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">连接配置</h4>
|
||||
<h4 class="setting-drawer__section-title">{{ t('settings.storageBackend.connectionSection') }}</h4>
|
||||
<div v-if="needsEndpoint" class="form-item">
|
||||
<label class="form-label required">Endpoint</label>
|
||||
<t-input
|
||||
@@ -190,42 +190,42 @@
|
||||
</div>
|
||||
<div v-if="form.provider === 'cos'" class="form-item">
|
||||
<label class="form-label">App ID</label>
|
||||
<t-input v-model="form.config.app_id" :disabled="!!editing" placeholder="可选" clearable />
|
||||
<t-input v-model="form.config.app_id" :disabled="!!editing" :placeholder="t('settings.storageBackend.optionalPlaceholder')" clearable />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">高级选项</h4>
|
||||
<h4 class="setting-drawer__section-title">{{ t('settings.storageBackend.advancedSection') }}</h4>
|
||||
<div class="form-item">
|
||||
<label class="form-label">路径前缀</label>
|
||||
<label class="form-label">{{ t('settings.storageBackend.pathPrefixLabel') }}</label>
|
||||
<t-input v-model="form.config.path_prefix" :disabled="!!editing" placeholder="weknora/" clearable />
|
||||
</div>
|
||||
<div v-if="form.provider === 'minio'" class="form-item">
|
||||
<div class="vision-toggle">
|
||||
<t-switch v-model="form.config.use_ssl" />
|
||||
<span class="form-desc form-desc--inline">使用 HTTPS 访问 MinIO</span>
|
||||
<span class="form-desc form-desc--inline">{{ t('settings.storageBackend.useSslDesc') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="form.provider === 's3'" class="form-item">
|
||||
<div class="vision-toggle">
|
||||
<t-switch v-model="form.config.force_path_style" />
|
||||
<span class="form-desc form-desc--inline">使用 Path Style</span>
|
||||
<span class="form-desc form-desc--inline">{{ t('settings.storageBackend.forcePathStyleDesc') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="form.provider === 'oss'" class="form-item">
|
||||
<div class="vision-toggle">
|
||||
<t-switch v-model="form.config.use_temp_bucket" />
|
||||
<span class="form-desc form-desc--inline">使用临时桶</span>
|
||||
<span class="form-desc form-desc--inline">{{ t('settings.storageBackend.useTempBucketDesc') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="['cos', 'tos'].includes(form.provider) || (form.provider === 'oss' && form.config.use_temp_bucket)">
|
||||
<div class="form-item">
|
||||
<label class="form-label">临时桶</label>
|
||||
<t-input v-model="form.config.temp_bucket_name" placeholder="可选,用于临时文件" clearable />
|
||||
<label class="form-label">{{ t('settings.storageBackend.tempBucketLabel') }}</label>
|
||||
<t-input v-model="form.config.temp_bucket_name" :placeholder="t('settings.storageBackend.tempBucketPlaceholder')" clearable />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label class="form-label">临时桶 Region</label>
|
||||
<t-input v-model="form.config.temp_region" placeholder="留空时使用主 Region" clearable />
|
||||
<label class="form-label">{{ t('settings.storageBackend.tempRegionLabel') }}</label>
|
||||
<t-input v-model="form.config.temp_region" :placeholder="t('settings.storageBackend.tempRegionPlaceholder')" clearable />
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
@@ -237,7 +237,7 @@
|
||||
<t-icon v-if="!testing && rawTestResult === 'ok'" name="check-circle-filled" class="status-icon available" />
|
||||
<t-icon v-else-if="!testing && rawTestResult === 'error'" name="close-circle-filled" class="status-icon unavailable" />
|
||||
</template>
|
||||
测试连接
|
||||
{{ t('settings.storageBackend.testConnection') }}
|
||||
</t-button>
|
||||
</template>
|
||||
</SettingDrawer>
|
||||
@@ -293,7 +293,7 @@ const monoLogoStyle = computed((): Record<string, string> => {
|
||||
})
|
||||
|
||||
function backendMeta(backend: StorageBackend): string {
|
||||
return backend.config.endpoint || backend.config.bucket_name || backend.config.path_prefix || '本地存储'
|
||||
return backend.config.endpoint || backend.config.bucket_name || backend.config.path_prefix || t('settings.storageBackend.localStorage')
|
||||
}
|
||||
|
||||
const canEdit = (backend: StorageBackend) => authStore.hasRole('admin') && backend.source !== 'env'
|
||||
@@ -304,10 +304,10 @@ const hasActions = (_backend: StorageBackend) => true
|
||||
|
||||
function getBackendOptions(backend: StorageBackend) {
|
||||
const options: { content: string; value: string; theme?: string }[] = []
|
||||
options.push({ content: '测试连接', value: 'test' })
|
||||
if (canSetDefault(backend)) options.push({ content: '设为默认', value: 'default' })
|
||||
if (canEdit(backend)) options.push({ content: '编辑', value: 'edit' })
|
||||
if (canDelete(backend)) options.push({ content: '删除', value: 'delete', theme: 'error' })
|
||||
options.push({ content: t('settings.storageBackend.testConnection'), value: 'test' })
|
||||
if (canSetDefault(backend)) options.push({ content: t('settings.storageBackend.setDefault'), value: 'default' })
|
||||
if (canEdit(backend)) options.push({ content: t('settings.storageBackend.edit'), value: 'edit' })
|
||||
if (canDelete(backend)) options.push({ content: t('settings.storageBackend.delete'), value: 'delete', theme: 'error' })
|
||||
return options
|
||||
}
|
||||
|
||||
@@ -340,23 +340,23 @@ async function testRaw() {
|
||||
rawTestResult.value = null
|
||||
try {
|
||||
const r: any = editing.value ? await testStorageBackendByID(editing.value.id) : await testStorageBackend(form)
|
||||
if (r.success) { rawTestResult.value = 'ok'; MessagePlugin.success('连接成功') }
|
||||
else { rawTestResult.value = 'error'; MessagePlugin.error(r.error || '连接失败') }
|
||||
if (r.success) { rawTestResult.value = 'ok'; MessagePlugin.success(t('settings.storageBackend.testSuccess')) }
|
||||
else { rawTestResult.value = 'error'; MessagePlugin.error(r.error || t('settings.storageBackend.testFailed')) }
|
||||
} finally { testing.value = false }
|
||||
}
|
||||
async function testSaved(backend: StorageBackend) { const r: any = await testStorageBackendByID(backend.id); r.success ? MessagePlugin.success('连接成功') : MessagePlugin.error(r.error || '连接失败') }
|
||||
async function testSaved(backend: StorageBackend) { const r: any = await testStorageBackendByID(backend.id); r.success ? MessagePlugin.success(t('settings.storageBackend.testSuccess')) : MessagePlugin.error(r.error || t('settings.storageBackend.testFailed')) }
|
||||
async function save() {
|
||||
if (!form.name.trim()) { MessagePlugin.warning('请输入名称'); return }
|
||||
if (!form.name.trim()) { MessagePlugin.warning(t('settings.storageBackend.nameRequired')); return }
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { name: form.name.trim(), provider: form.provider, config: { ...form.config } }
|
||||
if (editing.value) await updateStorageBackend(editing.value.id, payload); else await createStorageBackend(payload)
|
||||
MessagePlugin.success('保存成功'); visible.value = false; await load()
|
||||
} catch (e: any) { MessagePlugin.error(e?.message || '保存失败') } finally { saving.value = false }
|
||||
MessagePlugin.success(t('settings.storageBackend.saveSuccess')); visible.value = false; await load()
|
||||
} catch (e: any) { MessagePlugin.error(e?.message || t('settings.storageBackend.saveFailed')) } finally { saving.value = false }
|
||||
}
|
||||
async function makeDefault(backend: StorageBackend) { await setDefaultStorageBackend(backend.id); defaultID.value = backend.id; MessagePlugin.success('默认存储已更新') }
|
||||
async function makeDefault(backend: StorageBackend) { await setDefaultStorageBackend(backend.id); defaultID.value = backend.id; MessagePlugin.success(t('settings.storageBackend.defaultUpdated')) }
|
||||
function remove(backend: StorageBackend) {
|
||||
const dialog = DialogPlugin.confirm({ header: '删除存储实例', body: `确定删除“${backend.name}”吗?`, onConfirm: async () => { dialog.destroy(); try { await deleteStorageBackend(backend.id); await load(); MessagePlugin.success('已删除') } catch (e: any) { MessagePlugin.error(e?.message || '删除失败') } }, onCancel: () => dialog.destroy() })
|
||||
const dialog = DialogPlugin.confirm({ header: t('settings.storageBackend.deleteTitle'), body: t('settings.storageBackend.deleteConfirm', { name: backend.name }), onConfirm: async () => { dialog.destroy(); try { await deleteStorageBackend(backend.id); await load(); MessagePlugin.success(t('settings.storageBackend.deleted')) } catch (e: any) { MessagePlugin.error(e?.message || t('settings.storageBackend.deleteFailed')) } }, onCancel: () => dialog.destroy() })
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
@@ -41,6 +41,16 @@ type storageBackendRequest struct {
|
||||
|
||||
func storageTenantID(c *gin.Context) uint64 { return c.GetUint64(types.TenantIDContextKey.String()) }
|
||||
|
||||
// List godoc
|
||||
// @Summary List storage backends
|
||||
// @Description List all storage backend instances for the current workspace, with credentials masked. The workspace default backend id is returned alongside the list.
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{} "List of storage backends and default_storage_backend_id"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends [get]
|
||||
func (h *StorageBackendHandler) List(c *gin.Context) {
|
||||
tenantID := storageTenantID(c)
|
||||
backends, err := h.repo.List(c.Request.Context(), tenantID)
|
||||
@@ -60,6 +70,18 @@ func (h *StorageBackendHandler) List(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": result, "default_storage_backend_id": defaultID})
|
||||
}
|
||||
|
||||
// Get godoc
|
||||
// @Summary Get storage backend
|
||||
// @Description Retrieve a single storage backend by ID for the current workspace. Credentials are masked.
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Param id path string true "Storage backend ID"
|
||||
// @Success 200 {object} map[string]interface{} "Storage backend details"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 404 {object} apperrors.AppError "Storage backend not found"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/{id} [get]
|
||||
func (h *StorageBackendHandler) Get(c *gin.Context) {
|
||||
backend, err := h.repo.GetByID(c.Request.Context(), storageTenantID(c), c.Param("id"))
|
||||
if err != nil {
|
||||
@@ -73,6 +95,20 @@ func (h *StorageBackendHandler) Get(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": types.NewStorageBackendResponse(backend)})
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
// @Summary Create storage backend
|
||||
// @Description Register a new object/file storage instance for the current workspace. The configuration is validated and a connectivity test is run before the backend is persisted.
|
||||
// @Tags StorageBackend
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body storageBackendRequest true "Storage backend configuration"
|
||||
// @Success 201 {object} map[string]interface{} "Created storage backend"
|
||||
// @Failure 400 {object} apperrors.AppError "Invalid request, validation, or connectivity test failure"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 409 {object} apperrors.AppError "A storage backend with this name already exists"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends [post]
|
||||
func (h *StorageBackendHandler) Create(c *gin.Context) {
|
||||
var req storageBackendRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -87,6 +123,21 @@ func (h *StorageBackendHandler) Create(c *gin.Context) {
|
||||
c.JSON(http.StatusCreated, gin.H{"success": true, "data": types.NewStorageBackendResponse(backend)})
|
||||
}
|
||||
|
||||
// Update godoc
|
||||
// @Summary Update storage backend
|
||||
// @Description Update a storage backend's mutable fields (name, credentials, status). Provider and physical location (endpoint, region, bucket, path prefix) are immutable; use storage migration to move data. Environment-sourced backends are read-only. Redacted secret placeholders preserve the stored credentials.
|
||||
// @Tags StorageBackend
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Storage backend ID"
|
||||
// @Param request body storageBackendRequest true "Updated storage backend fields"
|
||||
// @Success 200 {object} map[string]interface{} "Updated storage backend"
|
||||
// @Failure 400 {object} apperrors.AppError "Immutable field change, read-only backend, validation, or connectivity failure"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 404 {object} apperrors.AppError "Storage backend not found"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/{id} [put]
|
||||
func (h *StorageBackendHandler) Update(c *gin.Context) {
|
||||
var req storageBackendRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -106,6 +157,19 @@ func (h *StorageBackendHandler) Update(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": types.NewStorageBackendResponse(updated)})
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
// @Summary Delete storage backend
|
||||
// @Description Soft-delete a storage backend. A backend that is the workspace default, still bound to knowledge bases, environment-sourced, or a legacy alias cannot be deleted.
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Param id path string true "Storage backend ID"
|
||||
// @Success 200 {object} map[string]interface{} "Deletion success"
|
||||
// @Failure 400 {object} apperrors.AppError "Backend is default, bound, read-only, or legacy alias"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 404 {object} apperrors.AppError "Storage backend not found"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/{id} [delete]
|
||||
func (h *StorageBackendHandler) Delete(c *gin.Context) {
|
||||
if err := h.service.Delete(c.Request.Context(), storageTenantID(c), c.Param("id")); err != nil {
|
||||
c.Error(err)
|
||||
@@ -114,6 +178,19 @@ func (h *StorageBackendHandler) Delete(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// SetDefault godoc
|
||||
// @Summary Set default storage backend
|
||||
// @Description Mark a storage backend as the workspace default. Only an active backend can become the default. New knowledge bases without an explicit binding use the default.
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Param id path string true "Storage backend ID"
|
||||
// @Success 200 {object} map[string]interface{} "Default set successfully"
|
||||
// @Failure 400 {object} apperrors.AppError "Backend is not active"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 404 {object} apperrors.AppError "Storage backend not found"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/{id}/default [put]
|
||||
func (h *StorageBackendHandler) SetDefault(c *gin.Context) {
|
||||
if err := h.service.SetDefault(c.Request.Context(), storageTenantID(c), c.Param("id")); err != nil {
|
||||
c.Error(err)
|
||||
@@ -122,6 +199,19 @@ func (h *StorageBackendHandler) SetDefault(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// TestRaw godoc
|
||||
// @Summary Test storage backend with raw config
|
||||
// @Description Test connectivity for the provided storage configuration without persisting it. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).
|
||||
// @Tags StorageBackend
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body storageBackendRequest true "Storage backend configuration to test"
|
||||
// @Success 200 {object} map[string]interface{} "Connectivity test result (success, error)"
|
||||
// @Failure 400 {object} apperrors.AppError "Invalid request or validation error"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/test [post]
|
||||
func (h *StorageBackendHandler) TestRaw(c *gin.Context) {
|
||||
var req storageBackendRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -140,6 +230,18 @@ func (h *StorageBackendHandler) TestRaw(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// TestByID godoc
|
||||
// @Summary Test storage backend by ID
|
||||
// @Description Test connectivity of an existing saved storage backend using its stored credentials. Returns success=false with a sanitized error message on failure (the HTTP status stays 200).
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Param id path string true "Storage backend ID"
|
||||
// @Success 200 {object} map[string]interface{} "Connectivity test result (success, error)"
|
||||
// @Failure 401 {object} map[string]interface{} "Unauthorized"
|
||||
// @Failure 404 {object} apperrors.AppError "Storage backend not found"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/{id}/test [post]
|
||||
func (h *StorageBackendHandler) TestByID(c *gin.Context) {
|
||||
backend, err := h.repo.GetByID(c.Request.Context(), storageTenantID(c), c.Param("id"))
|
||||
if err != nil {
|
||||
@@ -157,6 +259,15 @@ func (h *StorageBackendHandler) TestByID(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// Types godoc
|
||||
// @Summary List allowed storage provider types
|
||||
// @Description Return the storage provider types allowed by STORAGE_ALLOW_LIST for UI form generation (e.g. local, minio, cos, tos, s3, oss, ks3, obs).
|
||||
// @Tags StorageBackend
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{} "List of allowed storage provider types"
|
||||
// @Security Bearer
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /storage-backends/types [get]
|
||||
func (h *StorageBackendHandler) Types(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": storageallowlist.AllowedList()})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user