v0.2.0 1301099

+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} 

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