🐛 fix(kb): wire vector cleanup in TRPC router, OpenAPI service, and client

- 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
This commit is contained in:
Innei
2026-03-25 18:41:32 +08:00
parent e6445036a3
commit 1459bd7487
5 changed files with 47 additions and 8 deletions

View File

@@ -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 });

View File

@@ -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);
}),

View File

@@ -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[]) => {

View File

@@ -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();
});

View File

@@ -47,8 +47,8 @@ export class KnowledgeBaseCrudActionImpl {
await mutate(FETCH_KNOWLEDGE_BASE_LIST_KEY);
};
removeKnowledgeBase = async (id: string): Promise<void> => {
await knowledgeBaseService.deleteKnowledgeBase(id);
removeKnowledgeBase = async (id: string, removeFiles: boolean = true): Promise<void> => {
await knowledgeBaseService.deleteKnowledgeBase(id, removeFiles);
await this.#get().refreshKnowledgeBaseList();
};