From fdd0bb1f7e235a32220cb716370730472c2c6e2a Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 26 Jan 2026 15:25:41 +0100 Subject: [PATCH] Use dynamic env vars for Supabase config Changes $env/static/public to $env/dynamic/public for all Supabase URL and API key configuration. This allows the app to read environment variables at runtime instead of build time, enabling deployment with different configurations without rebuilding the Docker image. Files updated: - hooks.server.ts: Use dynamic env for PUBLIC_SUPABASE_URL/KEY - lib/server/supabase.ts: Lazy-init admin client with dynamic env - lib/server/storage.ts: Use dynamic env for browser-accessible URLs - lib/supabase.ts: Use dynamic env for browser client Co-Authored-By: Claude Opus 4.5 --- src/hooks.server.ts | 13 +++++++------ src/lib/server/storage.ts | 4 ++-- src/lib/server/supabase.ts | 34 +++++++++++++++++++++++----------- src/lib/supabase.ts | 7 +++++-- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/hooks.server.ts b/src/hooks.server.ts index f4f7b87..ce2fd94 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,7 +2,7 @@ import pkg from '@supabase/ssr'; const { createServerClient } = pkg; import { type Handle, redirect } from '@sveltejs/kit'; import { sequence } from '@sveltejs/kit/hooks'; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'; +import { env as publicEnv } from '$env/dynamic/public'; import { env } from '$env/dynamic/private'; import type { Database } from '$lib/types/database'; import { supabaseAdmin } from '$lib/server/supabase'; @@ -11,17 +11,18 @@ import { supabaseAdmin } from '$lib/server/supabase'; let setupCheckCache: { needsSetup: boolean; checkedAt: number } | null = null; const SETUP_CACHE_TTL = 60000; // 1 minute cache -// Use internal URL for server-side operations (Docker network), fallback to public URL -const SERVER_SUPABASE_URL = env.SUPABASE_INTERNAL_URL || PUBLIC_SUPABASE_URL; - /** * Supabase authentication hook * Sets up the Supabase client with cookie handling for SSR */ const supabaseHandle: Handle = async ({ event, resolve }) => { + // Use internal URL for server-side operations (Docker network), fallback to public URL + const supabaseUrl = env.SUPABASE_INTERNAL_URL || publicEnv.PUBLIC_SUPABASE_URL; + const supabaseAnonKey = publicEnv.PUBLIC_SUPABASE_ANON_KEY; + event.locals.supabase = createServerClient( - SERVER_SUPABASE_URL, - PUBLIC_SUPABASE_ANON_KEY, + supabaseUrl, + supabaseAnonKey, { cookies: { getAll: () => event.cookies.getAll(), diff --git a/src/lib/server/storage.ts b/src/lib/server/storage.ts index ce7d7d1..da2d327 100644 --- a/src/lib/server/storage.ts +++ b/src/lib/server/storage.ts @@ -1,5 +1,5 @@ import { supabaseAdmin } from './supabase'; -import { PUBLIC_SUPABASE_URL } from '$env/static/public'; +import { env as publicEnv } from '$env/dynamic/public'; import { S3Client, PutObjectCommand, @@ -18,7 +18,7 @@ export type StorageBucket = 'documents' | 'avatars' | 'event-images'; * This uses PUBLIC_SUPABASE_URL instead of the internal Docker URL */ function getBrowserAccessibleUrl(bucket: StorageBucket, path: string): string { - return `${PUBLIC_SUPABASE_URL}/storage/v1/object/public/${bucket}/${path}`; + return `${publicEnv.PUBLIC_SUPABASE_URL}/storage/v1/object/public/${bucket}/${path}`; } export interface UploadResult { diff --git a/src/lib/server/supabase.ts b/src/lib/server/supabase.ts index 86ac810..9118ac4 100644 --- a/src/lib/server/supabase.ts +++ b/src/lib/server/supabase.ts @@ -1,20 +1,22 @@ import pkg from '@supabase/ssr'; const { createServerClient } = pkg; import { createClient as createSupabaseClient } from '@supabase/supabase-js'; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'; +import { env as publicEnv } from '$env/dynamic/public'; import { env } from '$env/dynamic/private'; import type { Cookies } from '@sveltejs/kit'; import type { Database } from '$lib/types/database'; // Use internal URL for server-side operations (Docker network), fallback to public URL -const SERVER_SUPABASE_URL = env.SUPABASE_INTERNAL_URL || PUBLIC_SUPABASE_URL; +function getSupabaseUrl() { + return env.SUPABASE_INTERNAL_URL || publicEnv.PUBLIC_SUPABASE_URL; +} const SUPABASE_SERVICE_ROLE_KEY = env.SUPABASE_SERVICE_ROLE_KEY || ''; /** * Create a Supabase client for server-side operations with cookie handling */ export function createSupabaseServerClient(cookies: Cookies) { - return createServerClient(SERVER_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, { + return createServerClient(getSupabaseUrl(), publicEnv.PUBLIC_SUPABASE_ANON_KEY, { cookies: { getAll: () => cookies.getAll(), setAll: (cookiesToSet) => { @@ -26,17 +28,27 @@ export function createSupabaseServerClient(cookies: Cookies) { }); } +// Lazy-initialized admin client to ensure env vars are loaded +let _supabaseAdmin: ReturnType> | null = null; + /** * Supabase Admin client with service role key * Use this for administrative operations that bypass RLS */ -export const supabaseAdmin = createSupabaseClient( - SERVER_SUPABASE_URL, - SUPABASE_SERVICE_ROLE_KEY, - { - auth: { - autoRefreshToken: false, - persistSession: false +export const supabaseAdmin = new Proxy({} as ReturnType>, { + get(_, prop) { + if (!_supabaseAdmin) { + _supabaseAdmin = createSupabaseClient( + getSupabaseUrl(), + env.SUPABASE_SERVICE_ROLE_KEY || '', + { + auth: { + autoRefreshToken: false, + persistSession: false + } + } + ); } + return (_supabaseAdmin as any)[prop]; } -); +}); diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts index 0d101a0..2f7a61a 100644 --- a/src/lib/supabase.ts +++ b/src/lib/supabase.ts @@ -1,11 +1,14 @@ import pkg from '@supabase/ssr'; const { createBrowserClient } = pkg; -import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'; +import { env } from '$env/dynamic/public'; import type { Database } from './types/database'; /** * Create a Supabase client for browser-side operations */ export function createClient() { - return createBrowserClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY); + return createBrowserClient( + env.PUBLIC_SUPABASE_URL, + env.PUBLIC_SUPABASE_ANON_KEY + ); }