Files
veridian/server/api/messages/[messageId]/index.delete.ts
T
zoeissleeping 103dedd9cc refactor: replace focusedIndex with activeChildId
- Replace focusedIndex integer with activeChildId string in messages schema
- Add migration to convert existing data and drop focused_index column
- Add  hooks for providers.updatedAt and messageParts.lastUpdatedAt
- Add file size column to files table
- Update message navigation to use child ID-based lookup
- Simplify delete logic by removing focusedIndex math
- Fix file delete endpoint filename ([id[ -> [id])
2026-05-11 17:41:57 -05:00

126 lines
3.6 KiB
TypeScript

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 rootMessage = messagesMap.get(messageId);
if (!rootMessage) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid message',
});
}
if (rootMessage.role === 'user') {
await deeplyDeleteMessage(rootMessage.id, topicId, userId);
return { ok: true };
}
if (rootMessage.deleted === true) {
const childMessage = rootMessage.activeChildId
? rootMessage.children.find(c => c.id === rootMessage.activeChildId)
: rootMessage.children[0];
if (!childMessage) {
console.error('Message not found');
return { ok: false };
}
await deeplyDeleteMessage(childMessage.id, topicId, userId);
const remainingChildren = rootMessage.children.filter(child => child!.id !== childMessage.id);
if (remainingChildren.length === 0) {
await deeplyDeleteMessage(rootMessage.id, topicId, userId);
}
return { ok: true };
}
if (
rootMessage.activeChildId
&& rootMessage.children.length > 0
) {
const childMessage = rootMessage.children.find(c => c.id === rootMessage.activeChildId);
if (!childMessage) {
console.error('Message not found');
return { ok: false };
}
await deeplyDeleteMessage(childMessage.id, topicId, userId);
return { ok: true };
}
if (rootMessage.children.length === 0) {
await deeplyDeleteMessage(rootMessage.id, topicId, userId);
return { ok: true };
}
await db.update(messages).set({ deleted: true }).where(sql`${messages.id} = ${rootMessage.id} AND ${messages.userId} = ${userId}`);
topicEvents.emit(topicId, {
type: 'MESSAGE_UPDATED',
payload: { ...rootMessage, deleted: true },
});
return { ok: true };
});