All files / src hooks.server.ts

70.83% Statements 17/24
69.23% Branches 18/26
33.33% Functions 2/6
70.83% Lines 17/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82        9x 8x               8x                                     8x     8x 8x 5x           3x 3x       3x           8x   8x         8x 1x           7x 1x           6x            
import type { Handle } from '@sveltejs/kit';
import { env } from '$env/dynamic/public';
import { createServerClient } from '$lib/supabase/server';
 
export const handle: Handle = async ({ event, resolve }) => {
	Iif (!env.PUBLIC_SUPABASE_URL || !env.PUBLIC_SUPABASE_API_KEY) {
		console.error('Missing required environment variables', {
			PUBLIC_SUPABASE_URL: env.PUBLIC_SUPABASE_URL ? 'set' : 'MISSING',
			PUBLIC_SUPABASE_API_KEY: env.PUBLIC_SUPABASE_API_KEY ? 'set' : 'MISSING',
		});
		return new Response('Server configuration error', { status: 503 });
	}
 
	event.locals.supabase = createServerClient(
		env.PUBLIC_SUPABASE_URL,
		env.PUBLIC_SUPABASE_API_KEY,
		{
			getAll: () => event.cookies.getAll(),
			setAll: (cookiesToSet) => {
				cookiesToSet.forEach(({ name, value, options }) => {
					event.cookies.set(name, value, { path: '/', ...options });
				});
			},
		},
	);
 
	/**
	 * Unlike `supabase.auth.getSession()`, which returns the session from storage
	 * without validation, this function validates the session by calling `getUser()`
	 * on the Supabase Auth server. Use this instead of `getSession()` for
	 * server-side auth checks.
	 */
	event.locals.safeGetSession = async () => {
		const {
			data: { session },
		} = await event.locals.supabase.auth.getSession();
		if (!session) {
			return { session: null, user: null };
		}
 
		const {
			data: { user },
			error,
		} = await event.locals.supabase.auth.getUser();
		Iif (error) {
			return { session: null, user: null };
		}
 
		return { session, user };
	};
 
	// Auth guard: protect all routes except public auth routes.
	// NOTE: +layout.server.ts has a separate athlete-row guard with its own exception
	// list — keep both in sync when adding new auth routes.
	const { session } = await event.locals.safeGetSession();
	const isAuthRoute =
		event.url.pathname === '/login' ||
		event.url.pathname === '/forgot-password' ||
		event.url.pathname === '/reset-password' ||
		event.url.pathname.startsWith('/auth/');
 
	if (!session && !isAuthRoute) {
		return new Response(null, {
			status: 303,
			headers: { location: '/login' },
		});
	}
 
	if (session && event.url.pathname === '/login') {
		return new Response(null, {
			status: 303,
			headers: { location: '/dashboard' },
		});
	}
 
	return resolve(event, {
		filterSerializedResponseHeaders(name) {
			return name === 'content-range' || name === 'x-supabase-api-version';
		},
	});
};