gohorsejobs/frontend/src/lib/config.ts
2026-02-09 14:11:05 +00:00

129 lines
3.7 KiB
TypeScript

/**
* Runtime Configuration Service
*
* This service fetches and caches configuration from the /api/config endpoint,
* allowing the application to use runtime environment variables instead of
* build-time values.
*
* Usage:
* import { getApiUrl, getBackofficeUrl, initConfig } from '@/lib/config';
*
* // Initialize once at app startup (optional, auto-initializes on first use)
* await initConfig();
*
* // Get URLs (returns cached values or defaults)
* const apiUrl = getApiUrl();
*/
export interface RuntimeConfig {
apiUrl: string;
backofficeUrl: string;
seederApiUrl: string;
scraperApiUrl: string;
}
// Default values (fallback to build-time env or hardcoded defaults)
const defaultConfig: RuntimeConfig = {
apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8521',
backofficeUrl: process.env.NEXT_PUBLIC_BACKOFFICE_URL || 'http://localhost:3001',
seederApiUrl: process.env.NEXT_PUBLIC_SEEDER_API_URL || 'http://localhost:3002',
scraperApiUrl: process.env.NEXT_PUBLIC_SCRAPER_API_URL || 'http://localhost:3003',
};
let cachedConfig: RuntimeConfig | null = null;
let isInitialized = false;
let initPromise: Promise<RuntimeConfig> | null = null;
/**
* Initialize the config from the server.
* Safe to call multiple times - only fetches once.
*/
export async function initConfig(): Promise<RuntimeConfig> {
// If already initialized, return cached config
if (isInitialized && cachedConfig) {
return cachedConfig;
}
// If already initializing, wait for that promise
if (initPromise) {
return initPromise;
}
// Start initialization
initPromise = (async () => {
try {
// Only fetch if we're in the browser
if (typeof window !== 'undefined') {
const response = await fetch('/api/config');
if (response.ok) {
cachedConfig = await response.json();
console.log('[Config] Loaded runtime config:', cachedConfig);
} else {
console.warn('[Config] Failed to fetch config, using defaults');
cachedConfig = defaultConfig;
}
} else {
// Server-side: use defaults
cachedConfig = defaultConfig;
}
} catch (error) {
console.warn('[Config] Error fetching config, using defaults:', error);
cachedConfig = defaultConfig;
}
isInitialized = true;
return cachedConfig!;
})();
return initPromise;
}
/**
* Get the full config object.
* Returns cached config if initialized, otherwise defaults.
*/
export function getConfig(): RuntimeConfig {
// Trigger async init in background if not initialized
if (!isInitialized && typeof window !== 'undefined') {
initConfig();
}
return cachedConfig || defaultConfig;
}
/**
* Get the API base URL.
* @returns The API URL (e.g., "https://api.gohorsejobs.com")
*/
export function getApiUrl(): string {
return getConfig().apiUrl;
}
/**
* Get the full API URL with /api/v1 suffix.
* @returns The full API URL (e.g., "https://api.gohorsejobs.com/api/v1")
*/
export function getApiV1Url(): string {
return `${getConfig().apiUrl}/api/v1`;
}
/**
* Get the Backoffice URL.
*/
export function getBackofficeUrl(): string {
return getConfig().backofficeUrl;
}
/**
* Get the Seeder API URL.
*/
export function getSeederApiUrl(): string {
// Return backend API URL + /seeder path prefix
return `${getConfig().apiUrl}/api/v1/seeder`;
}
/**
* Get the Scraper API URL.
*/
export function getScraperApiUrl(): string {
return getConfig().scraperApiUrl;
}