diff --git a/frontend/src/api/initialization/index.ts b/frontend/src/api/initialization/index.ts index fcfe65ec..03e7e4a2 100644 --- a/frontend/src/api/initialization/index.ts +++ b/frontend/src/api/initialization/index.ts @@ -142,6 +142,7 @@ export interface KBModelConfigRequest { enabled: boolean } /** 存储引擎选择:"local" | "minio" | "cos" | "obs" 等,影响文档上传与文档内图片存储 */ + storageBackendId?: string storageProvider?: string nodeExtract: { enabled: boolean diff --git a/frontend/src/api/knowledge-base/index.ts b/frontend/src/api/knowledge-base/index.ts index 48a3fe71..b19572a3 100644 --- a/frontend/src/api/knowledge-base/index.ts +++ b/frontend/src/api/knowledge-base/index.ts @@ -65,6 +65,9 @@ export function createKnowledgeBase(data: { // store. Immutable after creation — UpdateKnowledgeBase intentionally // does not accept this field. vector_store_id?: string; + // Concrete tenant-owned storage instance. When omitted, the tenant default + // backend is bound by the server at creation time. + storage_backend_id?: string; vlm_config?: { enabled: boolean; model_id?: string; diff --git a/frontend/src/api/storage-backend.ts b/frontend/src/api/storage-backend.ts new file mode 100644 index 00000000..0c11f8da --- /dev/null +++ b/frontend/src/api/storage-backend.ts @@ -0,0 +1,45 @@ +import { get, post, put, del } from '@/utils/request' + +export interface StorageBackendConfig { + mode?: string + endpoint?: string + region?: string + access_key_id?: string + secret_access_key?: string + bucket_name?: string + path_prefix?: string + app_id?: string + use_ssl?: boolean + force_path_style?: boolean + use_temp_bucket?: boolean + temp_bucket_name?: string + temp_region?: string +} + +export interface StorageBackend { + id: string + tenant_id?: number + name: string + provider: string + config: StorageBackendConfig + source: 'user' | 'env' + status: 'active' | 'disabled' + legacy_alias?: boolean + created_at?: string + updated_at?: string +} + +export interface StorageBackendListResponse { + success: boolean + data: StorageBackend[] + default_storage_backend_id?: string | null +} + +export const listStorageBackends = (): Promise => get('/api/v1/storage-backends') +export const listStorageBackendTypes = (): Promise<{ success: boolean; data: string[] }> => get('/api/v1/storage-backends/types') +export const createStorageBackend = (data: Partial) => post('/api/v1/storage-backends', data) +export const updateStorageBackend = (id: string, data: Partial) => put(`/api/v1/storage-backends/${id}`, data) +export const deleteStorageBackend = (id: string) => del(`/api/v1/storage-backends/${id}`) +export const setDefaultStorageBackend = (id: string) => put(`/api/v1/storage-backends/${id}/default`, {}) +export const testStorageBackend = (data: Partial) => post('/api/v1/storage-backends/test', data) +export const testStorageBackendByID = (id: string) => post(`/api/v1/storage-backends/${id}/test`, {}) diff --git a/frontend/src/api/tenant/index.ts b/frontend/src/api/tenant/index.ts index d7929b47..6aca08a5 100644 --- a/frontend/src/api/tenant/index.ts +++ b/frontend/src/api/tenant/index.ts @@ -79,6 +79,7 @@ export type TenantAPIKeyCapability = | 'manage_datasources' | 'manage_channels' | 'manage_vector_stores' + | 'manage_storage_backends' | 'manage_web_search' | 'run_evaluations' | 'manage_members' diff --git a/frontend/src/assets/img/providers/color/storage/local.svg b/frontend/src/assets/img/providers/color/storage/local.svg new file mode 100644 index 00000000..d2016994 --- /dev/null +++ b/frontend/src/assets/img/providers/color/storage/local.svg @@ -0,0 +1,9 @@ + + Local storage + + + + + + + diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 82b48260..b6fc9460 100755 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -6240,6 +6240,8 @@ export default { capabilityManageChannelsHint: 'Manage agent embed channels, IM channels, and WeChat QR binding flows.', capabilityManageVectorStores: 'Manage retrieval infrastructure', capabilityManageVectorStoresHint: 'Manage vector-store configuration plus parser, document reader, and storage engine connectivity checks.', + capabilityManageStorageBackends: 'Manage storage backends', + capabilityManageStorageBackendsHint: 'Manage object/file storage backend instances (e.g. S3-compatible or local file storage): their CRUD lifecycle, connectivity tests, and the tenant default selection.', capabilityManageWebSearch: 'Manage web search', capabilityManageWebSearchHint: 'Manage web-search provider configurations, credentials, and connection tests.', capabilityRunEvaluations: 'Run evaluations', diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts index 10a7b6b1..ad1e75c9 100755 --- a/frontend/src/i18n/locales/ko-KR.ts +++ b/frontend/src/i18n/locales/ko-KR.ts @@ -6244,6 +6244,8 @@ export default { capabilityManageChannelsHint: "에이전트 임베드 채널, IM 채널 및 WeChat QR 바인딩 흐름을 관리합니다.", capabilityManageVectorStores: "검색 인프라 관리", capabilityManageVectorStoresHint: "벡터 스토어 설정과 파서, 문서 리더, 스토리지 엔진 연결 검사를 관리합니다.", + capabilityManageStorageBackends: "스토리지 백엔드 관리", + capabilityManageStorageBackendsHint: "객체/파일 스토리지 백엔드 인스턴스(예: S3 호환 또는 로컬 파일 스토리지)의 CRUD, 연결 테스트 및 테넌트 기본 스토리지 설정을 관리합니다.", capabilityManageWebSearch: "웹 검색 관리", capabilityManageWebSearchHint: "웹 검색 공급자 설정, 자격 증명 및 연결 테스트를 관리합니다.", capabilityRunEvaluations: "평가 실행", diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index 45d1a923..461924b4 100755 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -6065,6 +6065,8 @@ export default { capabilityManageChannelsHint: 'Управление embed-каналами агентов, IM-каналами и привязкой WeChat через QR-код.', capabilityManageVectorStores: 'Управление инфраструктурой поиска', capabilityManageVectorStoresHint: 'Управление векторными хранилищами и проверками парсеров, чтения документов и storage engine.', + capabilityManageStorageBackends: 'Управление хранилищами (backends)', + capabilityManageStorageBackendsHint: 'Управление экземплярами объектных/файловых хранилищ (например, S3-совместимых или локальных): их CRUD, проверки подключения и выбор хранилища по умолчанию для арендатора.', capabilityManageWebSearch: 'Управление веб-поиском', capabilityManageWebSearchHint: 'Управление провайдерами веб-поиска, учётными данными и проверками подключения.', capabilityRunEvaluations: 'Запуск оценок', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index b2630d9d..ca8fbd8f 100755 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -6265,6 +6265,8 @@ export default { capabilityManageChannelsHint: "允许管理智能体的嵌入渠道、IM 渠道和微信扫码绑定流程。", capabilityManageVectorStores: "管理检索基础设施", capabilityManageVectorStoresHint: "允许管理向量库配置,以及解析器、文档读取器和存储引擎连通性检查。", + capabilityManageStorageBackends: "管理存储后端", + capabilityManageStorageBackendsHint: "允许管理对象/文件存储后端实例(如 S3 兼容或本地文件存储)的增删改查、连通性测试及租户默认存储设置。", capabilityManageWebSearch: "管理联网搜索", capabilityManageWebSearchHint: "允许管理联网搜索供应商配置、凭据和连接测试。", capabilityRunEvaluations: "运行评测", diff --git a/frontend/src/views/integrations/ApiIntegrationSettings.vue b/frontend/src/views/integrations/ApiIntegrationSettings.vue index 9e7c5ee3..495dceba 100644 --- a/frontend/src/views/integrations/ApiIntegrationSettings.vue +++ b/frontend/src/views/integrations/ApiIntegrationSettings.vue @@ -645,6 +645,7 @@ const API_KEY_CAPABILITIES: TenantAPIKeyCapability[] = [ 'manage_datasources', 'manage_models', 'manage_vector_stores', + 'manage_storage_backends', 'manage_web_search', 'manage_channels', 'run_evaluations', @@ -699,6 +700,7 @@ const apiKeyCapabilityGroups: ApiKeyCapabilityGroup[] = [ capabilities: [ { value: 'manage_models', labelKey: 'integrations.api.capabilityManageModels', hintKey: 'integrations.api.capabilityManageModelsHint' }, { value: 'manage_vector_stores', labelKey: 'integrations.api.capabilityManageVectorStores', hintKey: 'integrations.api.capabilityManageVectorStoresHint' }, + { value: 'manage_storage_backends', labelKey: 'integrations.api.capabilityManageStorageBackends', hintKey: 'integrations.api.capabilityManageStorageBackendsHint' }, { value: 'manage_web_search', labelKey: 'integrations.api.capabilityManageWebSearch', hintKey: 'integrations.api.capabilityManageWebSearchHint' }, { value: 'manage_channels', labelKey: 'integrations.api.capabilityManageChannels', hintKey: 'integrations.api.capabilityManageChannelsHint' }, { value: 'run_evaluations', labelKey: 'integrations.api.capabilityRunEvaluations', hintKey: 'integrations.api.capabilityRunEvaluationsHint' }, @@ -786,6 +788,7 @@ function keyCapabilityLabels(key: TenantAPIKey): string[] { manage_datasources: t('integrations.api.capabilityManageDatasources'), manage_channels: t('integrations.api.capabilityManageChannels'), manage_vector_stores: t('integrations.api.capabilityManageVectorStores'), + manage_storage_backends: t('integrations.api.capabilityManageStorageBackends'), manage_web_search: t('integrations.api.capabilityManageWebSearch'), run_evaluations: t('integrations.api.capabilityRunEvaluations'), manage_members: t('integrations.api.capabilityManageMembers'), diff --git a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue index d1fa0260..5b8ecbc2 100644 --- a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue +++ b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue @@ -254,8 +254,10 @@
@@ -711,6 +713,7 @@ const initFormData = (type: 'document' | 'faq' = 'document') => { languages: [] as string[], tableMetadataInstructions: '' }, + storageBackendId: '' as string, storageProvider: '' as string, multimodalConfig: { enabled: false, @@ -832,6 +835,7 @@ const loadKBData = async () => { languages: kb.chunking_config?.languages || [], tableMetadataInstructions: kb.chunking_config?.table_metadata_instructions || '' }, + storageBackendId: (kb.storage_backend_id || '') as string, storageProvider: (kb.storage_provider_config?.provider || kb.storage_config?.provider || 'local') as string, multimodalConfig: { enabled: !!kb.vlm_config?.enabled, @@ -1025,6 +1029,12 @@ const handleStorageProviderUpdate = (value: string) => { } } +const handleStorageBackendUpdate = (value: string) => { + if (formData.value) { + formData.value.storageBackendId = value + } +} + async function loadTenantDefaultStorageProvider(force = false) { try { await editorResources.ensureStorageEngine(force) @@ -1177,8 +1187,11 @@ const buildSubmitData = () => { language: formData.value.asrConfig?.language || '' } - // 存储引擎:仅传 provider,参数从全局设置读取 - // Write to storage_provider_config (authoritative) + storage_config (legacy dual-write) + // storage_backend_id is authoritative. Keep provider projection for old clients + // and for rolling upgrades where a node has not picked up the new schema yet. + if (formData.value.storageBackendId) { + data.storage_backend_id = formData.value.storageBackendId + } const storageProvider = resolvedStorageProvider() data.storage_provider_config = { provider: storageProvider @@ -1362,6 +1375,7 @@ const doSubmit = async () => { multimodal: { enabled: !!data.vlm_config?.enabled }, + storageBackendId: formData.value?.storageBackendId || '', storageProvider: data.storage_provider_config?.provider || data.storage_config?.provider || 'local', nodeExtract: { enabled: data.extract_config?.enabled || false, diff --git a/frontend/src/views/knowledge/settings/KBStorageSettings.vue b/frontend/src/views/knowledge/settings/KBStorageSettings.vue index 97a6eee4..e65d1bdf 100644 --- a/frontend/src/views/knowledge/settings/KBStorageSettings.vue +++ b/frontend/src/views/knowledge/settings/KBStorageSettings.vue @@ -2,48 +2,28 @@

{{ $t('kbSettings.storage.title') }}

-

- {{ $t('kbSettings.storage.description') }} -

+

选择此知识库绑定的具体存储实例。

- -
- - {{ $t('kbSettings.storage.loading') }} -
- +
加载中...
- -

{{ $t('kbSettings.storage.engineDesc') }}

+ +

同一种存储类型可以配置多个不同 Endpoint、Bucket 或凭据的实例。

- - + + - {{ opt.label }} - {{ $t('kbSettings.storage.unavailable') }} - {{ $t('kbSettings.storage.notConfigured') }} - {{ $t('kbSettings.storage.unavailable') }} + {{ backend.name }} + {{ backend.provider.toUpperCase() }} + 默认 -

{{ $t('kbSettings.storage.changeWarning') }}

-

{{ selectedOption.desc }}

- {{ $t('kbSettings.storage.goGlobalSettings') }} +

知识库中已有文件,需通过存储迁移流程才能更换实例。

+

{{ selected.config.endpoint || selected.config.bucket_name || selected.config.path_prefix || '本地存储' }}

+ 管理存储实例
@@ -51,274 +31,39 @@ - diff --git a/frontend/src/views/settings/Settings.vue b/frontend/src/views/settings/Settings.vue index b3c8ff34..8de2e21a 100644 --- a/frontend/src/views/settings/Settings.vue +++ b/frontend/src/views/settings/Settings.vue @@ -196,7 +196,7 @@ import WebSearchSettings from './WebSearchSettings.vue' import ChatHistorySettings from './ChatHistorySettings.vue' import VectorStoreSettings from './VectorStoreSettings.vue' import ParserEngineSettings from './ParserEngineSettings.vue' -import StorageEngineSettings from './StorageEngineSettings.vue' +import StorageEngineSettings from './StorageBackendSettings.vue' import WeKnoraCloudSettings from './WeKnoraCloudSettings.vue' import TenantMembers from './TenantMembers.vue' import SystemSettings from '@/views/system/SystemSettings.vue' diff --git a/frontend/src/views/settings/StorageBackendSettings.vue b/frontend/src/views/settings/StorageBackendSettings.vue new file mode 100644 index 00000000..9879706c --- /dev/null +++ b/frontend/src/views/settings/StorageBackendSettings.vue @@ -0,0 +1,772 @@ + + + + + + + + diff --git a/frontend/src/views/settings/StorageEngineSettings.vue b/frontend/src/views/settings/StorageEngineSettings.vue index 481c48f8..1bae7732 100644 --- a/frontend/src/views/settings/StorageEngineSettings.vue +++ b/frontend/src/views/settings/StorageEngineSettings.vue @@ -113,7 +113,7 @@ - color logo(如 MinIO/AWS): 直接 ,保留品牌彩色 - mono logo(mask-image): 通过 ::before + currentColor 染色,颜色由 .storage-engine-drawer--{id} :deep(.setting-drawer__header-icon) 决定 - - 无 logo(local): 渲染首字母作为 monogram + - 无 logo: 渲染首字母作为 monogram -->