diff --git a/src/integrations/supabase/client.server.ts b/src/integrations/supabase/client.server.ts new file mode 100644 index 0000000..30e131b --- /dev/null +++ b/src/integrations/supabase/client.server.ts @@ -0,0 +1,69 @@ +// This file is automatically generated. Do not edit it directly. +// Server-side Supabase client with service role key - bypasses RLS. +// Use this for admin operations in server functions and server routes only. +// For user-authenticated queries (with RLS), use the auth middleware instead. +import { createClient } from '@supabase/supabase-js'; +import type { Database } from './types'; + +function isNewSupabaseApiKey(value: string): boolean { + return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); +} + +function createSupabaseFetch(supabaseKey: string): typeof fetch { + return (input, init) => { + const headers = new Headers( + typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, + ); + + if (init?.headers) { + new Headers(init.headers).forEach((value, key) => headers.set(key, value)); + } + + // New Supabase API keys are opaque strings, not bearer JWTs. + if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { + headers.delete('Authorization'); + } + + headers.set('apikey', supabaseKey); + return fetch(input, { ...init, headers }); + }; +} + +function createSupabaseAdminClient() { + const SUPABASE_URL = process.env.SUPABASE_URL; + const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; + + if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { + const missing = [ + ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), + ...(!SUPABASE_SERVICE_ROLE_KEY ? ['SUPABASE_SERVICE_ROLE_KEY'] : []), + ]; + const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; + console.error(`[Supabase] ${message}`); + throw new Error(message); + } + + return createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, { + global: { + fetch: createSupabaseFetch(SUPABASE_SERVICE_ROLE_KEY), + }, + auth: { + storage: undefined, + persistSession: false, + autoRefreshToken: false, + } + }); +} + +let _supabaseAdmin: ReturnType | undefined; + +// Server-side Supabase client with service role - bypasses RLS +// SECURITY: Only use this for trusted server-side operations, never expose to client code +// Load inside server handlers: const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); +// Top-level import is safe only in other .server.ts modules - route files and *.functions.ts ship to the client bundle. +export const supabaseAdmin = new Proxy({} as ReturnType, { + get(_, prop, receiver) { + if (!_supabaseAdmin) _supabaseAdmin = createSupabaseAdminClient(); + return Reflect.get(_supabaseAdmin, prop, receiver); + }, +});