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