From 97b610020c34b082742d2caf0992efb2367e8602 Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 22 Mar 2026 17:59:36 +0100 Subject: [PATCH] fix(storage): use runtime env vars instead of hardcoded localhost URLs The storage web app had hardcoded localhost:3001 (auth) and localhost:3016 (backend) URLs, causing production to try connecting to localhost. Added hooks.server.ts for runtime URL injection and CSP headers, matching the pattern used by calendar/chat apps. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/storage/apps/web/src/hooks.server.ts | 55 +++++++++++++++++++ apps/storage/apps/web/src/lib/api/client.ts | 13 ++++- .../apps/web/src/lib/stores/auth.svelte.ts | 14 ++++- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 apps/storage/apps/web/src/hooks.server.ts diff --git a/apps/storage/apps/web/src/hooks.server.ts b/apps/storage/apps/web/src/hooks.server.ts new file mode 100644 index 000000000..9ea4831fb --- /dev/null +++ b/apps/storage/apps/web/src/hooks.server.ts @@ -0,0 +1,55 @@ +/** + * Server Hooks for SvelteKit + * - Injects runtime environment variables for client-side use + * - Adds security headers + * - Auth is handled client-side via Mana Core Auth + */ + +import type { Handle } from '@sveltejs/kit'; + +// Get client-side URLs from environment (Docker runtime) +const PUBLIC_MANA_CORE_AUTH_URL_CLIENT = + process.env.PUBLIC_MANA_CORE_AUTH_URL_CLIENT || + process.env.PUBLIC_MANA_CORE_AUTH_URL || + 'http://localhost:3001'; +const PUBLIC_BACKEND_URL_CLIENT = + process.env.PUBLIC_BACKEND_URL_CLIENT || + process.env.PUBLIC_BACKEND_URL || + 'http://localhost:3016'; + +export const handle: Handle = async ({ event, resolve }) => { + const response = await resolve(event, { + transformPageChunk: ({ html }) => { + // Inject runtime environment variables into the HTML + // These will be available on window.__PUBLIC_*__ for client-side code + const envScript = ``; + return html.replace('', `${envScript}`); + }, + }); + + // Security headers + response.headers.set('X-Frame-Options', 'DENY'); + response.headers.set('X-Content-Type-Options', 'nosniff'); + response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); + response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()'); + response.headers.set( + 'Content-Security-Policy', + [ + "default-src 'self'", + "script-src 'self' 'unsafe-inline' https://stats.mana.how", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + `connect-src 'self' ${PUBLIC_MANA_CORE_AUTH_URL_CLIENT} ${PUBLIC_BACKEND_URL_CLIENT} https://stats.mana.how`, + "font-src 'self'", + "object-src 'none'", + "base-uri 'self'", + "form-action 'self'", + "frame-ancestors 'none'", + ].join('; ') + ); + + return response; +}; diff --git a/apps/storage/apps/web/src/lib/api/client.ts b/apps/storage/apps/web/src/lib/api/client.ts index bbdb2e2aa..11ea1d7f0 100644 --- a/apps/storage/apps/web/src/lib/api/client.ts +++ b/apps/storage/apps/web/src/lib/api/client.ts @@ -3,10 +3,21 @@ * Uses @manacore/shared-api-client for consistent error handling */ +import { browser } from '$app/environment'; import { createApiClient, type ApiResult } from '@manacore/shared-api-client'; import { authStore } from '$lib/stores/auth.svelte'; -const API_URL = 'http://localhost:3016'; +// Use client URL in browser (injected by hooks.server.ts), SSR URL on server +function getApiUrl(): string { + if (browser && typeof window !== 'undefined') { + const injectedUrl = (window as unknown as { __PUBLIC_BACKEND_URL__?: string }) + .__PUBLIC_BACKEND_URL__; + if (injectedUrl) return injectedUrl; + } + return process.env.PUBLIC_BACKEND_URL || 'http://localhost:3016'; +} + +const API_URL = getApiUrl(); /** * Storage API client instance diff --git a/apps/storage/apps/web/src/lib/stores/auth.svelte.ts b/apps/storage/apps/web/src/lib/stores/auth.svelte.ts index b4d25a8c7..5988f2a44 100644 --- a/apps/storage/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/storage/apps/web/src/lib/stores/auth.svelte.ts @@ -6,7 +6,19 @@ import { browser } from '$app/environment'; import { initializeWebAuth, type UserData } from '@manacore/shared-auth'; -const MANA_AUTH_URL = 'http://localhost:3001'; +// Get auth URL dynamically at runtime - fallback for SSR and client +function getAuthUrl(): string { + if (browser && typeof window !== 'undefined') { + // Client-side: use injected window variable (set by hooks.server.ts) + const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) + .__PUBLIC_MANA_CORE_AUTH_URL__; + return injectedUrl || 'http://localhost:3001'; + } + // Server-side (SSR): use Docker internal URL for container-to-container communication + return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; +} + +const MANA_AUTH_URL = getAuthUrl(); let _authService: ReturnType['authService'] | null = null; let _tokenManager: ReturnType['tokenManager'] | null = null;