+server.ts RawHistoryFolder
1import { eq } from 'drizzle-orm/sql';
2import db from '$lib/db/db.server';
3import { users } from '$lib/db/schema';
4import type { SafeUser } from '$lib/types/chat';
5import type { PublicRoomResponse } from '$lib/types/payloads';
6import { createSafeUser } from '$lib/types/chat';
7import { createLogger } from '$lib/utils/logger.server';
8
9const log = createLogger('rooms-server');
10
11const ONLINE_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
12
13export async function GET({ params, url, locals }): Promise<Response> {
14 const { roomId } = params;
15 const isPublic = !locals.user || url.searchParams.get('public') === 'true';
16
17 try {
18 log.debug('Fetching room data', {
19 roomId,
20 isPublic,
21 requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public'
22 });
23
24 // Calculate timeout threshold
25 const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS;
26
27 // Fetch buddy list: get all users
28 const fetchedUsers = await db.select()
29 .from(users)
30 .execute();
31
32 // Normalize users to ensure that lastSeen is always a number (using 0 if null)
33 const normalizedUsers = fetchedUsers.map(
34 (user: SafeUser & { lastSeen: number | null }) => ({
35 ...user,
36 lastSeen: user.lastSeen ?? 0
37 })
38 );
39
40 // Process users to mark them as offline if they've timed out
41 const processedUsers = normalizedUsers.map(
42 (user: SafeUser & { lastSeen: number }) => {
43 const shouldBeOffline = user.status !== 'offline' && user.lastSeen < timeoutThreshold;
44 return {
45 ...user,
46 status: shouldBeOffline ? 'offline' : user.status
47 };
48 }
49 );
50
51 // Find users who were originally non-offline but now marked offline
52 const usersToUpdate = processedUsers.filter(
53 (user: SafeUser & { lastSeen: number }) =>
54 user.status === 'offline' &&
55 normalizedUsers.find(
56 (u: SafeUser & { lastSeen: number }) => u.id === user.id
57 )?.status !== 'offline'
58 );
59
60 if (usersToUpdate.length > 0) {
61 await Promise.all(
62 usersToUpdate.map((user: SafeUser & { lastSeen: number }) =>
63 db.update(users)
64 .set({ status: 'offline', lastSeen: Date.now() })
65 .where(eq(users.id, user.id))
66 .execute()
67 )
68 );
69
70 log.debug('Updated offline status for users', {
71 count: usersToUpdate.length,
72 userIds: usersToUpdate.map((u: SafeUser & { lastSeen: number }) => u.id)
73 });
74 }
75
76 // Sanitize user data using createSafeUser
77 const sanitizedUsers = processedUsers.map(
78 (user: SafeUser & { lastSeen: number }) => createSafeUser(user)
79 );
80
81 const responseData: PublicRoomResponse = {
82 success: true,
83 buddyList: sanitizedUsers
84 };
85
86 log.debug('Successfully fetched room data', {
87 roomId,
88 userCount: sanitizedUsers.length,
89 isPublic,
90 offlineUpdates: usersToUpdate.length
91 });
92
93 return new Response(JSON.stringify(responseData), {
94 status: 200,
95 headers: { 'Content-Type': 'application/json' }
96 });
97 } catch (error) {
98 log.error('Error fetching room data', {
99 roomId,
100 error: error instanceof Error ? error.message : 'Unknown error'
101 });
102 const errorResponse: PublicRoomResponse = {
103 success: false,
104 error: 'Failed to fetch room data'
105 };
106 return new Response(JSON.stringify(errorResponse), {
107 status: 500,
108 headers: { 'Content-Type': 'application/json' }
109 });
110 }
111}