updates
This commit is contained in:
@@ -45,7 +45,52 @@ export const API_BASE_URL = isServer
|
||||
|
||||
// Internal API key for server-side requests (must match backend INTERNAL_API_KEY)
|
||||
// This is only used when calling backend directly (build time or internal requests)
|
||||
export const INTERNAL_API_KEY = process.env.INTERNAL_API_KEY || '9hZtPwyScigoBAl59Uvcz_9VztSRC6Zt_6L1B2xTM2M';
|
||||
// SECURITY: Never hardcode API keys in production - always use environment variables
|
||||
const getInternalApiKey = (): string => {
|
||||
const apiKey = process.env.INTERNAL_API_KEY;
|
||||
|
||||
// Check if we're in build phase (Next.js build context)
|
||||
// During build, NEXT_RUNTIME is typically not set
|
||||
// Also check for specific build phases
|
||||
const isBuildTime =
|
||||
!process.env.NEXT_RUNTIME || // Most reliable indicator - not set during build
|
||||
process.env.NEXT_PHASE === 'phase-production-build' ||
|
||||
process.env.NEXT_PHASE === 'phase-production-compile' ||
|
||||
process.env.NEXT_PHASE === 'phase-development-build';
|
||||
|
||||
if (!apiKey) {
|
||||
// During build time, be lenient - allow build to proceed
|
||||
// The key will be validated when actually used (in getApiHeaders)
|
||||
if (isBuildTime) {
|
||||
// Build time: allow fallback (will be validated when actually used)
|
||||
return 'build-time-fallback-key';
|
||||
}
|
||||
|
||||
// Runtime production: require the key (but only validate when actually used)
|
||||
if (isProduction) {
|
||||
// Don't throw here - validate lazily in getApiHeaders when actually needed
|
||||
// This allows the build to complete even if key is missing
|
||||
return 'runtime-requires-key';
|
||||
}
|
||||
|
||||
// Development fallback (only for local development)
|
||||
return 'dev-key-not-for-production';
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
};
|
||||
|
||||
// Lazy getter - only evaluates when accessed
|
||||
let _internalApiKey: string | null = null;
|
||||
export const getInternalApiKeyLazy = (): string => {
|
||||
if (_internalApiKey === null) {
|
||||
_internalApiKey = getInternalApiKey();
|
||||
}
|
||||
return _internalApiKey;
|
||||
};
|
||||
|
||||
// For backward compatibility - evaluates at module load but uses lenient validation
|
||||
export const INTERNAL_API_KEY = getInternalApiKeyLazy();
|
||||
|
||||
// Helper to get headers for API requests
|
||||
// Adds API key header when calling internal backend directly
|
||||
@@ -55,9 +100,25 @@ export const getApiHeaders = (): Record<string, string> => {
|
||||
};
|
||||
|
||||
// If we're calling the internal backend directly (not through nginx),
|
||||
// add the API key header
|
||||
// add the API key header (lazy evaluation - only when actually needed)
|
||||
if (isServer && API_BASE_URL.includes('127.0.0.1:1086')) {
|
||||
headers['X-Internal-API-Key'] = INTERNAL_API_KEY;
|
||||
const apiKey = getInternalApiKeyLazy();
|
||||
|
||||
// Validate API key when actually used (not at module load time)
|
||||
if (apiKey === 'runtime-requires-key' && isProduction) {
|
||||
const actualKey = process.env.INTERNAL_API_KEY;
|
||||
if (!actualKey) {
|
||||
throw new Error(
|
||||
'INTERNAL_API_KEY environment variable is required in production runtime. ' +
|
||||
'Set it in your .env.production file or environment variables.'
|
||||
);
|
||||
}
|
||||
// Update cached value
|
||||
_internalApiKey = actualKey;
|
||||
headers['X-Internal-API-Key'] = actualKey;
|
||||
} else {
|
||||
headers['X-Internal-API-Key'] = apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
|
||||
@@ -10,6 +10,40 @@ export const FALLBACK_IMAGES = {
|
||||
DEFAULT: '/images/logo.png'
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the correct image URL for the current environment
|
||||
*
|
||||
* During build: Use internal backend URL so Next.js can fetch images
|
||||
* During runtime (client): Use relative URLs (nginx serves media files)
|
||||
* During runtime (server/SSR): Use relative URLs (nginx serves media files)
|
||||
* In development: Use API_BASE_URL (which points to backend)
|
||||
*/
|
||||
function getImageBaseUrl(): string {
|
||||
const isServer = typeof window === 'undefined';
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
// Check if we're in build phase (Next.js build context)
|
||||
const isBuildTime =
|
||||
!process.env.NEXT_RUNTIME || // Not set during build
|
||||
process.env.NEXT_PHASE === 'phase-production-build' ||
|
||||
process.env.NEXT_PHASE === 'phase-production-compile';
|
||||
|
||||
// During build time in production: use internal backend URL
|
||||
// This allows Next.js to fetch images during static generation
|
||||
if (isProduction && isBuildTime && isServer) {
|
||||
return process.env.INTERNAL_API_URL || 'http://127.0.0.1:1086';
|
||||
}
|
||||
|
||||
// Runtime (both client and server): use relative URLs
|
||||
// Nginx will serve /media/ files directly
|
||||
if (isProduction) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Development: use API_BASE_URL (which points to backend)
|
||||
return API_BASE_URL;
|
||||
}
|
||||
|
||||
export function getValidImageUrl(imageUrl?: string, fallback?: string): string {
|
||||
if (!imageUrl || imageUrl.trim() === '') {
|
||||
return fallback || FALLBACK_IMAGES.DEFAULT;
|
||||
@@ -20,22 +54,28 @@ export function getValidImageUrl(imageUrl?: string, fallback?: string): string {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
// If it starts with /media/, it's a Django media file - prepend API base URL
|
||||
// Get the base URL for images (handles client/server differences)
|
||||
const baseUrl = getImageBaseUrl();
|
||||
|
||||
// If it starts with /media/, it's a Django media file
|
||||
if (imageUrl.startsWith('/media/')) {
|
||||
return `${API_BASE_URL}${imageUrl}`;
|
||||
// In production client-side, baseUrl is empty, so this becomes /media/... (correct)
|
||||
// In production server-side, baseUrl is the domain, so this becomes https://domain.com/media/... (correct)
|
||||
return `${baseUrl}${imageUrl}`;
|
||||
}
|
||||
|
||||
// If it starts with /images/, it's a local public file
|
||||
// If it starts with /images/, it's a local public file (always relative)
|
||||
if (imageUrl.startsWith('/images/')) {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
// If it starts with /, check if it's a media file
|
||||
if (imageUrl.startsWith('/')) {
|
||||
// If it contains /media/, prepend API base URL
|
||||
// If it contains /media/, prepend base URL
|
||||
if (imageUrl.includes('/media/')) {
|
||||
return `${API_BASE_URL}${imageUrl}`;
|
||||
return `${baseUrl}${imageUrl}`;
|
||||
}
|
||||
// Other absolute paths (like /static/) are served directly
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user