import crypto from 'node:crypto'; import type { Handle, HandleServerError } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit'; import { resolve } from '$app/paths'; import { ForbiddenError, isAppError, logger as rootLogger, NotFoundError, runWithContext, server, serverLogger as log, UnauthorizedError, ValidationError, } from '$lib/server'; import type { User } from '$lib/types/user'; const LOGIN = resolve('/auth/login'); const WORKSPACE_RESOURCES = resolve('/api/workspaces'); const ready = server.init(); const shutdown = async (signal: string) => { log.info(`[${signal}] Shutting down...`); await server.shutdown(); rootLogger.close(); process.exit(0); }; process.once('SIGTERM', () => shutdown('SIGTERM')); process.once('SIGINT', () => shutdown('SIGINT')); const handle: Handle = async ({ event, resolve: resolveRequest }) => { await ready; const requestId = crypto.randomUUID(); event.locals.requestId = requestId; return runWithContext({ requestId }, async () => { const auth = server.auth; let user: User | undefined; if (auth) { const token = event.cookies.get('session'); user = token ? await auth.verifyToken(token) : undefined; if (!user && event.url.pathname !== LOGIN) { redirect(302, LOGIN); } } else { user = { id: 'admin', name: 'Admin', email: 'admin@locostack.com', workspaces: ['*'], }; } event.locals.user = user || null; const { pathname } = event.url; if (pathname.startsWith(WORKSPACE_RESOURCES) && pathname !== WORKSPACE_RESOURCES) { if (!user) { throw new UnauthorizedError('Authentication required'); } const workspaceId = event.params.workspaceId; if (workspaceId === undefined) { throw new ValidationError('Workspace ID is required'); } if (!user.workspaces.includes(workspaceId) && !user.workspaces.includes('*')) { throw new ForbiddenError('User does not have access to this workspace'); } const workspace = server.ws.list().find((ws) => ws.id === workspaceId); if (!workspace) { throw new NotFoundError('Workspace not found'); } event.locals.workspaceId = workspaceId; event.locals.workspace = workspace; } else { event.locals.workspaceId = event.cookies.get('workspaceId') || null; } return resolveRequest(event); }); }; /** * Catches any error that escapes a load function, action, or endpoint. * AppErrors are surfaced with their code and message; unexpected errors * are logged and replaced with a generic message so internals are not leaked. */ const handleError: HandleServerError = ({ error, event }) => { const requestId = event.locals.requestId; if (isAppError(error)) { log.warn('Application error', { code: error.code, status: error.statusCode, message: error.message, path: event.url.pathname, requestId, }); return { code: error.code, message: error.message }; } log.error('Unexpected server error', { err: error, path: event.url.pathname, requestId }); return { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred.' }; }; export { handle, handleError };