import { requireAuth, type JWTContents } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { getPersona } from "@/lib/personas"; import { getUserTier, DAILY_LIMITS } from "@/lib/tiers"; import { generateAIReply } from "@/lib/ai-server"; export const runtime = "nodejs"; export async function POST(request: Request) { try { const user = await requireAuth(request); if (!user) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } const body = await request.json(); const { messages, personaId } = body as { messages?: { role: string; content: string }[]; personaId?: string; }; if (!messages || !Array.isArray(messages) || messages.length === 0) { return Response.json({ error: "messages array is required" }, { status: 400 }); } if (!personaId || typeof personaId !== "string") { return Response.json({ error: "personaId is required" }, { status: 400 }); } // Verify persona exists const persona = getPersona(personaId); if (!persona) { return Response.json({ error: "Invalid persona" }, { status: 400 }); } // Get user from DB to check limits const dbUser = await prisma.user.findUnique({ where: { id: user.userId }, }); if (!dbUser) { return Response.json({ error: "User not found" }, { status: 404 }); } const tier = getUserTier(dbUser.isPremium, dbUser.isPro); const limits = DAILY_LIMITS[tier]; // Check persona access for this tier if (!limits.personas.includes(personaId)) { const tierName = tier.charAt(0).toUpperCase() + tier.slice(1); return Response.json( { error: `This persona requires ${tier === "free" ? "Premium" : "Pro"} subscription`, tier, requiredTier: tier === "free" ? "premium" : "pro", }, { status: 403 } ); } // Check daily message limit const now = new Date(); let dailyMsgCount = dbUser.dailyMsgCount; let dailyMsgResetAt = dbUser.dailyMsgResetAt; // Reset count if a new day if (!dailyMsgResetAt || now.getTime() - dailyMsgResetAt.getTime() > 24 * 60 * 60 * 1000) { dailyMsgCount = 0; dailyMsgResetAt = now; } if (limits.msgs !== Infinity && dailyMsgCount >= limits.msgs) { return Response.json( { error: "Daily message limit reached", count: dailyMsgCount, limit: limits.msgs, resetAt: dailyMsgResetAt.toISOString(), }, { status: 429 } ); } // Determine the user's last message for context const lastUserMessage = messages[messages.length - 1]?.content || ""; // Get recent history from DB for context (up to 10 recent messages) const recentHistory = await prisma.chatHistory.findMany({ where: { userId: user.userId }, orderBy: { createdAt: "desc" }, take: 10, }); const historyContext = recentHistory .reverse() .map((h) => ({ role: h.role, content: h.content })); // Save user message(s) to ChatHistory for (const msg of messages) { if (msg.role === "user") { await prisma.chatHistory.create({ data: { userId: user.userId, role: "user", content: msg.content, metadata: JSON.stringify({ personaId }), }, }); } } // Generate AI response (tries LLM first, falls back to keyword rules) const aiContent = await generateAIReply(personaId, lastUserMessage, historyContext); // Save AI response to ChatHistory const aiMessage = await prisma.chatHistory.create({ data: { userId: user.userId, role: "assistant", content: aiContent, metadata: JSON.stringify({ personaId }), }, }); // Update daily message count const newCount = dailyMsgCount + messages.filter((m) => m.role === "user").length; await prisma.user.update({ where: { id: user.userId }, data: { dailyMsgCount: newCount, dailyMsgResetAt: dailyMsgResetAt, }, }); let updatedUser: any = { dailyMsgCount: newCount }; if (newCount >= limits.msgs - 2 && limits.msgs !== Infinity) { updatedUser.limitWarning = true; updatedUser.limitRemaining = limits.msgs - newCount; } return Response.json({ message: { role: "assistant", content: aiContent, createdAt: aiMessage.createdAt.toISOString(), id: aiMessage.id, }, daily: { count: newCount, limit: limits.msgs, resetAt: dailyMsgResetAt.toISOString(), }, user: updatedUser, }); } catch (error) { console.error("Chat send error:", error); return Response.json({ error: "Internal server error" }, { status: 500 }); } }