hooks.server.ts RawHistoryFolder
1import type { Handle } from "@sveltejs/kit";
2import db from "$lib/db/db.server";
3import { handleRateLimit } from "$lib/api/rate-limiter";
4import { users } from "$lib/db/schema";
5import { createLogger } from "$lib/utils/logger.server";
6import { ensureDefaultChatRoom } from "$lib/utils/chat.server";
7import { validateSessionToken } from "$lib/api/session.server";
8import { setSessionTokenCookie, deleteSessionTokenCookie } from "$lib/api/session.cookie";
9
10const log = createLogger('hooks-server');
11
12// Set all users to offline on server start
13async function setAllUsersOffline() {
14 try {
15 await db.update(users)
16 .set({ status: 'offline'});
17 log.info('All users set to offline on server start');
18 } catch (error: unknown) {
19 log.error('Failed to set users offline on server start:', { error });
20 }
21}
22
23// Initialize server
24Promise.resolve()
25 .then(async () => {
26 log.info('Initializing server...');
27 await ensureDefaultChatRoom(db);
28 await setAllUsersOffline();
29 log.info('Server initialized successfully');
30 })
31 .catch((error: unknown) => {
32 log.error('Failed to initialize server:', { error });
33 process.exit(1);
34 });
35
36export const handle: Handle = async ({ event, resolve }) => {
37 log.debug("Handling request", { path: event.url.pathname });
38
39 // Get session token from cookies
40 const token = event.cookies.get("session") ?? null;
41
42 // Validate session token if token exists
43 if (token) {
44 const { session, user } = await validateSessionToken(token);
45 if (session) {
46 log.debug("Valid session found", { user: user?.nickname });
47 setSessionTokenCookie(event, token, session.expiresAt);
48 event.locals.session = session;
49 event.locals.user = user;
50 } else {
51 log.debug("Invalid session token");
52 deleteSessionTokenCookie(event);
53 event.locals.session = null;
54 event.locals.user = null;
55 }
56 }
57
58 // Identify public chat and room requests if there is no valid session
59 const isPublicChatRequest = event.url.pathname.startsWith('/api/chat/messages') && event.request.method === 'GET';
60 const isPublicRoomRequest = event.url.pathname.startsWith('/api/rooms/') && event.request.method === 'GET';
61
62 if ((isPublicChatRequest || isPublicRoomRequest) && !event.locals.session) {
63 log.debug("Public request accessed", { path: event.url.pathname });
64 const response = await resolve(event);
65 return response;
66 }
67
68 // Public routes that don't require authentication
69 const publicRoutes = [
70 "/",
71 "/api/session/login",
72 "/api/session/validate",
73 "/api/register",
74 "/login",
75 "/register"
76 ];
77
78 // Handle public routes
79 if (publicRoutes.includes(event.url.pathname)) {
80 log.debug("Public route accessed", { path: event.url.pathname });
81 const response = await resolve(event);
82 return response;
83 }
84
85 // Skip rate limiting for SSE endpoint when authenticated
86 if (event.url.pathname.startsWith('/api/sse')) {
87 return await resolve(event);
88 }
89
90 // Apply rate limiting for non-public endpoints
91 if (!isPublicChatRequest && !isPublicRoomRequest) {
92 const rateLimitResult = await handleRateLimit(event);
93 if (rateLimitResult?.status === 429) {
94 return new Response('Too Many Requests', {
95 status: 429,
96 headers: {
97 'Retry-After': '60'
98 }
99 });
100 }
101 }
102
103 const response = await resolve(event);
104 return response;
105};