import { redirect, type Handle, type HandleFetch } from '@sveltejs/kit'; import { paraglideMiddleware } from '$lib/paraglide/server'; import { sequence } from '@sveltejs/kit/hooks'; import { env } from 'process'; import { cookieName, cookieMaxAge } from '$lib/paraglide/runtime'; import { detectLocale } from '$lib/server/locale'; const PUBLIC_PATHS = ['/login', '/logout', '/forgot-password', '/reset-password', '/register']; const handleLocaleDetection: Handle = ({ event, resolve }) => { if (!event.cookies.get(cookieName)) { const locale = detectLocale(event.request.headers.get('accept-language') ?? ''); if (locale) { event.cookies.set(cookieName, locale, { path: '/', sameSite: 'lax', maxAge: cookieMaxAge, httpOnly: false }); } } return resolve(event); }; const handleAuth: Handle = async ({ event, resolve }) => { const isPublic = PUBLIC_PATHS.some((p) => event.url.pathname.startsWith(p)); if (!isPublic && !event.locals.user) { throw redirect(302, '/login'); } return resolve(event); }; const handleParaglide: Handle = ({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => { event.request = request; return resolve(event, { transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale) }); }); const userGroup: Handle = async ({ event, resolve }) => { const auth = event.cookies.get('auth_token'); if (auth) { try { const apiUrl = env.API_INTERNAL_URL || 'http://localhost:8080'; const response = await fetch(`${apiUrl}/api/users/me`, { headers: { Authorization: auth } }); if (response.ok) { const user = await response.json(); event.locals.user = user; } } catch (error) { console.error('Error fetching user in hook:', error); } } return resolve(event); }; export const handleFetch: HandleFetch = async ({ event, request, fetch }) => { const apiUrl = env.API_INTERNAL_URL || 'http://localhost:8080'; const isApi = request.url.startsWith(apiUrl) || request.url.includes('/api/'); if (isApi) { // If the request already carries an explicit Authorization header (e.g. the // login action sends Basic auth), pass it through unchanged. if (request.headers.has('Authorization')) { return fetch(request); } // Password reset endpoints are public — no auth header needed. const PUBLIC_API_PATHS = [ '/api/auth/forgot-password', '/api/auth/reset-password', '/api/auth/invite/', '/api/auth/register' ]; if (PUBLIC_API_PATHS.some((p) => request.url.includes(p))) { return fetch(request); } const token = event.cookies.get('auth_token'); if (!token) { return new Response('Unauthorized', { status: 401 }); } // Clone the request first to preserve the body const clonedRequest = request.clone(); // Create new request with Authorization header and preserved body const modifiedRequest = new Request(clonedRequest, { headers: { ...Object.fromEntries(clonedRequest.headers), Authorization: token } }); return fetch(modifiedRequest); } return fetch(request); }; export const handle = sequence(userGroup, handleAuth, handleLocaleDetection, handleParaglide);