import { sql } from "drizzle-orm"; import { messages } from "~~/drizzle/schema"; import { db } from "~~/server/lib/db"; import { topicEvents } from "~~/server/utils/events"; async function deeplyDeleteMessage(messageId: string, topicId: string, userId: string) { const children = await db.query.messages.findMany({ where: { parentMessageId: messageId, userId, } } as any); for (const child of children) { await deeplyDeleteMessage(child.id, topicId, userId); } await db.delete(messages).where(sql`${messages.id} = ${messageId} AND ${messages.userId} = ${userId}`); topicEvents.emit(topicId, { type: 'MESSAGE_DELETED', payload: { id: messageId }, }); } export default defineEventHandler(async (event) => { await protectRoute(event); const userId = event.context.user!.id; const messageId = getRouterParam(event, 'messageId')!; const message = await db.query.messages.findFirst({ where: { id: messageId, userId, } } as any); if (!message) { throw createError({ statusCode: 400, statusMessage: 'Invalid message', }); } const topicId = message.topicId; const allTopicMessages = await db.query.messages.findMany({ where: { topicId, userId, }, orderBy: { createdAt: 'asc', }, }); const messagesMap = new Map(allTopicMessages.map(m => [m.id, { ...m, children: [] as typeof m[] }])); for (const msg of messagesMap.values()) { if (msg.parentMessageId && messagesMap.has(msg.parentMessageId)) { messagesMap.get(msg.parentMessageId)!.children.push(msg); } } const targetMessage = messagesMap.get(messageId); if (!targetMessage) { throw createError({ statusCode: 400, statusMessage: 'Invalid message', }); } if (targetMessage.role === 'user') { await deeplyDeleteMessage(targetMessage.id, topicId, userId); return { ok: true }; } if (targetMessage.parentMessageId && messagesMap.has(targetMessage.parentMessageId)) { const parent = messagesMap.get(targetMessage.parentMessageId)!; await deeplyDeleteMessage(targetMessage.id, topicId, userId); const remainingChildren = parent.children.filter((child) => { return child.id !== targetMessage.id; }); if (parent.deleted === true && remainingChildren.length === 0) { await deeplyDeleteMessage(parent.id, topicId, userId); } else if (parent.activeChildId === targetMessage.id) { const nextActiveChildId = remainingChildren.length > 0 ? (parent.deleted ? remainingChildren[0]!.id : remainingChildren[remainingChildren.length - 1]!.id) : null; await db.update(messages).set({ activeChildId: nextActiveChildId }).where(sql`${messages.id} = ${parent.id} AND ${messages.userId} = ${userId}`); topicEvents.emit(topicId, { type: 'MESSAGE_UPDATED', payload: { ...parent, activeChildId: nextActiveChildId }, }); } return { ok: true }; } if (targetMessage.deleted === true) { const childMessage = targetMessage.activeChildId ? targetMessage.children.find((c) => { return c.id === targetMessage.activeChildId; }) : targetMessage.children[0]; if (childMessage) { await deeplyDeleteMessage(childMessage.id, topicId, userId); const remainingChildren = targetMessage.children.filter((child) => { return child.id !== childMessage.id; }); if (remainingChildren.length === 0) { await deeplyDeleteMessage(targetMessage.id, topicId, userId); } else if (targetMessage.activeChildId === childMessage.id) { const nextActiveChildId = remainingChildren[0]!.id; await db.update(messages).set({ activeChildId: nextActiveChildId }).where(sql`${messages.id} = ${targetMessage.id} AND ${messages.userId} = ${userId}`); topicEvents.emit(topicId, { type: 'MESSAGE_UPDATED', payload: { ...targetMessage, activeChildId: nextActiveChildId }, }); } } else { await deeplyDeleteMessage(targetMessage.id, topicId, userId); } return { ok: true }; } if ( targetMessage.activeChildId && targetMessage.children.length > 0 ) { const childMessage = targetMessage.children.find((c) => { return c.id === targetMessage.activeChildId; }); if (childMessage) { await deeplyDeleteMessage(childMessage.id, topicId, userId); const remainingChildren = targetMessage.children.filter((child) => { return child.id !== childMessage.id; }); const nextActiveChildId = remainingChildren.length > 0 ? remainingChildren[remainingChildren.length - 1]!.id : null; await db.update(messages).set({ activeChildId: nextActiveChildId }).where(sql`${messages.id} = ${targetMessage.id} AND ${messages.userId} = ${userId}`); topicEvents.emit(topicId, { type: 'MESSAGE_UPDATED', payload: { ...targetMessage, activeChildId: nextActiveChildId }, }); return { ok: true }; } } if (targetMessage.children.length === 0) { await deeplyDeleteMessage(targetMessage.id, topicId, userId); return { ok: true }; } const nextActiveChildId = targetMessage.children[0]?.id ?? null; await db.update(messages).set({ deleted: true, activeChildId: nextActiveChildId, }).where(sql`${messages.id} = ${targetMessage.id} AND ${messages.userId} = ${userId}`); topicEvents.emit(topicId, { type: 'MESSAGE_UPDATED', payload: { ...targetMessage, deleted: true, activeChildId: nextActiveChildId }, }); return { ok: true }; });