Skip to content

Commit

Permalink
feat(auth): force acuc on read replica
Browse files Browse the repository at this point in the history
  • Loading branch information
mogery committed Mar 3, 2025
1 parent 64af3ba commit 67ee266
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
4 changes: 2 additions & 2 deletions apps/api/src/controllers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 },
Expand Down
35 changes: 33 additions & 2 deletions apps/api/src/services/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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",
);
Expand All @@ -30,19 +32,27 @@ class SupabaseService {
},
},
});

this.rrClient = createClient(supabaseReplicaUrl, supabaseServiceToken);
}
}

// Provides access to the initialized Supabase client, if available.
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();
Expand All @@ -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;

0 comments on commit 67ee266

Please sign in to comment.