continue scaffolding and refine the basic foundation
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import { agents } from "~~/db/schema";
|
||||
import { protectRoute } from "~~/server/utils/auth";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const db = useDrizzle();
|
||||
const userId = event.context.user.id;
|
||||
|
||||
const rows = await db.select().from(agents);
|
||||
// Only return agents for the authenticated user
|
||||
const rows = await db.select().from(agents).where(eq(agents.userId, userId));
|
||||
return rows;
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { protectRoute } from '~~/server/utils/auth';
|
||||
import { registerPendingGeneration } from '~~/server/utils/generation';
|
||||
import { createPendingGeneration } 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;
|
||||
const { topicId, messages, regeneratesFrom } = body;
|
||||
|
||||
if (!topicId || !messages) {
|
||||
throw createError({
|
||||
@@ -22,12 +22,19 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
const generationId = `gen_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
||||
try {
|
||||
const generationId = await createPendingGeneration(event.context.user.id, topicId, messages, regeneratesFrom);
|
||||
|
||||
registerPendingGeneration(event.context.user.id, generationId, topicId, messages);
|
||||
|
||||
return {
|
||||
generationId,
|
||||
status: 'pending'
|
||||
};
|
||||
return {
|
||||
generationId,
|
||||
status: 'pending',
|
||||
regeneratesFrom
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to create generation:', error);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to create generation'
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { protectRoute } from '~~/server/utils/auth';
|
||||
import { getPendingGeneration, getActiveGeneration, isGenerationActive } from '~~/server/utils/generation';
|
||||
import type { GenerationStatus } from '~~/server/types/chat';
|
||||
import { getGenerationStatus } from '~~/server/utils/generation';
|
||||
import type { GenerationStatusResponse } from '~~/server/types/chat';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
@@ -14,39 +14,30 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
const pendingGeneration = getPendingGeneration(generationId);
|
||||
const isActive = isGenerationActive(generationId);
|
||||
const activeGeneration = getActiveGeneration(generationId);
|
||||
const generation = await getGenerationStatus(generationId);
|
||||
|
||||
if (!pendingGeneration && !activeGeneration) {
|
||||
if (!generation) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Generation not found'
|
||||
});
|
||||
}
|
||||
|
||||
if (pendingGeneration) {
|
||||
const status: GenerationStatus = {
|
||||
generationId,
|
||||
status: 'pending',
|
||||
topicId: pendingGeneration.topicId
|
||||
};
|
||||
return status;
|
||||
// Verify ownership
|
||||
if (generation.userId !== event.context.user.id) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: 'Unauthorized'
|
||||
});
|
||||
}
|
||||
|
||||
if (isActive && activeGeneration) {
|
||||
const status: GenerationStatus = {
|
||||
generationId,
|
||||
status: 'active',
|
||||
content: activeGeneration.content,
|
||||
topicId: activeGeneration.topicId
|
||||
};
|
||||
return status;
|
||||
}
|
||||
|
||||
const status: GenerationStatus = {
|
||||
const status: GenerationStatusResponse = {
|
||||
generationId,
|
||||
status: 'completed'
|
||||
status: generation.status as any,
|
||||
topicId: generation.topicId,
|
||||
content: generation.content,
|
||||
error: generation.error || undefined
|
||||
};
|
||||
|
||||
return status;
|
||||
});
|
||||
@@ -1,8 +1,9 @@
|
||||
import { protectRoute } from '~~/server/utils/auth';
|
||||
import { getPendingGeneration, startGeneration, addClientToGeneration, removeClientFromGeneration, sendToClient } from '~~/server/utils/generation';
|
||||
import { startGeneration, addClientToGeneration, removeClientFromGeneration, sendToClient, isGenerationStreaming, getGenerationStatus } from '~~/server/utils/generation';
|
||||
import { eventHandler, setHeader, setResponseStatus } from 'h3';
|
||||
import { generations, messages } from '~~/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { useDrizzle } from '~~/server/utils/drizzle';
|
||||
import { generations, messages } from '~~/db/schema';
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
@@ -16,12 +17,20 @@ export default eventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
const pendingGeneration = getPendingGeneration(generationId);
|
||||
|
||||
if (pendingGeneration && pendingGeneration.expired) {
|
||||
// Fetch generation from database
|
||||
const generation = await getGenerationStatus(generationId);
|
||||
if (!generation) {
|
||||
throw createError({
|
||||
statusCode: 410,
|
||||
statusMessage: 'Generation expired - no client connected within 60 seconds'
|
||||
statusCode: 404,
|
||||
statusMessage: 'Generation not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify ownership
|
||||
if (generation.userId !== event.context.user.id) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: 'Unauthorized'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,36 +41,81 @@ export default eventHandler(async (event) => {
|
||||
|
||||
setResponseStatus(event, 200);
|
||||
|
||||
const shouldStartGeneration = pendingGeneration;
|
||||
|
||||
try {
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
addClientToGeneration(generationId, controller);
|
||||
try {
|
||||
// If generation is already completed, send the completed message
|
||||
if (generation.status === 'completed' && generation.messageId) {
|
||||
const db = useDrizzle();
|
||||
const [message] = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.id, generation.messageId));
|
||||
|
||||
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' });
|
||||
if (message) {
|
||||
sendToClient(controller, {
|
||||
type: 'complete',
|
||||
data: message
|
||||
});
|
||||
}
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// If generation failed, send the error
|
||||
if (generation.status === 'failed') {
|
||||
sendToClient(controller, {
|
||||
type: 'error',
|
||||
data: { error: generation.error || 'Generation failed' }
|
||||
});
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// If already streaming, just add this client
|
||||
if (isGenerationStreaming(generationId)) {
|
||||
addClientToGeneration(generationId, controller);
|
||||
} else {
|
||||
// Start generation if in pending status
|
||||
if (generation.status === 'pending') {
|
||||
// Fetch the original messages context (stored in topic messages)
|
||||
const db = useDrizzle();
|
||||
const topicMessages = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.topicId, generation.topicId));
|
||||
|
||||
const chatMessages = topicMessages.map(m => ({
|
||||
type: m.isUser ? 'user' as const : ('agent' as const),
|
||||
message: m.content
|
||||
}));
|
||||
|
||||
addClientToGeneration(generationId, controller);
|
||||
await startGeneration(generationId, generation.userId, generation.topicId, chatMessages, controller);
|
||||
}
|
||||
}
|
||||
|
||||
event.node.req.on('close', () => {
|
||||
removeClientFromGeneration(generationId, controller);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Stream start error:', error);
|
||||
sendToClient(controller, {
|
||||
type: 'complete',
|
||||
data: message[0].content
|
||||
type: 'error',
|
||||
data: { error: 'Stream initialization failed' }
|
||||
});
|
||||
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
|
||||
console.error('Stream error:', error);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Stream error'
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,13 +1,26 @@
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { and, asc, eq } from "drizzle-orm";
|
||||
import { messages, topics } from "~~/db/schema";
|
||||
import { protectRoute } from "~~/server/utils/auth";
|
||||
import type { Message, Topic } from '~~/types'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const db = useDrizzle();
|
||||
const userId = event.context.user.id;
|
||||
const topicId = getRouterParam(event, 'id');
|
||||
|
||||
const rows = await db.select().from(topics).where(and(eq(topics.userId, event.context.user.id), eq(topics.id, getRouterParam(event, 'id')!)));
|
||||
if (!topicId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Topic ID is required'
|
||||
});
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(topics)
|
||||
.where(and(eq(topics.userId, userId), eq(topics.id, topicId)));
|
||||
|
||||
if (rows.length === 0) {
|
||||
throw createError({
|
||||
@@ -17,7 +30,13 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// Fetch messages for this topic, ordered chronologically
|
||||
topic.messages = await db
|
||||
.select()
|
||||
.from(messages)
|
||||
.where(eq(messages.topicId, topic.id))
|
||||
.orderBy(asc(messages.createdAt));
|
||||
|
||||
return topic;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { messages, topics } from "~~/db/schema";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const db = useDrizzle();
|
||||
|
||||
const { id } = event.context.params!;
|
||||
|
||||
const [topic] = await db.select().from(topics).where(eq(topics.id, id));
|
||||
if (topic === undefined || topic.userId !== event.context.user.id) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Topic not found' });
|
||||
}
|
||||
|
||||
const { content } = await readBody(event);
|
||||
if (!content) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'No content provided' });
|
||||
}
|
||||
|
||||
const [message] = await db.insert(messages).values({
|
||||
topicId: topic.id,
|
||||
userId: event.context.user.id,
|
||||
content,
|
||||
isUser: true,
|
||||
}).returning();
|
||||
|
||||
return message;
|
||||
});
|
||||
@@ -1,11 +1,14 @@
|
||||
import { topics } from "~~/db/schema";
|
||||
import { protectRoute } from "~~/server/utils/auth";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const db = useDrizzle();
|
||||
const userId = event.context.user.id;
|
||||
|
||||
const rows = await db.select().from(topics);
|
||||
// Only return topics for the authenticated user
|
||||
const rows = await db.select().from(topics).where(eq(topics.userId, userId));
|
||||
return rows;
|
||||
});
|
||||
@@ -1,16 +1,30 @@
|
||||
import { topics } from "~~/db/schema";
|
||||
import { topics, agents } from "~~/db/schema";
|
||||
import { protectRoute } from "~~/server/utils/auth";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const db = useDrizzle();
|
||||
const userId = event.context.user.id;
|
||||
|
||||
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();
|
||||
|
||||
// Verify the agent belongs to this user
|
||||
const [agent] = await db.select().from(agents).where(eq(agents.id, agentId));
|
||||
if (!agent || agent.userId !== userId) {
|
||||
throw createError({ statusCode: 403, statusMessage: 'Agent not found or unauthorized' });
|
||||
}
|
||||
|
||||
const [inserted] = await db
|
||||
.insert(topics)
|
||||
.values({ userId, agentId, name })
|
||||
.returning();
|
||||
|
||||
return inserted;
|
||||
});
|
||||
Reference in New Issue
Block a user