diff --git a/src/routes/ai.ts b/src/routes/ai.ts index bdcbd04..eb77571 100644 --- a/src/routes/ai.ts +++ b/src/routes/ai.ts @@ -1,6 +1,6 @@ import { Router, Request, Response } from 'express'; import { z } from 'zod'; -import prisma from '../lib/prisma'; +import db from '../lib/db'; import { authenticateToken, AuthenticatedRequest } from '../middleware/auth'; import { asyncHandler } from '../middleware/errorHandler'; @@ -10,6 +10,15 @@ const parseMessageSchema = z.object({ message: z.string().min(1, 'Message is required').max(1000), }); +// AI parsed event validation schema +const parsedEventSchema = z.object({ + type: z.enum(['anniversary', 'reminder']), + title: z.string().min(1).max(200), + date: z.string().datetime(), + is_lunar: z.boolean(), + repeat_type: z.enum(['yearly', 'monthly', 'none']), +}); + // All routes require authentication router.use(authenticateToken); @@ -23,19 +32,17 @@ router.post( const aiResponse = await callDeepSeek(message); // Save conversation - const conversation = await prisma.aICoachConversation.create({ - data: { - user_id: req.user!.userId, - message, - response: aiResponse.response, - parsed_data: aiResponse.parsed, - }, + const convId = crypto.randomUUID(); + await db.execute({ + sql: `INSERT INTO ai_coach_conversations (id, user_id, message, response, parsed_data, created_at) + VALUES (?, ?, ?, ?, ?, datetime('now'))`, + args: [convId, req.user!.userId, message, aiResponse.response, JSON.stringify(aiResponse.parsed)], }); res.json({ parsed: aiResponse.parsed, response: aiResponse.response, - conversation_id: conversation.id, + conversation_id: convId, }); }) ); @@ -97,11 +104,19 @@ async function callDeepSeek(message: string): Promise<{ // Extract JSON from response const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { - const parsed = JSON.parse(jsonMatch[0]); - return { - parsed: parsed.parsed || parsed, - response: parsed.response || content, - }; + try { + const rawParsed = JSON.parse(jsonMatch[0]); + const parsed = rawParsed.parsed || rawParsed; + // Validate parsed data with Zod schema + const validated = parsedEventSchema.parse(parsed); + return { + parsed: validated, + response: parsed.response || content, + }; + } catch (e) { + // Schema validation failed, use mock response + return mockParseResponse(message); + } } return mockParseResponse(message);