import { Router } from 'express'; import prisma from '../database.js'; import { requireAuth } from '../middlewares/authMiddleware.js'; // Import the middleware import { askGeminiChat } from '../utils/gemini.js'; import { getUserById } from './auth.js'; const router = Router(); // Apply the authentication middleware to all chat routes router.use(requireAuth); // POST /api/chat/threads - Create a new chat thread (optionally with a first message) router.post('/threads', async(req, res) => { const { content } = req.body; // Content is now optional // If content is provided, validate it if (content && (typeof content !== 'string' || content.trim().length === 0)) { return res.status(400).json({ error: 'Message content cannot be empty if provided.' }); } try { const createData = {}; if (content) { // If content exists, create the thread with the first message createData.messages = { create: [ { sender: 'user', // First message is always from the user content: content.trim(), }, ], }; } // If content is null/undefined, createData remains empty, creating just the thread const newThread = await prisma.chatThread.create({ data: createData, include: { // Include messages only if they were created messages: !!content, }, }); if(content) { const user = await getUserById(req.session.loggedInUserId); if (!user) { req.session.destroy(err => {}); return res.status(401).json({ status: 'unauthenticated' }); } await askGeminiChat(newThread.id, `[${user.fullName || user.username}] ${content}`); } // Respond with the new thread ID and messages (if any) res.status(201).json({ threadId: newThread.id, // Ensure messages array is empty if no content was provided messages: newThread.messages ? newThread.messages.map(msg => ({ ...msg, createdAt: msg.createdAt.toISOString() })) : [] }); } catch (error) { console.error('Error creating chat thread:', error); res.status(500).json({ error: 'Failed to create chat thread.' }); } }); // GET /api/chat/threads/:threadId/messages - Get messages for a specific thread router.get('/threads/:threadId/messages', async(req, res) => { const { threadId } = req.params; try { const messages = await prisma.chatMessage.findMany({ where: { threadId: threadId, }, orderBy: { createdAt: 'asc', // Get messages in chronological order }, }); if (!messages) { // Check if thread exists indirectly // If findMany returns empty, the thread might not exist or has no messages. // Check if thread exists explicitly const thread = await prisma.chatThread.findUnique({ where: { id: threadId } }); if (!thread) { return res.status(404).json({ error: 'Chat thread not found.' }); } } res.status(200).json(messages.map(msg => ({ ...msg, createdAt: msg.createdAt.toISOString() }))); } catch (error) { console.error(`Error fetching messages for thread ${threadId}:`, error); // Basic error handling, check for specific Prisma errors if needed if (error.code === 'P2023' || error.message.includes('Malformed UUID')) { // Example: Invalid UUID format return res.status(400).json({ error: 'Invalid thread ID format.' }); } res.status(500).json({ error: 'Failed to fetch messages.' }); } }); // POST /api/chat/threads/:threadId/messages - Add a message to an existing thread router.post('/threads/:threadId/messages', async(req, res) => { const { threadId } = req.params; const { content, sender = 'user' } = req.body; // Default sender to 'user' if (!content || typeof content !== 'string' || content.trim().length === 0) { return res.status(400).json({ error: 'Message content cannot be empty.' }); } if (sender !== 'user' && sender !== 'bot') { return res.status(400).json({ error: 'Invalid sender type.' }); } try { // Verify thread exists first const thread = await prisma.chatThread.findUnique({ where: { id: threadId }, }); if (!thread) { return res.status(404).json({ error: 'Chat thread not found.' }); } const newMessage = await prisma.chatMessage.create({ data: { threadId: threadId, sender: sender, content: content.trim(), }, }); // Optionally: Update the thread's updatedAt timestamp await prisma.chatThread.update({ where: { id: threadId }, data: { updatedAt: new Date() } }); const user = await getUserById(req.session.loggedInUserId); if (!user) { req.session.destroy(err => {}); return res.status(401).json({ status: 'unauthenticated' }); } await askGeminiChat(threadId, `[${user.fullName || user.username}] ${content}`); res.status(201).json({ ...newMessage, createdAt: newMessage.createdAt.toISOString() }); } catch (error) { console.error(`Error adding message to thread ${threadId}:`, error); if (error.code === 'P2023' || error.message.includes('Malformed UUID')) { // Example: Invalid UUID format return res.status(400).json({ error: 'Invalid thread ID format.' }); } res.status(500).json({ error: 'Failed to add message.' }); } }); export default router;