initial commit

This commit is contained in:
Zoe
2026-01-11 05:04:29 -06:00
commit 0877cc10bd
65 changed files with 5009 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import { eq } from "drizzle-orm";
import { agents } from "~~/db/schema";
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const { id } = event.context.params!;
const [row] = await db.select().from(agents).where(eq(agents.id, id));
if (row === undefined || row.userId !== event.context.user.id) {
throw createError({ statusCode: 404, statusMessage: 'Agent not found' });
}
const { name, systemPrompt, imageUrl } = await readBody(event);
const updateObject: Partial<typeof row> = {};
if (name !== undefined) updateObject.name = name;
if (systemPrompt !== undefined) updateObject.systemPrompt = systemPrompt;
if (imageUrl !== undefined) updateObject.imageUrl = imageUrl;
if (Object.keys(updateObject).length === 0) {
throw createError({ statusCode: 400, statusMessage: 'No update data provided' });
}
const [agent] = await db.update(agents).set(updateObject).where(eq(agents.id, id)).returning();
return agent;
});
+11
View File
@@ -0,0 +1,11 @@
import { agents } from "~~/db/schema";
import { protectRoute } from "~~/server/utils/auth";
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const rows = await db.select().from(agents);
return rows;
});
+17
View File
@@ -0,0 +1,17 @@
import { agents } from "~~/db/schema";
import { protectRoute } from "~~/server/utils/auth";
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const { name, systemPrompt, imageUrl } = await readBody(event);
const userId = event.context.user.id;
if (!name || !systemPrompt) {
throw createError({ statusCode: 400, statusMessage: 'Missing required fields' });
}
const [inserted] = await db.insert(agents).values({ name, userId, systemPrompt, imageUrl }).returning();
return inserted;
});
+5
View File
@@ -0,0 +1,5 @@
import { auth } from "~~/lib/auth";
export default defineEventHandler((event) => {
return auth.handler(toWebRequest(event));
});
+33
View File
@@ -0,0 +1,33 @@
import { protectRoute } from '~~/server/utils/auth';
import { registerPendingGeneration } from '~~/server/utils/generation';
import type { GenerateRequestBody } from '~~/server/types/chat';
export default defineEventHandler(async (event) => {
await protectRoute(event);
const body = await readBody(event) as GenerateRequestBody;
const { topicId, messages } = body;
if (!topicId || !messages) {
throw createError({
statusCode: 400,
statusMessage: 'Missing required fields: topicId and messages'
});
}
if (messages.length === 0) {
throw createError({
statusCode: 400,
statusMessage: 'Messages array cannot be empty'
});
}
const generationId = `gen_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
registerPendingGeneration(event.context.user.id, generationId, topicId, messages);
return {
generationId,
status: 'pending'
};
});
+52
View File
@@ -0,0 +1,52 @@
import { protectRoute } from '~~/server/utils/auth';
import { getPendingGeneration, getActiveGeneration, isGenerationActive } from '~~/server/utils/generation';
import type { GenerationStatus } from '~~/server/types/chat';
export default defineEventHandler(async (event) => {
await protectRoute(event);
const generationId = getRouterParam(event, 'id');
if (!generationId) {
throw createError({
statusCode: 400,
statusMessage: 'Missing generation ID'
});
}
const pendingGeneration = getPendingGeneration(generationId);
const isActive = isGenerationActive(generationId);
const activeGeneration = getActiveGeneration(generationId);
if (!pendingGeneration && !activeGeneration) {
throw createError({
statusCode: 404,
statusMessage: 'Generation not found'
});
}
if (pendingGeneration) {
const status: GenerationStatus = {
generationId,
status: 'pending',
topicId: pendingGeneration.topicId
};
return status;
}
if (isActive && activeGeneration) {
const status: GenerationStatus = {
generationId,
status: 'active',
content: activeGeneration.content,
topicId: activeGeneration.topicId
};
return status;
}
const status: GenerationStatus = {
generationId,
status: 'completed'
};
return status;
});
+67
View File
@@ -0,0 +1,67 @@
import { protectRoute } from '~~/server/utils/auth';
import { getPendingGeneration, startGeneration, addClientToGeneration, removeClientFromGeneration, sendToClient } from '~~/server/utils/generation';
import { eventHandler, setHeader, setResponseStatus } from 'h3';
import { generations, messages } from '~~/db/schema';
import { eq } from 'drizzle-orm';
export default eventHandler(async (event) => {
await protectRoute(event);
const generationId = getRouterParam(event, 'id');
if (!generationId) {
throw createError({
statusCode: 400,
statusMessage: 'Missing generation ID'
});
}
const pendingGeneration = getPendingGeneration(generationId);
if (pendingGeneration && pendingGeneration.expired) {
throw createError({
statusCode: 410,
statusMessage: 'Generation expired - no client connected within 60 seconds'
});
}
setHeader(event, 'Content-Type', 'text/event-stream');
setHeader(event, 'Cache-Control', 'no-cache');
setHeader(event, 'Connection', 'keep-alive');
setHeader(event, 'X-Accel-Buffering', 'no');
setResponseStatus(event, 200);
const shouldStartGeneration = pendingGeneration;
try {
const stream = new ReadableStream({
async start(controller) {
addClientToGeneration(generationId, controller);
if (shouldStartGeneration) {
startGeneration(generationId, controller);
} else {
const generation = await useDrizzle().select().from(generations).where(eq(generations.id, getRouterParam(event, 'id')!))
if (!generation) throw createError({ statusCode: 404, statusMessage: 'Generation not found' });
const message = await useDrizzle().select().from(messages).where(eq(messages.id, generation[0].messageId!))
if (!message) throw createError({ statusCode: 404, statusMessage: 'Message not found' });
sendToClient(controller, {
type: 'complete',
data: message[0].content
});
controller.close();
}
event.node.req.on('close', () => {
removeClientFromGeneration(generationId, controller);
});
}
});
return sendStream(event, stream);
} catch (error) {
// assume it failed because the generation is complete so try to send the message
}
});
+23
View File
@@ -0,0 +1,23 @@
import { and, desc, eq } from "drizzle-orm";
import { messages, topics } from "~~/db/schema";
import type { Message, Topic } from '~~/types'
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const rows = await db.select().from(topics).where(and(eq(topics.userId, event.context.user.id), eq(topics.id, getRouterParam(event, 'id')!)));
if (rows.length === 0) {
throw createError({
statusCode: 404,
statusMessage: 'Topic not found'
});
}
const topic = rows[0] as Topic & { messages: Message[] };
topic.messages = await db.select().from(messages).where(eq(messages.topicId, topic.id)).orderBy(desc(messages.createdAt));
return topic;
});
+11
View File
@@ -0,0 +1,11 @@
import { topics } from "~~/db/schema";
import { protectRoute } from "~~/server/utils/auth";
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const rows = await db.select().from(topics);
return rows;
});
+16
View File
@@ -0,0 +1,16 @@
import { topics } from "~~/db/schema";
import { protectRoute } from "~~/server/utils/auth";
export default defineEventHandler(async (event) => {
await protectRoute(event);
const db = useDrizzle();
const body = await readBody(event);
const { agentId, name } = body;
if (!agentId || !name) {
throw createError({ statusCode: 400, statusMessage: 'Missing required fields' });
}
const [inserted] = await db.insert(topics).values({ userId: event.context.user.id, agentId, name }).returning();
return inserted;
});