bug fixes, and performance improvements, and drawer

This commit is contained in:
Zoe
2026-09-27 18:44:51 -05:00
parent d48d8163a7
commit 2b9f8d64d7
52 changed files with 4913 additions and 778 deletions
+37 -72
View File
@@ -1,95 +1,60 @@
import { db } from "~~/server/lib/db";
import { topicEvents } from "~~/server/utils/events";
import { db } from '~~/server/lib/db';
import { topicEvents } from '~~/server/utils/events';
import { createTopicSnapshotStream } from '~~/server/utils/topic-snapshot-stream';
export default defineEventHandler(async (event) => {
await protectRoute(event);
const topicId = getRouterParam(event, 'topicId')!;
const isSSE = getHeader(event, 'accept')?.includes('text/event-stream') ?? false;
const userId = event.context.user!.id as string;
const topic = await db.query.topics.findFirst({
where: {
id: topicId,
userId,
},
const isSSE = getHeader(event, 'accept')?.includes('text/event-stream') ?? false;
const readTopic = () => db.query.topics.findFirst({
where: { id: topicId, userId },
with: {
messages: {
orderBy: {
createdAt: 'asc',
},
orderBy: { createdAt: 'asc' },
with: {
parts: {
orderBy: {
createdAt: 'asc',
},
with: {
toolCall: true,
}
},
attachments: {
with: {
file: true,
}
orderBy: { createdAt: 'asc' },
with: { toolCall: true },
},
attachments: { with: { file: true } },
generation: true,
}
},
},
},
});
if (!topic) {
console.log("topic not found");
throw createError({
statusCode: 404,
statusMessage: 'Not Found',
});
}
if (isSSE === false) {
if (!isSSE) {
const topic = await readTopic();
if (!topic) {
setResponseStatus(event, 404);
return { ok: false, error: 'Topic not found' };
}
topicEvents.applyLiveText(topicId, topic.messages);
return topic;
}
// Authorize before subscribing; no topic data is exposed before this check.
const ownedTopic = await db.query.topics.findFirst({
where: { id: topicId, userId },
columns: { id: true },
});
if (!ownedTopic) {
setResponseStatus(event, 404);
return { ok: false, error: 'Topic not found' };
}
const subscription = createTopicSnapshotStream(topicId, getQuery(event).snapshot);
event.node.res.once('close', subscription.close);
const stream = await subscription.open(readTopic);
if (!stream.ok) {
setResponseStatus(event, stream.error === 'read-failed' ? 500 : 404);
return { ok: false, error: stream.error };
}
setHeader(event, 'Content-Type', 'text/event-stream');
setHeader(event, 'Cache-Control', 'no-cache');
setHeader(event, 'Connection', 'keep-alive');
setHeader(event, 'X-Accel-Buffering', 'no');
const { lastUpdate, count } = getQuery(event);
const serverLastUpdate = topic.messages.at(-1)?.updatedAt;
const serverCount = topic.messages.length;
// TODO: there is potentially a race condition here where the client could
// connect at milisecond 87, and the last database write was at 0 (there
// is a write every 100ms for some operations), if there were tokens sent
// at 20 40 and 60, those tokens are lost to the client.
let streamController: ReadableStreamDefaultController;
let interval: NodeJS.Timeout;
const stream = new ReadableStream({
start(controller) {
streamController = controller;
controller.enqueue(`:connected\n\n`);
if ((lastUpdate && count) && (lastUpdate === serverLastUpdate?.toISOString() && count === serverCount)) {
controller.enqueue(':already_synced\n\n')
} else {
controller.enqueue(`data: ${JSON.stringify({
type: 'initial_state',
payload: topic
})}\n\n`);
}
interval = setInterval(() => {
controller.enqueue(`:heartbeat\n\n`);
}, 15000);
topicEvents.subscribe(topicId, streamController);
},
cancel() {
topicEvents.unsubscribe(topicId, streamController);
clearInterval(interval);
}
});
return sendStream(event, stream);
return sendStream(event, stream.data);
});