import { authenticate } from '../../plugins/auth.js' import { getMessages } from '../../services/chat.service.js' import { getSessionClosures } from '../../services/closure.service.js' import { registerDeviceToken } from '../../services/notification.service.js' import { flipSessionSensitivity } from '../../services/sensitivity.service.js' import { getDb } from '../../db/client.js' import { TopicSensitivity, UserType } from '../../constants.js' const sql = getDb() const resolveUser = async (request, reply) => { if (request.auth?.userType !== UserType.CUSTOMER && request.auth?.userType !== UserType.MITRA) { return reply.code(403).send({ success: false, error: { code: 'FORBIDDEN', message: 'Customer or mitra account required' }, }) } request.userType = request.auth.userType request.userId = request.auth.userId } // Verify session belongs to the authenticated user const verifySessionOwnership = async (request, reply) => { const { sessionId } = request.params const [session] = await sql` SELECT id FROM chat_sessions WHERE id = ${sessionId} AND (customer_id = ${request.userId} OR mitra_id = ${request.userId}) ` if (!session) { return reply.code(403).send({ success: false, error: { code: 'FORBIDDEN', message: 'You do not have access to this session' }, }) } } export const sharedChatRoutes = async (app) => { // Get messages for a session (paginated) app.get('/chat/:sessionId/messages', { preHandler: [authenticate, resolveUser, verifySessionOwnership] }, async (request, reply) => { const { sessionId } = request.params const { limit, before } = request.query const messages = await getMessages(sessionId, { limit: limit ? parseInt(limit) : 50, before, }) return reply.send({ success: true, data: messages }) }) // Get session info app.get('/chat/:sessionId/info', { preHandler: [authenticate, resolveUser, verifySessionOwnership] }, async (request, reply) => { const { sessionId } = request.params const { getSessionById } = await import('../../services/session.service.js') const session = await getSessionById(sessionId) if (!session) { return reply.code(404).send({ success: false, error: { code: 'NOT_FOUND', message: 'Session not found' } }) } return reply.send({ success: true, data: session }) }) // Get full transcript (read-only, for history) app.get('/chat/:sessionId/transcript', { preHandler: [authenticate, resolveUser, verifySessionOwnership] }, async (request, reply) => { const { sessionId } = request.params const messages = await getMessages(sessionId, { limit: 10000 }) const closures = await getSessionClosures(sessionId) return reply.send({ success: true, data: { messages, closures } }) }) // Register FCM device token app.post('/device-token', { preHandler: [authenticate, resolveUser] }, async (request, reply) => { const { token } = request.body if (!token) { return reply.code(400).send({ success: false, error: { code: 'BAD_REQUEST', message: 'Token is required' } }) } await registerDeviceToken(request.userType, request.userId, token) return reply.send({ success: true }) }) // Submit goodbye/closure message app.post('/sessions/:sessionId/close-message', { preHandler: [authenticate, resolveUser, verifySessionOwnership] }, async (request, reply) => { const { sessionId } = request.params const { message } = request.body if (!message) { return reply.code(400).send({ success: false, error: { code: 'BAD_REQUEST', message: 'Message is required' } }) } const { submitClosureMessage } = await import('../../services/closure.service.js') const closure = await submitClosureMessage(sessionId, request.userType, request.userId, message) return reply.send({ success: true, data: closure }) }) // Mitra flips session topic sensitivity (regular <-> sensitive) app.patch('/chat/sessions/:sessionId/topic', { preHandler: [authenticate, resolveUser] }, async (request, reply) => { if (request.userType !== UserType.MITRA) { return reply.code(403).send({ success: false, error: { code: 'FORBIDDEN', message: 'Only mitra can change topic sensitivity' }, }) } const { sessionId } = request.params const { topic_sensitivity } = request.body || {} if (topic_sensitivity !== TopicSensitivity.REGULAR && topic_sensitivity !== TopicSensitivity.SENSITIVE) { return reply.code(400).send({ success: false, error: { code: 'BAD_REQUEST', message: 'topic_sensitivity must be regular or sensitive' }, }) } try { const result = await flipSessionSensitivity({ sessionId, mitraId: request.userId, toValue: topic_sensitivity, }) return reply.send({ success: true, data: result }) } catch (err) { return reply.code(err.statusCode || 500).send({ success: false, error: { code: err.code || 'INTERNAL', message: err.message }, }) } }) }