From 67ee2662de866dcadaa58dff6030738e3a1c2fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20M=C3=B3ricz?= Date: Mon, 3 Mar 2025 20:36:45 +0100 Subject: [PATCH] feat(auth): force acuc on read replica --- apps/api/src/controllers/auth.ts | 4 ++-- apps/api/src/services/supabase.ts | 35 +++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/api/src/controllers/auth.ts b/apps/api/src/controllers/auth.ts index 2dca4767a..71d3c43aa 100644 --- a/apps/api/src/controllers/auth.ts +++ b/apps/api/src/controllers/auth.ts @@ -6,7 +6,7 @@ import { PlanType, RateLimiterMode, } from "../types"; -import { supabase_service } from "../services/supabase"; +import { supabase_rr_service } from "../services/supabase"; import { withAuth } from "../lib/withAuth"; import { RateLimiterRedis } from "rate-limiter-flexible"; import { sendNotification } from "../services/notification/email_notification"; @@ -100,7 +100,7 @@ export async function getACUC( ? "auth_credit_usage_chunk_extract" : "auth_credit_usage_chunk_test_22_credit_pack_n_extract"; while (retries < maxRetries) { - ({ data, error } = await supabase_service.rpc( + ({ data, error } = await supabase_rr_service.rpc( rpcName, { input_key: api_key }, { get: true }, diff --git a/apps/api/src/services/supabase.ts b/apps/api/src/services/supabase.ts index fa60d5670..37b0d4dc1 100644 --- a/apps/api/src/services/supabase.ts +++ b/apps/api/src/services/supabase.ts @@ -6,9 +6,11 @@ configDotenv(); // SupabaseService class initializes the Supabase client conditionally based on environment variables. class SupabaseService { private client: SupabaseClient | null = null; + private rrClient: SupabaseClient | null = null; constructor() { const supabaseUrl = process.env.SUPABASE_URL; + const supabaseReplicaUrl = process.env.SUPABASE_REPLICA_URL; const supabaseServiceToken = process.env.SUPABASE_SERVICE_TOKEN; const useDbAuthentication = process.env.USE_DB_AUTHENTICATION === "true"; // Only initialize the Supabase client if both URL and Service Token are provided. @@ -18,7 +20,7 @@ class SupabaseService { "Authentication is disabled. Supabase client will not be initialized.", ); this.client = null; - } else if (!supabaseUrl || !supabaseServiceToken) { + } else if (!supabaseUrl || !supabaseServiceToken || !supabaseReplicaUrl) { logger.error( "Supabase environment variables aren't configured correctly. Supabase client will not be initialized. Fix ENV configuration or disable DB authentication with USE_DB_AUTHENTICATION env variable", ); @@ -30,6 +32,8 @@ class SupabaseService { }, }, }); + + this.rrClient = createClient(supabaseReplicaUrl, supabaseServiceToken); } } @@ -37,12 +41,18 @@ class SupabaseService { getClient(): SupabaseClient | null { return this.client; } + + getRRClient(): SupabaseClient | null { + return this.rrClient; + } } +const serv = new SupabaseService(); + // Using a Proxy to handle dynamic access to the Supabase client or service methods. // This approach ensures that if Supabase is not configured, any attempt to use it will result in a clear error. export const supabase_service: SupabaseClient = new Proxy( - new SupabaseService(), + serv, { get: function (target, prop, receiver) { const client = target.getClient(); @@ -61,3 +71,24 @@ export const supabase_service: SupabaseClient = new Proxy( }, }, ) as unknown as SupabaseClient; + +export const supabase_rr_service: SupabaseClient = new Proxy( + serv, + { + get: function (target, prop, receiver) { + const client = target.getRRClient(); + // If the Supabase client is not initialized, intercept property access to provide meaningful error feedback. + if (client === null) { + return () => { + throw new Error("Supabase RR client is not configured."); + }; + } + // Direct access to SupabaseService properties takes precedence. + if (prop in target) { + return Reflect.get(target, prop, receiver); + } + // Otherwise, delegate access to the Supabase client. + return Reflect.get(client, prop, receiver); + }, + }, +) as unknown as SupabaseClient;