@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
94 lines (80 loc) • 3.48 kB
text/typescript
import { z } from 'zod';
import { GenerationTopicModel } from '@/database/models/generationTopic';
import { GenerationTopicItem } from '@/database/schemas/generation';
import { authedProcedure, router } from '@/libs/trpc/lambda';
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
import { FileService } from '@/server/services/file';
import { GenerationService } from '@/server/services/generation';
const generationTopicProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
const { ctx } = opts;
return opts.next({
ctx: {
fileService: new FileService(ctx.serverDB, ctx.userId),
generationService: new GenerationService(ctx.serverDB, ctx.userId),
generationTopicModel: new GenerationTopicModel(ctx.serverDB, ctx.userId),
},
});
});
// Define input schemas
const updateTopicSchema = z.object({
id: z.string(),
value: z.object({
coverUrl: z.string().nullable().optional(),
title: z.string().nullable().optional(),
}),
});
const updateTopicCoverSchema = z.object({
coverUrl: z.string(),
id: z.string(),
});
export const generationTopicRouter = router({
createTopic: generationTopicProcedure.input(z.void()).mutation(async ({ ctx }) => {
const data = await ctx.generationTopicModel.create('');
return data.id;
}),
deleteTopic: generationTopicProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
// 1. Delete database records and get file URLs to clean
const result = await ctx.generationTopicModel.delete(input.id);
// If topic not found, throw an error instead of returning undefined
if (!result) {
return;
}
const { deletedTopic, filesToDelete } = result;
// 2. Clean up all files from S3 (cover image and thumbnails)
// Note: Even if file deletion fails, we consider the topic deletion successful
// since the database record has been removed and users won't see the topic anymore
if (filesToDelete.length > 0) {
try {
await ctx.fileService.deleteFiles(filesToDelete);
} catch (error) {
// Log the error but don't throw - file cleanup failure shouldn't affect
// the user experience since the database operation succeeded
console.error('Failed to delete files from S3:', error);
}
}
return deletedTopic;
}),
getAllGenerationTopics: generationTopicProcedure.query(async ({ ctx }) => {
return ctx.generationTopicModel.queryAll();
}),
updateTopic: generationTopicProcedure
.input(updateTopicSchema)
.mutation(async ({ ctx, input }) => {
return ctx.generationTopicModel.update(input.id, input.value as Partial<GenerationTopicItem>);
}),
updateTopicCover: generationTopicProcedure
.input(updateTopicCoverSchema)
.mutation(async ({ ctx, input }) => {
// Process the cover image and get key
const newCoverKey = await ctx.generationService.createCoverFromUrl(input.coverUrl);
// Update the topic with the new cover key
return ctx.generationTopicModel.update(input.id, { coverUrl: newCoverKey });
}),
});
export type GenerationTopicRouter = typeof generationTopicRouter;
// Export input types for client/server service consistency
export type UpdateTopicInput = z.infer<typeof updateTopicSchema>;
export type UpdateTopicValue = UpdateTopicInput['value'];
export type UpdateTopicCoverInput = z.infer<typeof updateTopicCoverSchema>;