detached 3c14cb2

refactor: Enhance logging and privacy in SSE and authentication modules

commit
3c14cb2215e8731c1d3fc1ec0e87f935ff1fa07b
author
Kit · · 2025-02-14 13:35 UTC
parent
d46837f
signature
SSH UnverifiedSHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6Tk The key is unknown
4 files · +54 -36
M src/lib/sseEmitter.ts +11 -6
@@ -8,12 +8,16 @@ class CustomEventEmitter {
88 private maxListeners: number = 100;
99
1010 constructor() {
11- console.debug('Initializing custom SSE emitter');
11+ console.log('[SSE-Emitter] Initialized');
1212 }
1313
1414 emit(event: string, data: SSEEventData) {
1515 const eventListeners = this.listeners.get(event);
1616 if (eventListeners) {
17+ // Only log non-sensitive event types
18+ if (!['userStatusUpdate', 'chatMessage'].includes(data.type)) {
19+ console.log('[SSE-Emitter] Broadcasting event:', { type: data.type, listenersCount: eventListeners.length });
20+ }
1721 eventListeners.forEach(listener => listener(data));
1822 }
1923 }
@@ -25,24 +29,25 @@ class CustomEventEmitter {
2529 const eventListeners = this.listeners.get(event)!;
2630
2731 if (eventListeners.length >= this.maxListeners) {
28- console.warn(`Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);
32+ console.log(`[SSE-Emitter] Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);
2933 }
3034
3135 eventListeners.push(listener);
36+ console.log('[SSE-Emitter] New listener added:', { event, totalListeners: eventListeners.length });
3237 }
3338
3439 removeListener(event: string, listenerToRemove: (data: SSEEventData) => void) {
3540 const eventListeners = this.listeners.get(event);
3641 if (eventListeners) {
37- this.listeners.set(
38- event,
39- eventListeners.filter(listener => listener !== listenerToRemove)
40- );
42+ const newListeners = eventListeners.filter(listener => listener !== listenerToRemove);
43+ this.listeners.set(event, newListeners);
44+ console.log('[SSE-Emitter] Listener removed:', { event, remainingListeners: newListeners.length });
4145 }
4246 }
4347
4448 setMaxListeners(n: number) {
4549 this.maxListeners = n;
50+ console.log('[SSE-Emitter] Max listeners updated:', { newLimit: n });
4651 }
4752 }
4853
@@ -8,12 +8,16 @@ class CustomEventEmitter {
8 private maxListeners: number = 100;8 private maxListeners: number = 100;
9 9
10 constructor() {10 constructor() {
11- console.debug('Initializing custom SSE emitter');11+ console.log('[SSE-Emitter] Initialized');
12 }12 }
13 13
14 emit(event: string, data: SSEEventData) {14 emit(event: string, data: SSEEventData) {
15 const eventListeners = this.listeners.get(event);15 const eventListeners = this.listeners.get(event);
16 if (eventListeners) {16 if (eventListeners) {
17+ // Only log non-sensitive event types
18+ if (!['userStatusUpdate', 'chatMessage'].includes(data.type)) {
19+ console.log('[SSE-Emitter] Broadcasting event:', { type: data.type, listenersCount: eventListeners.length });
20+ }
17 eventListeners.forEach(listener => listener(data));21 eventListeners.forEach(listener => listener(data));
18 }22 }
19 }23 }
@@ -25,24 +29,25 @@ class CustomEventEmitter {
25 const eventListeners = this.listeners.get(event)!;29 const eventListeners = this.listeners.get(event)!;
26 30
27 if (eventListeners.length >= this.maxListeners) {31 if (eventListeners.length >= this.maxListeners) {
28- console.warn(`Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);32+ console.log(`[SSE-Emitter] Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);
29 }33 }
30 34
31 eventListeners.push(listener);35 eventListeners.push(listener);
36+ console.log('[SSE-Emitter] New listener added:', { event, totalListeners: eventListeners.length });
32 }37 }
33 38
34 removeListener(event: string, listenerToRemove: (data: SSEEventData) => void) {39 removeListener(event: string, listenerToRemove: (data: SSEEventData) => void) {
35 const eventListeners = this.listeners.get(event);40 const eventListeners = this.listeners.get(event);
36 if (eventListeners) {41 if (eventListeners) {
37- this.listeners.set(42+ const newListeners = eventListeners.filter(listener => listener !== listenerToRemove);
38- event,43+ this.listeners.set(event, newListeners);
39- eventListeners.filter(listener => listener !== listenerToRemove)44+ console.log('[SSE-Emitter] Listener removed:', { event, remainingListeners: newListeners.length });
40- );
41 }45 }
42 }46 }
43 47
44 setMaxListeners(n: number) {48 setMaxListeners(n: number) {
45 this.maxListeners = n;49 this.maxListeners = n;
50+ console.log('[SSE-Emitter] Max listeners updated:', { newLimit: n });
46 }51 }
47 }52 }
48 53
M src/routes/api/chat/messages/+server.ts +18 -11
@@ -58,7 +58,7 @@ export async function GET({ request, locals }) {
5858 // Ensure default chat room exists
5959 await ensureDefaultChatRoom(db);
6060
61- console.debug('Fetching messages', { isPublic });
61+ console.log('[Chat] Fetching messages', { isPublic });
6262 const query = db.select()
6363 .from(messages)
6464 .orderBy(desc(messages.timestamp));
@@ -83,8 +83,8 @@ export async function GET({ request, locals }) {
8383 headers: { 'Content-Type': 'application/json' }
8484 }
8585 );
86- } catch (error) {
87- console.debug('Error fetching messages:', error);
86+ } catch {
87+ console.log('[Chat] Error fetching messages');
8888 const errorResponse: GetMessagesResponse = {
8989 success: false,
9090 error: 'Failed to fetch messages'
@@ -105,12 +105,13 @@ export async function POST({ request, locals }: { request: Request, locals: App.
105105 throw error(401, 'Authentication required');
106106 }
107107
108- console.debug('Received new message POST');
108+ console.log('[Chat] New message received');
109109 try {
110110 // Check rate limiting
111111 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);
112112 if (!canSend) {
113- console.debug('Rate limited:', { userId: locals.user.id, retryAfter });
113+ const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`;
114+ console.log('[Chat] Rate limited:', { userId: maskedUserId, retryAfter });
114115 const errorResponse: SendMessageResponse = {
115116 success: false,
116117 error: 'Please wait before sending another message',
@@ -131,7 +132,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
131132
132133 const data = await request.json() as SendMessageRequest;
133134 if (!data.content || !data.userId) {
134- console.debug('Invalid payload', data);
135+ console.log('[Chat] Invalid payload received');
135136 const errorResponse: SendMessageResponse = {
136137 success: false,
137138 error: 'Invalid message payload'
@@ -153,7 +154,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
153154 .get();
154155
155156 if (!chatRoom) {
156- console.debug('Chat room not found:', chatRoomId);
157+ console.log('[Chat] Chat room not found:', chatRoomId);
157158 const errorResponse: SendMessageResponse = {
158159 success: false,
159160 error: 'Chat room not found'
@@ -168,7 +169,8 @@ export async function POST({ request, locals }: { request: Request, locals: App.
168169 .get();
169170
170171 if (!user) {
171- console.debug('User not found:', data.userId);
172+ const maskedUserId = `${data.userId.slice(0, 4)}...${data.userId.slice(-4)}`;
173+ console.log('[Chat] User not found:', maskedUserId);
172174 const errorResponse: SendMessageResponse = {
173175 success: false,
174176 error: 'User not found'
@@ -186,7 +188,12 @@ export async function POST({ request, locals }: { request: Request, locals: App.
186188 };
187189
188190 await db.insert(messages).values(newMessage);
189- console.debug('Message saved in DB', newMessage);
191+ console.log('[Chat] Message saved in DB', {
192+ messageId: newMessage.id,
193+ chatRoomId: newMessage.chatRoomId,
194+ type: newMessage.type,
195+ timestamp: newMessage.timestamp
196+ });
190197
191198 // Broadcast the new message via the unified SSE emitter
192199 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });
@@ -203,8 +210,8 @@ export async function POST({ request, locals }: { request: Request, locals: App.
203210 headers: { 'Content-Type': 'application/json' }
204211 }
205212 );
206- } catch (error) {
207- console.debug('Error processing POST:', error);
213+ } catch {
214+ console.log('[Chat] Error processing message');
208215 const errorResponse: SendMessageResponse = {
209216 success: false,
210217 error: 'Failed to save message'
@@ -58,7 +58,7 @@ export async function GET({ request, locals }) {
58 // Ensure default chat room exists58 // Ensure default chat room exists
59 await ensureDefaultChatRoom(db);59 await ensureDefaultChatRoom(db);
60 60
61- console.debug('Fetching messages', { isPublic });61+ console.log('[Chat] Fetching messages', { isPublic });
62 const query = db.select()62 const query = db.select()
63 .from(messages)63 .from(messages)
64 .orderBy(desc(messages.timestamp));64 .orderBy(desc(messages.timestamp));
@@ -83,8 +83,8 @@ export async function GET({ request, locals }) {
83 headers: { 'Content-Type': 'application/json' }83 headers: { 'Content-Type': 'application/json' }
84 }84 }
85 );85 );
86- } catch (error) {86+ } catch {
87- console.debug('Error fetching messages:', error);87+ console.log('[Chat] Error fetching messages');
88 const errorResponse: GetMessagesResponse = {88 const errorResponse: GetMessagesResponse = {
89 success: false,89 success: false,
90 error: 'Failed to fetch messages'90 error: 'Failed to fetch messages'
@@ -105,12 +105,13 @@ export async function POST({ request, locals }: { request: Request, locals: App.
105 throw error(401, 'Authentication required');105 throw error(401, 'Authentication required');
106 }106 }
107 107
108- console.debug('Received new message POST');108+ console.log('[Chat] New message received');
109 try {109 try {
110 // Check rate limiting110 // Check rate limiting
111 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);111 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);
112 if (!canSend) {112 if (!canSend) {
113- console.debug('Rate limited:', { userId: locals.user.id, retryAfter });113+ const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`;
114+ console.log('[Chat] Rate limited:', { userId: maskedUserId, retryAfter });
114 const errorResponse: SendMessageResponse = {115 const errorResponse: SendMessageResponse = {
115 success: false,116 success: false,
116 error: 'Please wait before sending another message',117 error: 'Please wait before sending another message',
@@ -131,7 +132,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
131 132
132 const data = await request.json() as SendMessageRequest;133 const data = await request.json() as SendMessageRequest;
133 if (!data.content || !data.userId) {134 if (!data.content || !data.userId) {
134- console.debug('Invalid payload', data);135+ console.log('[Chat] Invalid payload received');
135 const errorResponse: SendMessageResponse = {136 const errorResponse: SendMessageResponse = {
136 success: false,137 success: false,
137 error: 'Invalid message payload'138 error: 'Invalid message payload'
@@ -153,7 +154,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
153 .get();154 .get();
154 155
155 if (!chatRoom) {156 if (!chatRoom) {
156- console.debug('Chat room not found:', chatRoomId);157+ console.log('[Chat] Chat room not found:', chatRoomId);
157 const errorResponse: SendMessageResponse = {158 const errorResponse: SendMessageResponse = {
158 success: false,159 success: false,
159 error: 'Chat room not found'160 error: 'Chat room not found'
@@ -168,7 +169,8 @@ export async function POST({ request, locals }: { request: Request, locals: App.
168 .get();169 .get();
169 170
170 if (!user) {171 if (!user) {
171- console.debug('User not found:', data.userId);172+ const maskedUserId = `${data.userId.slice(0, 4)}...${data.userId.slice(-4)}`;
173+ console.log('[Chat] User not found:', maskedUserId);
172 const errorResponse: SendMessageResponse = {174 const errorResponse: SendMessageResponse = {
173 success: false,175 success: false,
174 error: 'User not found'176 error: 'User not found'
@@ -186,7 +188,12 @@ export async function POST({ request, locals }: { request: Request, locals: App.
186 };188 };
187 189
188 await db.insert(messages).values(newMessage);190 await db.insert(messages).values(newMessage);
189- console.debug('Message saved in DB', newMessage);191+ console.log('[Chat] Message saved in DB', {
192+ messageId: newMessage.id,
193+ chatRoomId: newMessage.chatRoomId,
194+ type: newMessage.type,
195+ timestamp: newMessage.timestamp
196+ });
190 197
191 // Broadcast the new message via the unified SSE emitter198 // Broadcast the new message via the unified SSE emitter
192 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });199 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });
@@ -203,8 +210,8 @@ export async function POST({ request, locals }: { request: Request, locals: App.
203 headers: { 'Content-Type': 'application/json' } 210 headers: { 'Content-Type': 'application/json' }
204 }211 }
205 );212 );
206- } catch (error) {213+ } catch {
207- console.debug('Error processing POST:', error);214+ console.log('[Chat] Error processing message');
208 const errorResponse: SendMessageResponse = {215 const errorResponse: SendMessageResponse = {
209 success: false,216 success: false,
210 error: 'Failed to save message'217 error: 'Failed to save message'
M src/routes/api/session/login/+server.ts +10 -9
@@ -24,7 +24,7 @@ setInterval(() => {
2424 }, 60 * 60 * 1000);
2525
2626 export const POST: RequestHandler = async ({ request, cookies }) => {
27- console.debug("POST /api/session/login called");
27+ console.log("[Login] New login attempt received");
2828
2929 if (request.method !== 'POST') {
3030 return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 });
@@ -32,28 +32,29 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
3232
3333 // Get the IP address for rate limiting
3434 const ip = request.headers.get('x-forwarded-for') || 'unknown';
35+ const maskedIp = ip.split('.').map((octet, idx) => idx < 3 ? 'xxx' : octet).join('.');
3536 const now = Date.now();
3637 const attemptData = loginAttempts.get(ip) || { count: 0, lastAttempt: 0 };
3738
3839 // If the user has failed three or more times, apply exponential backoff
3940 if (attemptData.count >= MAX_ATTEMPTS) {
4041 const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds
41- console.debug(`Applying exponential backoff delay of ${delay}ms for IP ${ip}`);
42+ console.log(`[Login] Rate limit exceeded for IP ${maskedIp} - applying ${delay}ms delay`);
4243 await sleep(delay);
4344 }
4445
4546 let body: unknown;
4647 try {
4748 body = await request.json();
48- } catch (err) {
49- console.debug("Invalid JSON", err);
49+ } catch {
50+ console.log("[Login] Invalid JSON payload received");
5051 return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 });
5152 }
5253
5354 const { username, password } = body as { username: string, password: string };
5455
5556 if (typeof username !== 'string' || typeof password !== 'string') {
56- console.debug("Missing or invalid input fields", { username, password });
57+ console.log("Missing or invalid input fields", { username, password });
5758 return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 });
5859 }
5960
@@ -66,7 +67,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
6667 attemptData.count++;
6768 attemptData.lastAttempt = now;
6869 loginAttempts.set(ip, attemptData);
69- console.debug(`Login failed for IP ${ip}. Attempt count: ${attemptData.count}`);
70+ console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`);
7071 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);
7172 return new Response(JSON.stringify({
7273 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}`
@@ -80,14 +81,14 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
8081 attemptData.count++;
8182 attemptData.lastAttempt = now;
8283 loginAttempts.set(ip, attemptData);
83- console.debug(`Login failed for IP ${ip}. Attempt count: ${attemptData.count}`);
84+ console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`);
8485 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);
8586 return new Response(JSON.stringify({
8687 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}`
8788 } as LoginResponseError), { status: 401 });
8889 }
89- } catch (err) {
90- console.debug("Error verifying password", err);
90+ } catch {
91+ console.log("[Login] Error verifying password");
9192 return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 });
9293 }
9394
@@ -24,7 +24,7 @@ setInterval(() => {
24 }, 60 * 60 * 1000);24 }, 60 * 60 * 1000);
25 25
26 export const POST: RequestHandler = async ({ request, cookies }) => {26 export const POST: RequestHandler = async ({ request, cookies }) => {
27- console.debug("POST /api/session/login called");27+ console.log("[Login] New login attempt received");
28 28
29 if (request.method !== 'POST') {29 if (request.method !== 'POST') {
30 return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 });30 return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 });
@@ -32,28 +32,29 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
32 32
33 // Get the IP address for rate limiting33 // Get the IP address for rate limiting
34 const ip = request.headers.get('x-forwarded-for') || 'unknown';34 const ip = request.headers.get('x-forwarded-for') || 'unknown';
35+ const maskedIp = ip.split('.').map((octet, idx) => idx < 3 ? 'xxx' : octet).join('.');
35 const now = Date.now();36 const now = Date.now();
36 const attemptData = loginAttempts.get(ip) || { count: 0, lastAttempt: 0 };37 const attemptData = loginAttempts.get(ip) || { count: 0, lastAttempt: 0 };
37 38
38 // If the user has failed three or more times, apply exponential backoff39 // If the user has failed three or more times, apply exponential backoff
39 if (attemptData.count >= MAX_ATTEMPTS) {40 if (attemptData.count >= MAX_ATTEMPTS) {
40 const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds41 const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds
41- console.debug(`Applying exponential backoff delay of ${delay}ms for IP ${ip}`);42+ console.log(`[Login] Rate limit exceeded for IP ${maskedIp} - applying ${delay}ms delay`);
42 await sleep(delay);43 await sleep(delay);
43 }44 }
44 45
45 let body: unknown;46 let body: unknown;
46 try {47 try {
47 body = await request.json();48 body = await request.json();
48- } catch (err) {49+ } catch {
49- console.debug("Invalid JSON", err);50+ console.log("[Login] Invalid JSON payload received");
50 return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 });51 return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 });
51 }52 }
52 53
53 const { username, password } = body as { username: string, password: string };54 const { username, password } = body as { username: string, password: string };
54 55
55 if (typeof username !== 'string' || typeof password !== 'string') {56 if (typeof username !== 'string' || typeof password !== 'string') {
56- console.debug("Missing or invalid input fields", { username, password });57+ console.log("Missing or invalid input fields", { username, password });
57 return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 });58 return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 });
58 }59 }
59 60
@@ -66,7 +67,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
66 attemptData.count++;67 attemptData.count++;
67 attemptData.lastAttempt = now;68 attemptData.lastAttempt = now;
68 loginAttempts.set(ip, attemptData);69 loginAttempts.set(ip, attemptData);
69- console.debug(`Login failed for IP ${ip}. Attempt count: ${attemptData.count}`);70+ console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`);
70 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);71 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);
71 return new Response(JSON.stringify({ 72 return new Response(JSON.stringify({
72 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` 73 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}`
@@ -80,14 +81,14 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
80 attemptData.count++;81 attemptData.count++;
81 attemptData.lastAttempt = now;82 attemptData.lastAttempt = now;
82 loginAttempts.set(ip, attemptData);83 loginAttempts.set(ip, attemptData);
83- console.debug(`Login failed for IP ${ip}. Attempt count: ${attemptData.count}`);84+ console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`);
84 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);85 const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count);
85 return new Response(JSON.stringify({ 86 return new Response(JSON.stringify({
86 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` 87 error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}`
87 } as LoginResponseError), { status: 401 });88 } as LoginResponseError), { status: 401 });
88 }89 }
89- } catch (err) {90+ } catch {
90- console.debug("Error verifying password", err);91+ console.log("[Login] Error verifying password");
91 return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 });92 return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 });
92 }93 }
93 94
M src/routes/api/sse/+server.ts +15 -10
@@ -4,7 +4,7 @@ import db from '$lib/db/db.server';
44 import { users } from '$lib/db/schema';
55 import { eq, and, lt } from 'drizzle-orm';
66
7-console.debug('SSE connection initiated');
7+console.log('[SSE] Server initialized');
88
99 // Constants for timeout checks
1010 const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
@@ -13,7 +13,7 @@ const CHECK_INTERVAL_MS = 60 * 1000; // Check every minute
1313 // Initialize periodic check for timed out users
1414 const timeoutInterval = setInterval(async () => {
1515 const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS;
16- console.debug('Checking for timed out users, threshold:', new Date(timeoutThreshold));
16+ console.log('[SSE] Checking for timed out users', { checkTime: new Date().toISOString() });
1717
1818 try {
1919 // Find and update users who are marked as online but haven't been seen recently
@@ -33,6 +33,7 @@ const timeoutInterval = setInterval(async () => {
3333
3434 // Broadcast status updates for timed out users
3535 result.forEach(user => {
36+ const maskedUserId = `${user.id.slice(0, 4)}...${user.id.slice(-4)}`;
3637 sseEmitter.emit('sse', {
3738 type: 'userStatusUpdate',
3839 data: {
@@ -41,10 +42,10 @@ const timeoutInterval = setInterval(async () => {
4142 lastSeen: now
4243 }
4344 });
44- console.debug('User timed out and marked offline:', user.id);
45+ console.log('[SSE] User timed out and marked offline:', maskedUserId);
4546 });
46- } catch (error) {
47- console.debug('Error during timeout check:', error);
47+ } catch {
48+ console.log('[SSE] Error during timeout check');
4849 }
4950 }, CHECK_INTERVAL_MS);
5051
@@ -91,9 +92,12 @@ export const GET: RequestHandler = async ({ locals }) => {
9192 const payload = `event: ${event.type}\n` +
9293 `data: ${JSON.stringify(event.data)}\n\n`;
9394 controller.enqueue(encoder.encode(payload));
94- console.debug('Sent SSE event:', event);
95- } catch (err) {
96- console.debug('Error sending SSE event:', err);
95+ // Only log non-sensitive event types
96+ if (!['userStatusUpdate', 'chatMessage'].includes(event.type)) {
97+ console.log('[SSE] Event sent:', { type: event.type });
98+ }
99+ } catch {
100+ console.log('[SSE] Error sending event');
97101 }
98102 };
99103
@@ -102,7 +106,7 @@ export const GET: RequestHandler = async ({ locals }) => {
102106 };
103107
104108 sseEmitter.addListener('sse', onSSE);
105- console.debug('Added SSE listener');
109+ console.log('[SSE] Added new listener');
106110
107111 // Send initial connection message
108112 controller.enqueue(encoder.encode('data: Connected\n\n'));
@@ -119,7 +123,8 @@ export const GET: RequestHandler = async ({ locals }) => {
119123 async cancel(reason) {
120124 if (keepAliveInterval) clearInterval(keepAliveInterval);
121125 if (onSSE) sseEmitter.removeListener('sse', onSSE);
122- console.debug('SSE connection cancelled:', reason);
126+ const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`;
127+ console.log('[SSE] Connection closed:', { userId: maskedUserId, reason });
123128
124129 // Update user status to offline in the database
125130 const now = Date.now();
@@ -4,7 +4,7 @@ import db from '$lib/db/db.server';
4 import { users } from '$lib/db/schema';4 import { users } from '$lib/db/schema';
5 import { eq, and, lt } from 'drizzle-orm';5 import { eq, and, lt } from 'drizzle-orm';
6 6
7-console.debug('SSE connection initiated');7+console.log('[SSE] Server initialized');
8 8
9 // Constants for timeout checks9 // Constants for timeout checks
10 const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes10 const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
@@ -13,7 +13,7 @@ const CHECK_INTERVAL_MS = 60 * 1000; // Check every minute
13 // Initialize periodic check for timed out users13 // Initialize periodic check for timed out users
14 const timeoutInterval = setInterval(async () => {14 const timeoutInterval = setInterval(async () => {
15 const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS;15 const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS;
16- console.debug('Checking for timed out users, threshold:', new Date(timeoutThreshold));16+ console.log('[SSE] Checking for timed out users', { checkTime: new Date().toISOString() });
17 17
18 try {18 try {
19 // Find and update users who are marked as online but haven't been seen recently19 // Find and update users who are marked as online but haven't been seen recently
@@ -33,6 +33,7 @@ const timeoutInterval = setInterval(async () => {
33 33
34 // Broadcast status updates for timed out users34 // Broadcast status updates for timed out users
35 result.forEach(user => {35 result.forEach(user => {
36+ const maskedUserId = `${user.id.slice(0, 4)}...${user.id.slice(-4)}`;
36 sseEmitter.emit('sse', {37 sseEmitter.emit('sse', {
37 type: 'userStatusUpdate',38 type: 'userStatusUpdate',
38 data: {39 data: {
@@ -41,10 +42,10 @@ const timeoutInterval = setInterval(async () => {
41 lastSeen: now42 lastSeen: now
42 }43 }
43 });44 });
44- console.debug('User timed out and marked offline:', user.id);45+ console.log('[SSE] User timed out and marked offline:', maskedUserId);
45 });46 });
46- } catch (error) {47+ } catch {
47- console.debug('Error during timeout check:', error);48+ console.log('[SSE] Error during timeout check');
48 }49 }
49 }, CHECK_INTERVAL_MS);50 }, CHECK_INTERVAL_MS);
50 51
@@ -91,9 +92,12 @@ export const GET: RequestHandler = async ({ locals }) => {
91 const payload = `event: ${event.type}\n` +92 const payload = `event: ${event.type}\n` +
92 `data: ${JSON.stringify(event.data)}\n\n`;93 `data: ${JSON.stringify(event.data)}\n\n`;
93 controller.enqueue(encoder.encode(payload));94 controller.enqueue(encoder.encode(payload));
94- console.debug('Sent SSE event:', event);95+ // Only log non-sensitive event types
95- } catch (err) {96+ if (!['userStatusUpdate', 'chatMessage'].includes(event.type)) {
96- console.debug('Error sending SSE event:', err);97+ console.log('[SSE] Event sent:', { type: event.type });
98+ }
99+ } catch {
100+ console.log('[SSE] Error sending event');
97 }101 }
98 };102 };
99 103
@@ -102,7 +106,7 @@ export const GET: RequestHandler = async ({ locals }) => {
102 };106 };
103 107
104 sseEmitter.addListener('sse', onSSE);108 sseEmitter.addListener('sse', onSSE);
105- console.debug('Added SSE listener');109+ console.log('[SSE] Added new listener');
106 110
107 // Send initial connection message111 // Send initial connection message
108 controller.enqueue(encoder.encode('data: Connected\n\n'));112 controller.enqueue(encoder.encode('data: Connected\n\n'));
@@ -119,7 +123,8 @@ export const GET: RequestHandler = async ({ locals }) => {
119 async cancel(reason) {123 async cancel(reason) {
120 if (keepAliveInterval) clearInterval(keepAliveInterval);124 if (keepAliveInterval) clearInterval(keepAliveInterval);
121 if (onSSE) sseEmitter.removeListener('sse', onSSE);125 if (onSSE) sseEmitter.removeListener('sse', onSSE);
122- console.debug('SSE connection cancelled:', reason);126+ const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`;
127+ console.log('[SSE] Connection closed:', { userId: maskedUserId, reason });
123 128
124 // Update user status to offline in the database129 // Update user status to offline in the database
125 const now = Date.now();130 const now = Date.now();

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