v0.4.0 f6d8405

+server.ts RawHistoryFolder

1import { users } from '$lib/db/convex.server';
2import { validateSessionToken, generateSessionToken, createSession } from '$lib/api/session.server';
3import { setSessionTokenCookie } from '$lib/api/session.cookie';
4import { createSafeUser } from '$lib/types/chat';
5import { createLogger } from '$lib/utils/logger.server';
6import type { RequestHandler } from './$types';
7
8const log = createLogger('status-server');
9
10export const POST: RequestHandler = async ({ request, cookies, locals }) => {
11	if (!locals.user) {
12		log.warn('Authentication required');
13		return new Response(JSON.stringify({ success: false, error: 'Unauthorized' }), {
14			status: 401,
15			headers: { 'Content-Type': 'application/json' }
16		});
17	}
18
19	try {
20		const { status } = await request.json();
21		const validStatuses = ['online', 'away', 'busy', 'offline', 'idle'];
22		if (!validStatuses.includes(status)) {
23			log.warn('Invalid status received', { status });
24			return new Response(JSON.stringify({ success: false, error: 'Invalid status' }), {
25				status: 400,
26				headers: { 'Content-Type': 'application/json' }
27			});
28		}
29
30		const userId = locals.user.id;
31		const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`;
32		log.debug('Updating user status', { userId: maskedUserId, status });
33
34		const now = Date.now();
35
36		// Update the user status in Convex
37		const updatedUser = await users.updateStatus(userId, status);
38		log.debug('Convex updated successfully', { userId: maskedUserId, status });
39
40		// Renew session if status is online and session expires in less than 1 day
41		if (status === 'online') {
42			try {
43				const token = cookies.get('session');
44				if (token) {
45					const result = await validateSessionToken(token);
46					if (result.session) {
47						const remaining = result.session.expiresAt - now;
48						const threshold = 24 * 60 * 60 * 1000; // 1 day in milliseconds
49						if (remaining < threshold) {
50							const newToken = generateSessionToken();
51							const newSession = await createSession(newToken, userId);
52							setSessionTokenCookie({ cookies }, newToken, newSession.expiresAt);
53							log.info('Session renewed', {
54								userId: maskedUserId,
55								expiresAt: new Date(newSession.expiresAt).toISOString()
56							});
57						}
58					}
59				}
60			} catch (renewalError) {
61				log.error('Session renewal failed', {
62					error: renewalError instanceof Error ? renewalError.message : 'Unknown error',
63					userId: maskedUserId
64				});
65				// Continue with status update even if renewal fails
66			}
67		}
68
69		return new Response(
70			JSON.stringify({
71				success: true,
72				user: createSafeUser({
73					id: updatedUser._id,
74					nickname: updatedUser.nickname,
75					status: updatedUser.status,
76					avatarUrl: updatedUser.avatarUrl,
77					lastSeen: updatedUser.lastSeen
78				})
79			}),
80			{
81				status: 200,
82				headers: { 'Content-Type': 'application/json' }
83			}
84		);
85	} catch (error) {
86		log.error('Error updating status', {
87			error: error instanceof Error ? error.message : 'Unknown error',
88			userId: locals.user?.id
89		});
90		return new Response(
91			JSON.stringify({
92				success: false,
93				error: 'Failed to update status'
94			}),
95			{
96				status: 500,
97				headers: { 'Content-Type': 'application/json' }
98			}
99		);
100	}
101};

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