1import { v4 as uuidv4 } from 'uuid';
2import db from '$lib/db/db.server';
3import { messages } from '$lib/db/schema';
4import type { Message } from '$lib/types/chat';
5import type {
6 SendMessageRequest,
7 SendMessageResponse,
8 GetMessagesResponse
9} from '$lib/types/payloads';
10import { desc } from 'drizzle-orm';
11import { error } from '@sveltejs/kit';
12import { sseEmitter } from '$lib/sseEmitter';
13import { eq } from 'drizzle-orm';
14import { chatRooms, DEFAULT_CHAT_ROOM_ID } from '$lib/db/schema';
15import { users } from '$lib/db/schema';
16import { ensureDefaultChatRoom } from '$lib/db/schema';
17
18// GET endpoint: fetch messages from the DB
19export async function GET({ request, locals }) {
20 const isPublic = new URL(request.url).searchParams.get('public') === 'true';
21 if (!isPublic && !locals.user) {
22 throw error(401, 'Authentication required');
23 }
24
25 try {
26 // Ensure default chat room exists
27 await ensureDefaultChatRoom(db);
28
29 console.debug('Fetching messages', { isPublic });
30 const query = db.select()
31 .from(messages)
32 .orderBy(desc(messages.timestamp));
33
34 if (isPublic) {
35 query.limit(10);
36 } else {
37 query.limit(50);
38 }
39
40 const fetchedMessages = await query;
41
42 const response: GetMessagesResponse = {
43 success: true,
44 messages: fetchedMessages.reverse()
45 };
46
47 return new Response(
48 JSON.stringify(response),
49 {
50 status: 200,
51 headers: { 'Content-Type': 'application/json' }
52 }
53 );
54 } catch (error) {
55 console.debug('Error fetching messages:', error);
56 const errorResponse: GetMessagesResponse = {
57 success: false,
58 error: 'Failed to fetch messages'
59 };
60 return new Response(
61 JSON.stringify(errorResponse),
62 {
63 status: 500,
64 headers: { 'Content-Type': 'application/json' }
65 }
66 );
67 }
68}
69
70// POST endpoint: receive a new message, save it in the DB, and broadcast via SSE
71export async function POST({ request, locals }: { request: Request, locals: App.Locals }) {
72 if (!locals.user) {
73 throw error(401, 'Authentication required');
74 }
75
76 console.debug('Received new message POST');
77 try {
78 // Ensure default chat room exists
79 await ensureDefaultChatRoom(db);
80
81 const data = await request.json() as SendMessageRequest;
82 if (!data.content || !data.userId) {
83 console.debug('Invalid payload', data);
84 const errorResponse: SendMessageResponse = {
85 success: false,
86 error: 'Invalid message payload'
87 };
88 return new Response(JSON.stringify(errorResponse), { status: 400 });
89 }
90
91 if (data.userId !== locals.user.id) {
92 throw error(403, 'Cannot post messages as another user');
93 }
94
95 // Validate that both the chat room and user exist
96 const chatRoomId = data.chatRoomId || DEFAULT_CHAT_ROOM_ID;
97
98 // Check chat room existence
99 const chatRoom = await db.select()
100 .from(chatRooms)
101 .where(eq(chatRooms.id, chatRoomId))
102 .get();
103
104 if (!chatRoom) {
105 console.debug('Chat room not found:', chatRoomId);
106 const errorResponse: SendMessageResponse = {
107 success: false,
108 error: 'Chat room not found'
109 };
110 return new Response(JSON.stringify(errorResponse), { status: 404 });
111 }
112
113 // Check user existence
114 const user = await db.select()
115 .from(users)
116 .where(eq(users.id, data.userId))
117 .get();
118
119 if (!user) {
120 console.debug('User not found:', data.userId);
121 const errorResponse: SendMessageResponse = {
122 success: false,
123 error: 'User not found'
124 };
125 return new Response(JSON.stringify(errorResponse), { status: 404 });
126 }
127
128 const newMessage: Message = {
129 id: uuidv4(),
130 chatRoomId: data.chatRoomId || DEFAULT_CHAT_ROOM_ID,
131 senderId: data.userId,
132 content: data.content,
133 type: data.type || 'chat',
134 timestamp: Date.now()
135 };
136
137 await db.insert(messages).values(newMessage);
138 console.debug('Message saved in DB', newMessage);
139
140 // Broadcast the new message via the unified SSE emitter
141 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });
142
143 const successResponse: SendMessageResponse = {
144 success: true,
145 message: newMessage
146 };
147
148 return new Response(
149 JSON.stringify(successResponse),
150 {
151 status: 201,
152 headers: { 'Content-Type': 'application/json' }
153 }
154 );
155 } catch (error) {
156 console.debug('Error processing POST:', error);
157 const errorResponse: SendMessageResponse = {
158 success: false,
159 error: 'Failed to save message'
160 };
161 return new Response(
162 JSON.stringify(errorResponse),
163 {
164 status: 500,
165 headers: { 'Content-Type': 'application/json' }
166 }
167 );
168 }
169}