129 lines
3.7 KiB
TypeScript
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;
|
|
}
|