From be75aaa195ade4e41e8225cad7ba06e5df661385 Mon Sep 17 00:00:00 2001 From: Caleb Peffer <44934913+calebpeffer@users.noreply.github.com> Date: Sun, 21 Apr 2024 09:31:22 -0700 Subject: [PATCH] Caleb: first version of supabase proxy to make db authentication optional --- apps/api/src/controllers/auth.ts | 11 +++++++ apps/api/src/controllers/crawl.ts | 15 +++++---- apps/api/src/controllers/scrape.ts | 26 ++++++++------- apps/api/src/services/supabase.ts | 53 +++++++++++++++++++++++++++--- 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/apps/api/src/controllers/auth.ts b/apps/api/src/controllers/auth.ts index 76bacbe..6ae234d 100644 --- a/apps/api/src/controllers/auth.ts +++ b/apps/api/src/controllers/auth.ts @@ -3,6 +3,7 @@ import { getRateLimiter } from "../../src/services/rate-limiter"; import { RateLimiterMode } from "../../src/types"; import { supabase_service } from "../../src/services/supabase"; + export async function authenticateUser( req, res, @@ -13,6 +14,16 @@ export async function authenticateUser( error?: string; status?: number; }> { + + console.log(process.env) + + if(process.env.USE_DB_AUTHENTICATION === "false"){ + console.log("WARNING - YOU'RE bypassing Authentication"); + return { success: true}; + } + + console.log("USING SUPABASE AUTH"); + const authHeader = req.headers.authorization; if (!authHeader) { return { success: false, error: "Unauthorized", status: 401 }; diff --git a/apps/api/src/controllers/crawl.ts b/apps/api/src/controllers/crawl.ts index 17cfa62..36c013e 100644 --- a/apps/api/src/controllers/crawl.ts +++ b/apps/api/src/controllers/crawl.ts @@ -8,6 +8,8 @@ import { addWebScraperJob } from "../../src/services/queue-jobs"; export async function crawlController(req: Request, res: Response) { try { + + console.log("hello") const { success, team_id, error, status } = await authenticateUser( req, res, @@ -16,14 +18,15 @@ export async function crawlController(req: Request, res: Response) { if (!success) { return res.status(status).json({ error }); } - - const { success: creditsCheckSuccess, message: creditsCheckMessage } = - await checkTeamCredits(team_id, 1); - if (!creditsCheckSuccess) { - return res.status(402).json({ error: "Insufficient credits" }); + + if (process.env.USE_DB_AUTHENTICATION === "true") { + const { success: creditsCheckSuccess, message: creditsCheckMessage } = + await checkTeamCredits(team_id, 1); + if (!creditsCheckSuccess) { + return res.status(402).json({ error: "Insufficient credits" }); + } } - // authenticate on supabase const url = req.body.url; if (!url) { return res.status(400).json({ error: "Url is required" }); diff --git a/apps/api/src/controllers/scrape.ts b/apps/api/src/controllers/scrape.ts index 632fff5..47b00f0 100644 --- a/apps/api/src/controllers/scrape.ts +++ b/apps/api/src/controllers/scrape.ts @@ -40,18 +40,22 @@ export async function scrapeHelper( if (filteredDocs.length === 0) { return { success: true, error: "No page found", returnCode: 200 }; } - const { success, credit_usage } = await billTeam( - team_id, - filteredDocs.length - ); - if (!success) { - return { - success: false, - error: - "Failed to bill team. Insufficient credits or subscription not found.", - returnCode: 402, - }; + + if (process.env.USE_DB_AUTHENTICATION === "true") { + const { success, credit_usage } = await billTeam( + team_id, + filteredDocs.length + ); + if (!success) { + return { + success: false, + error: + "Failed to bill team. Insufficient credits or subscription not found.", + returnCode: 402, + }; + } } + return { success: true, data: filteredDocs[0], diff --git a/apps/api/src/services/supabase.ts b/apps/api/src/services/supabase.ts index 49121fa..9a2366d 100644 --- a/apps/api/src/services/supabase.ts +++ b/apps/api/src/services/supabase.ts @@ -1,6 +1,49 @@ -import { createClient } from "@supabase/supabase-js"; +import { createClient, SupabaseClient } from '@supabase/supabase-js'; -export const supabase_service = createClient( - process.env.SUPABASE_URL, - process.env.SUPABASE_SERVICE_TOKEN, -); +// SupabaseService class initializes the Supabase client conditionally based on environment variables. +class SupabaseService { + private client: SupabaseClient | null = null; + + constructor() { + const supabaseUrl = process.env.SUPABASE_URL; + const supabaseServiceToken = process.env.SUPABASE_SERVICE_TOKEN; + + // Only initialize the Supabase client if both URL and Service Token are provided. + if (process.env.USE_DB_AUTHENTICATION === "false") { + + // Warn the user that Authentication is disabled by setting the client to null + console.warn("\x1b[33mAuthentication is disabled. Supabase client will not be initialized.\x1b[0m"); + this.client = null; + } else if (!supabaseUrl || !supabaseServiceToken) { + console.error("\x1b[31mSupabase 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\x1b[0m"); + } else { + this.client = createClient(supabaseUrl, supabaseServiceToken); + } + } + + // Provides access to the initialized Supabase client, if available. + getClient(): SupabaseClient | null { + return this.client; + } +} + +// 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(), { + get: function (target, prop, receiver) { + const client = target.getClient(); + // If the Supabase client is not initialized, intercept property access to provide meaningful error feedback. + if (client === null) { + console.error("Attempted to access Supabase client when it's not configured."); + return () => { + throw new Error("Supabase 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; \ No newline at end of file