v0.1.1 88fcae1

+server.ts RawHistoryFolder

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} 

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 9

↑↓ moveenter openesc close