From 1459bd7487886e7ab9cd4722689793a06b7bccd5 Mon Sep 17 00:00:00 2001 From: Innei Date: Wed, 25 Mar 2026 18:41:32 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(kb):=20wire=20vector=20clean?= =?UTF-8?q?up=20in=20TRPC=20router,=20OpenAPI=20service,=20and=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TRPC removeKnowledgeBase: use deleteWithFiles when removeFiles=true + S3 cleanup - TRPC removeAllKnowledgeBases: use deleteAllWithFiles + S3 cleanup - OpenAPI deleteKnowledgeBase: use deleteWithFiles + S3 cleanup - Client service: default removeFiles=true when deleting knowledge base --- .../src/services/knowledge-base.service.ts | 16 ++++++++-- src/server/routers/lambda/knowledgeBase.ts | 29 ++++++++++++++++++- src/services/knowledgeBase.ts | 4 +-- src/store/library/slices/crud/action.test.ts | 2 +- src/store/library/slices/crud/action.ts | 4 +-- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/openapi/src/services/knowledge-base.service.ts b/packages/openapi/src/services/knowledge-base.service.ts index 12e57d67f9..4ea88ab4fa 100644 --- a/packages/openapi/src/services/knowledge-base.service.ts +++ b/packages/openapi/src/services/knowledge-base.service.ts @@ -4,6 +4,7 @@ import { KnowledgeBaseModel } from '@/database/models/knowledgeBase'; import type { KnowledgeBaseItem } from '@/database/schemas'; import { knowledgeBases } from '@/database/schemas'; import type { LobeChatDatabase } from '@/database/type'; +import { FileService as CoreFileService } from '@/server/services/file'; import { BaseService } from '../common/base.service'; import { processPaginationConditions } from '../helpers/pagination'; @@ -237,8 +238,19 @@ export class KnowledgeBaseService extends BaseService { throw this.createNotFoundError('Knowledge base not found or access denied'); } - // Delete knowledge base - await this.knowledgeBaseModel.delete(id); + // Delete knowledge base and its exclusive files + const result = await this.knowledgeBaseModel.deleteWithFiles(id); + + // Clean up physical storage for deleted files + if (result.deletedFiles.length > 0) { + const fileService = new CoreFileService(this.db, this.userId); + const urls = result.deletedFiles + .map((f: { url: string | null }) => f.url) + .filter(Boolean) as string[]; + if (urls.length > 0) { + await fileService.deleteFiles(urls); + } + } this.log('info', 'Knowledge base deleted successfully', { id }); diff --git a/src/server/routers/lambda/knowledgeBase.ts b/src/server/routers/lambda/knowledgeBase.ts index d2db6d9da4..b899e535cb 100644 --- a/src/server/routers/lambda/knowledgeBase.ts +++ b/src/server/routers/lambda/knowledgeBase.ts @@ -1,10 +1,12 @@ import { TRPCError } from '@trpc/server'; import { z } from 'zod'; +import { serverDBEnv } from '@/config/db'; import { KnowledgeBaseModel } from '@/database/models/knowledgeBase'; import { insertKnowledgeBasesSchema } from '@/database/schemas'; import { authedProcedure, router } from '@/libs/trpc/lambda'; import { serverDatabase } from '@/libs/trpc/lambda/middleware'; +import { FileService } from '@/server/services/file'; import { type KnowledgeBaseItem } from '@/types/knowledgeBase'; const knowledgeBaseProcedure = authedProcedure.use(serverDatabase).use(async (opts) => { @@ -68,7 +70,15 @@ export const knowledgeBaseRouter = router({ }), removeAllKnowledgeBases: knowledgeBaseProcedure.mutation(async ({ ctx }) => { - return ctx.knowledgeBaseModel.deleteAll(); + const result = await ctx.knowledgeBaseModel.deleteAllWithFiles(serverDBEnv.REMOVE_GLOBAL_FILE); + + if (result.deletedFiles.length > 0) { + const fileService = new FileService(ctx.serverDB, ctx.userId); + const urls = result.deletedFiles.map((f) => f.url).filter(Boolean) as string[]; + if (urls.length > 0) { + await fileService.deleteFiles(urls); + } + } }), removeFilesFromKnowledgeBase: knowledgeBaseProcedure @@ -80,6 +90,23 @@ export const knowledgeBaseRouter = router({ removeKnowledgeBase: knowledgeBaseProcedure .input(z.object({ id: z.string(), removeFiles: z.boolean().optional() })) .mutation(async ({ input, ctx }) => { + if (input.removeFiles) { + const result = await ctx.knowledgeBaseModel.deleteWithFiles( + input.id, + serverDBEnv.REMOVE_GLOBAL_FILE, + ); + + if (result.deletedFiles.length > 0) { + const fileService = new FileService(ctx.serverDB, ctx.userId); + const urls = result.deletedFiles.map((f) => f.url).filter(Boolean) as string[]; + if (urls.length > 0) { + await fileService.deleteFiles(urls); + } + } + + return; + } + return ctx.knowledgeBaseModel.delete(input.id); }), diff --git a/src/services/knowledgeBase.ts b/src/services/knowledgeBase.ts index df92fed362..aff5216680 100644 --- a/src/services/knowledgeBase.ts +++ b/src/services/knowledgeBase.ts @@ -18,8 +18,8 @@ class KnowledgeBaseService { return lambdaClient.knowledgeBase.updateKnowledgeBase.mutate({ id, value }); }; - deleteKnowledgeBase = async (id: string) => { - return lambdaClient.knowledgeBase.removeKnowledgeBase.mutate({ id }); + deleteKnowledgeBase = async (id: string, removeFiles: boolean = true) => { + return lambdaClient.knowledgeBase.removeKnowledgeBase.mutate({ id, removeFiles }); }; addFilesToKnowledgeBase = async (knowledgeBaseId: string, ids: string[]) => { diff --git a/src/store/library/slices/crud/action.test.ts b/src/store/library/slices/crud/action.test.ts index 081c1007fc..b85f5a9967 100644 --- a/src/store/library/slices/crud/action.test.ts +++ b/src/store/library/slices/crud/action.test.ts @@ -147,7 +147,7 @@ describe('KnowledgeBaseCrudAction', () => { await result.current.removeKnowledgeBase('kb-to-delete'); }); - expect(knowledgeBaseService.deleteKnowledgeBase).toHaveBeenCalledWith('kb-to-delete'); + expect(knowledgeBaseService.deleteKnowledgeBase).toHaveBeenCalledWith('kb-to-delete', true); expect(refreshSpy).toHaveBeenCalled(); }); diff --git a/src/store/library/slices/crud/action.ts b/src/store/library/slices/crud/action.ts index 104619fb29..576a6c871e 100644 --- a/src/store/library/slices/crud/action.ts +++ b/src/store/library/slices/crud/action.ts @@ -47,8 +47,8 @@ export class KnowledgeBaseCrudActionImpl { await mutate(FETCH_KNOWLEDGE_BASE_LIST_KEY); }; - removeKnowledgeBase = async (id: string): Promise => { - await knowledgeBaseService.deleteKnowledgeBase(id); + removeKnowledgeBase = async (id: string, removeFiles: boolean = true): Promise => { + await knowledgeBaseService.deleteKnowledgeBase(id, removeFiles); await this.#get().refreshKnowledgeBaseList(); };