From c6832d237c2659860073ffdef02f58bdb1eb13a1 Mon Sep 17 00:00:00 2001 From: Niranjan Kumar Date: Tue, 23 Jun 2026 20:23:16 +0530 Subject: [PATCH] feat: integrate runtime environment configuration and refactor API client to utilize public environment variables --- src/app/(auth)/login/page.tsx | 34 ++++++++++++++++----------------- src/app/layout.tsx | 4 ++++ src/components/contact-form.tsx | 4 ++-- src/components/runtime-env.tsx | 15 +++++++++++++++ src/lib/api-client.ts | 11 +++++++---- src/lib/public-env.ts | 31 ++++++++++++++++++++++++++++++ src/lib/social-login.ts | 2 +- 7 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 src/components/runtime-env.tsx create mode 100644 src/lib/public-env.ts diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 0b9c985..7621fe4 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -34,11 +34,8 @@ import { PublicTenantAuthConfig, } from "@/lib/types"; import { getApiErrorMessage, isNotAllowedError } from "@/lib/errors"; -import { - PLATFORM_TENANT_ID, - PublicOAuthProvider, - SOCIAL_PROVIDER_META, -} from "@/lib/social-login"; +import { getPlatformTenantId, PublicOAuthProvider, SOCIAL_PROVIDER_META } from "@/lib/social-login"; +import { getPublicEnv } from "@/lib/public-env"; import { isWebAuthnSupported } from "@/lib/webauthn-support"; import { LoginFormSkeleton } from "@/components/auth/login-form-skeleton"; @@ -243,16 +240,15 @@ function LoginPageContent() { }, [enrollData]); const { data: authConfig, isLoading: isLoadingAuthConfig } = useQuery({ - queryKey: ["publicAuthConfig", PLATFORM_TENANT_ID], + queryKey: ["publicAuthConfig"], queryFn: async () => { - const { data } = await apiClient.get("/auth/auth-config", { - params: { tenant_id: PLATFORM_TENANT_ID }, - }); + const { data } = await apiClient.get("/auth/auth-config"); return data; }, - enabled: !!PLATFORM_TENANT_ID, }); + const platformTenantId = authConfig?.tenant_id ?? getPlatformTenantId(); + const allowedMethods = new Set(authConfig?.allowed_methods ?? []); const showEmailPassword = allowedMethods.has("email_password"); const showMagicLink = allowedMethods.has("magic_link"); @@ -265,15 +261,17 @@ function LoginPageContent() { const { data: socialProviders = [], isLoading: isLoadingSocialProviders } = useQuery< PublicOAuthProvider[] >({ - queryKey: ["loginSocialProviders", PLATFORM_TENANT_ID], + queryKey: ["loginSocialProviders", platformTenantId], queryFn: async () => { const { data } = await apiClient.get( "/auth/oauth/providers", - { params: { tenant_id: PLATFORM_TENANT_ID } } + platformTenantId + ? { params: { tenant_id: platformTenantId } } + : undefined ); return data; }, - enabled: !!PLATFORM_TENANT_ID && showSocial, + enabled: !!platformTenantId && showSocial, }); const passwordForm = useForm>({ @@ -315,7 +313,7 @@ function LoginPageContent() { const sendMagicLink = async (email: string) => { await apiClient.post("/auth/magic-link/request", { email, - tenant_id: PLATFORM_TENANT_ID || undefined, + tenant_id: platformTenantId || undefined, }); }; @@ -323,7 +321,7 @@ function LoginPageContent() { mutationFn: async (values: { email: string; password: string }) => { const { data } = await apiClient.post("/auth/login", { ...values, - tenant_id: PLATFORM_TENANT_ID || undefined, + tenant_id: platformTenantId || undefined, }); return data; }, @@ -446,7 +444,7 @@ function LoginPageContent() { try { const { data } = await apiClient.post("/auth/webauthn/authenticate/begin", { - tenant_id: PLATFORM_TENANT_ID || undefined, + tenant_id: platformTenantId || undefined, }); const assertion = await startAuthentication({ optionsJSON: data.options }); @@ -465,7 +463,7 @@ function LoginPageContent() { userHandle: assertion.response.userHandle, }, }, - tenant_id: PLATFORM_TENANT_ID || undefined, + tenant_id: platformTenantId || undefined, } ); @@ -482,7 +480,7 @@ function LoginPageContent() { }; const handleSocialLogin = (provider: string, tenantId: string) => { - const baseUrl = process.env.NEXT_PUBLIC_API_URL; + const baseUrl = getPublicEnv().API_URL; const params = new URLSearchParams({ tenant_id: tenantId }); window.location.href = `${baseUrl}/auth/oauth/${provider}/login?${params.toString()}`; }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a87fdd2..b629783 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ThemeProvider } from "@/components/theme-provider"; import Providers from "@/components/providers"; +import { RuntimeEnv } from "@/components/runtime-env"; import { Toaster } from "@/components/ui/sonner"; const geistSans = Geist({ @@ -27,6 +28,9 @@ export default function RootLayout({ }>) { return ( + + + diff --git a/src/components/contact-form.tsx b/src/components/contact-form.tsx index 2ae737e..3988fa5 100644 --- a/src/components/contact-form.tsx +++ b/src/components/contact-form.tsx @@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; +import { getPublicEnv } from "@/lib/public-env"; const COMPANY_SIZES = ["1–10", "11–50", "51–200", "201–1,000", "1,000+"]; const MAU_RANGES = [ @@ -63,8 +64,7 @@ export function ContactForm() { return; } - const apiBase = - process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000/api/v1"; + const apiBase = getPublicEnv().API_URL || "http://localhost:8000/api/v1"; setIsSubmitting(true); try { diff --git a/src/components/runtime-env.tsx b/src/components/runtime-env.tsx new file mode 100644 index 0000000..dd4bc80 --- /dev/null +++ b/src/components/runtime-env.tsx @@ -0,0 +1,15 @@ +export function RuntimeEnv() { + const config = { + API_URL: process.env.NEXT_PUBLIC_API_URL ?? "", + APP_URL: process.env.NEXT_PUBLIC_APP_URL ?? "", + PLATFORM_TENANT_ID: process.env.NEXT_PUBLIC_PLATFORM_TENANT_ID ?? "", + }; + + return ( +