From c5e2fc35143e47730cb097a12dea250db7ded902 Mon Sep 17 00:00:00 2001 From: ajaysi Date: Sat, 4 Apr 2026 07:08:34 +0530 Subject: [PATCH] fix: require REACT_APP_API_URL in production, throw clear error if missing --- frontend/src/api/client.ts | 20 +++++++++++++------ frontend/src/hooks/useCollections.ts | 10 +++++++++- frontend/src/hooks/useContentAssets.ts | 10 +++++++++- frontend/src/hooks/useRealTimeData.ts | 11 ++++++++-- .../services/hallucinationDetectorService.ts | 10 ++++++++-- frontend/src/services/seoApiService.ts | 12 +++++++++-- .../src/services/writingAssistantService.ts | 10 ++++++++-- 7 files changed, 67 insertions(+), 16 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 746ab4eb..3f8de8d7 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -64,13 +64,21 @@ export const getAuthTokenGetter = (): (() => Promise) | null => { // Get API URL from environment variables export const getApiUrl = () => { - if (process.env.NODE_ENV === 'production') { - // In production, use the environment variable or fallback - return process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL; + const apiUrl = process.env.REACT_APP_API_URL; + const isProduction = process.env.NODE_ENV === 'production'; + + // In production, require REACT_APP_API_URL to be set + if (isProduction && !apiUrl) { + console.error('[apiClient] ❌ REACT_APP_API_URL is not set for production! Please configure in Vercel environment variables.'); + throw new Error('REACT_APP_API_URL environment variable is required for production. Please set it in your Vercel project settings.'); } - // In development, prefer the local backend to avoid CORS/proxy header stripping. - // If an ngrok URL is set in env but we're on localhost, override to localhost:8000. - const envUrl = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL; + + if (isProduction) { + return apiUrl; + } + + // In development, use localhost by default + const envUrl = process.env.REACT_APP_API_URL; const isLocalhost = typeof window !== 'undefined' && window.location.hostname === 'localhost'; const isNgrok = envUrl && envUrl.includes('ngrok'); if (isLocalhost) { diff --git a/frontend/src/hooks/useCollections.ts b/frontend/src/hooks/useCollections.ts index 555b5613..cef7ceb3 100644 --- a/frontend/src/hooks/useCollections.ts +++ b/frontend/src/hooks/useCollections.ts @@ -26,7 +26,15 @@ export interface CollectionUpdateRequest { cover_asset_id?: number; } -const API_BASE_URL = process.env.REACT_APP_API_URL || process.env.REACT_APP_API_BASE_URL || 'http://localhost:8000'; +const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; +}; + +const API_BASE_URL = getApiBaseUrl(); export const useCollections = () => { const { getToken } = useAuth(); diff --git a/frontend/src/hooks/useContentAssets.ts b/frontend/src/hooks/useContentAssets.ts index 5cc9492f..f566a5aa 100644 --- a/frontend/src/hooks/useContentAssets.ts +++ b/frontend/src/hooks/useContentAssets.ts @@ -49,7 +49,15 @@ export interface AssetListResponse { offset: number; } -const API_BASE_URL = process.env.REACT_APP_API_URL || process.env.REACT_APP_API_BASE_URL || 'http://localhost:8000'; +const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; +}; + +const API_BASE_URL = getApiBaseUrl(); export const useContentAssets = (filters: AssetFilters = {}) => { const { getToken } = useAuth(); diff --git a/frontend/src/hooks/useRealTimeData.ts b/frontend/src/hooks/useRealTimeData.ts index bede6e75..e7f2e72f 100644 --- a/frontend/src/hooks/useRealTimeData.ts +++ b/frontend/src/hooks/useRealTimeData.ts @@ -50,8 +50,15 @@ export const useRealTimeData = (options: RealTimeDataOptions) => { try { // Build WebSocket URL from environment variables - // Consistent with API URL pattern - no hardcoded localhost - const apiUrl = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL || ''; + const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; + }; + + const apiUrl = getApiBaseUrl(); // In development, use proxy (empty string means use same origin) // In production, derive WebSocket URL from API URL diff --git a/frontend/src/services/hallucinationDetectorService.ts b/frontend/src/services/hallucinationDetectorService.ts index 7ffb5455..171413f4 100644 --- a/frontend/src/services/hallucinationDetectorService.ts +++ b/frontend/src/services/hallucinationDetectorService.ts @@ -77,8 +77,14 @@ class HallucinationDetectorService { private baseUrl: string; constructor() { - // Consistent API URL pattern - no hardcoded localhost fallback - this.baseUrl = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL || ''; + const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; + }; + this.baseUrl = getApiBaseUrl(); } /** diff --git a/frontend/src/services/seoApiService.ts b/frontend/src/services/seoApiService.ts index e43ea7b6..e4625ce4 100644 --- a/frontend/src/services/seoApiService.ts +++ b/frontend/src/services/seoApiService.ts @@ -12,8 +12,16 @@ import { CopilotSuggestion } from '../types/seoCopilotTypes'; -// Consistent API URL pattern - use same env vars as other services -const API_BASE_URL = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL || ''; +// API URL - require REACT_APP_API_URL in production +const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; +}; + +const API_BASE_URL = getApiBaseUrl(); class SEOApiService { private baseUrl: string; diff --git a/frontend/src/services/writingAssistantService.ts b/frontend/src/services/writingAssistantService.ts index 9c83c894..69557839 100644 --- a/frontend/src/services/writingAssistantService.ts +++ b/frontend/src/services/writingAssistantService.ts @@ -21,8 +21,14 @@ export interface WASuggestResponse { class WritingAssistantService { private baseUrl: string; constructor() { - // Consistent API URL pattern - no hardcoded localhost fallback - this.baseUrl = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL || ''; + const getApiBaseUrl = () => { + const url = process.env.REACT_APP_API_URL; + if (process.env.NODE_ENV === 'production' && !url) { + throw new Error('REACT_APP_API_URL environment variable is required for production'); + } + return url || 'http://localhost:8000'; + }; + this.baseUrl = getApiBaseUrl(); } async suggest(text: string): Promise {