2025-12-03 21:56:50 +00:00
import { z } from 'zod' ;
2026-01-16 11:15:20 +00:00
import { logger } from '@/utils/logger' ;
2025-12-03 21:56:50 +00:00
// Schéma de validation pour les variables d'environnement
2025-12-16 19:40:16 +00:00
// Aligné avec FRONTEND_INTEGRATION.md
2026-01-15 18:26:53 +00:00
// Support URLs relatives (commençant par /) ou absolues
const urlOrPathSchema = z . string ( ) . refine (
( val ) = > {
if ( ! val ) return false ;
// Accepter les URLs absolues (http://, https://, ws://, wss://)
if ( /^https?:\/\// . test ( val ) || /^wss?:\/\// . test ( val ) ) {
try {
new URL ( val ) ;
return true ;
} catch {
return false ;
}
}
// Accepter les chemins relatifs (commençant par /)
return val . startsWith ( '/' ) ;
} ,
{ message : 'Must be a valid URL or a path starting with /' }
) ;
2026-02-10 21:51:51 +00:00
// --- Domain (single source of truth for frontend URLs) ---
// Change VITE_DOMAIN in .env.local to switch domain everywhere.
const domain = import . meta . env . VITE_DOMAIN || 'veza.fr' ;
2025-12-03 21:56:50 +00:00
const envSchema = z . object ( {
2026-02-10 21:51:51 +00:00
VITE_DOMAIN : z.string ( ) . default ( 'veza.fr' ) ,
2026-01-15 18:26:53 +00:00
VITE_API_URL : urlOrPathSchema.default ( '/api/v1' ) ,
2026-02-10 21:51:51 +00:00
VITE_WS_URL : urlOrPathSchema.default ( ` ws:// ${ domain } :8081/ws ` ) ,
VITE_STREAM_URL : urlOrPathSchema.default ( ` ws:// ${ domain } :8082/stream ` ) ,
2026-01-15 18:26:53 +00:00
VITE_UPLOAD_URL : urlOrPathSchema.default ( '/upload' ) ,
2025-12-03 21:56:50 +00:00
VITE_APP_NAME : z.string ( ) . default ( 'Veza' ) ,
2026-01-11 15:33:12 +00:00
VITE_API_VERSION : z.string ( ) . default ( 'v1' ) ,
2025-12-03 21:56:50 +00:00
VITE_DEBUG : z
. string ( )
2025-12-13 02:34:34 +00:00
. transform ( ( val ) = > val === 'true' || val === '1' )
2025-12-03 21:56:50 +00:00
. default ( 'false' ) ,
VITE_USE_MSW : z
. string ( )
2025-12-13 02:34:34 +00:00
. transform ( ( val ) = > val === '1' || val === 'true' )
2025-12-03 21:56:50 +00:00
. default ( '0' ) ,
2026-02-14 20:45:15 +00:00
VITE_HYPERSWITCH_PUBLISHABLE_KEY : z.string ( ) . optional ( ) ,
2025-12-03 21:56:50 +00:00
VITE_FCM_VAPID_KEY : z.string ( ) . optional ( ) ,
2025-12-27 00:58:49 +00:00
// FIX #20: Configuration Sentry pour error tracking
VITE_SENTRY_DSN : z.string ( ) . url ( ) . optional ( ) ,
2025-12-03 21:56:50 +00:00
} ) ;
// Validation et parsing des variables d'environnement
const parseEnv = ( ) = > {
try {
return envSchema . parse ( {
2026-02-10 21:51:51 +00:00
VITE_DOMAIN : import.meta.env.VITE_DOMAIN ,
2025-12-16 19:40:16 +00:00
VITE_API_URL : import.meta.env.VITE_API_URL ,
VITE_WS_URL : import.meta.env.VITE_WS_URL ,
2025-12-03 21:56:50 +00:00
VITE_STREAM_URL : import.meta.env.VITE_STREAM_URL ,
VITE_UPLOAD_URL : import.meta.env.VITE_UPLOAD_URL ,
VITE_APP_NAME : import.meta.env.VITE_APP_NAME ,
2026-01-11 15:33:12 +00:00
VITE_API_VERSION : import.meta.env.VITE_API_VERSION ,
2025-12-03 21:56:50 +00:00
VITE_DEBUG : import.meta.env.VITE_DEBUG ,
2026-01-13 18:47:57 +00:00
VITE_USE_MSW : import.meta.env.VITE_USE_MSW ,
2026-02-14 20:45:15 +00:00
VITE_HYPERSWITCH_PUBLISHABLE_KEY :
import . meta . env . VITE_HYPERSWITCH_PUBLISHABLE_KEY ,
2026-01-13 18:47:57 +00:00
VITE_FCM_VAPID_KEY : import.meta.env.VITE_FCM_VAPID_KEY ,
VITE_SENTRY_DSN : import.meta.env.VITE_SENTRY_DSN ,
} ) ;
2025-12-03 21:56:50 +00:00
} catch ( error ) {
if ( error instanceof z . ZodError ) {
2026-01-16 11:15:20 +00:00
logger . error ( '❌ Invalid environment variables' , {
errors : error.errors ,
} ) ;
2025-12-03 21:56:50 +00:00
throw new Error (
` Environment variables validation failed: ${ error . errors
2025-12-13 02:34:34 +00:00
. map ( ( e ) = > ` ${ e . path . join ( '.' ) } : ${ e . message } ` )
. join ( ', ' ) } ` ,
2025-12-03 21:56:50 +00:00
) ;
}
throw error ;
}
} ;
// Variables d'environnement validées
const validatedEnv = parseEnv ( ) ;
2026-02-07 19:36:48 +00:00
// En dev, alerter si l'API est en cross-origin : les cookies ne seront pas envoyés (SameSite),
// ce qui provoque 401 après login et redirections en boucle. Utiliser VITE_API_URL=/api/v1 (proxy).
if ( import . meta . env . DEV && typeof window !== 'undefined' ) {
const apiUrl = validatedEnv . VITE_API_URL ;
if ( apiUrl . startsWith ( 'http' ) ) {
try {
const apiOrigin = new URL ( apiUrl ) . origin ;
if ( window . location . origin !== apiOrigin ) {
logger . warn (
'[Config] API is cross-origin: cookies will not be sent, login may fail or redirect in a loop. Use VITE_API_URL=/api/v1 so the Vite proxy is used (same origin).' ,
{ apiOrigin , pageOrigin : window.location.origin }
) ;
}
} catch {
// ignore invalid URL
}
}
}
2025-12-03 21:56:50 +00:00
// Export de l'objet env avec types
export const env = {
2026-02-10 21:51:51 +00:00
DOMAIN : validatedEnv.VITE_DOMAIN ,
2025-12-16 19:40:16 +00:00
API_URL : validatedEnv.VITE_API_URL ,
WS_URL : validatedEnv.VITE_WS_URL ,
2025-12-03 21:56:50 +00:00
STREAM_URL : validatedEnv.VITE_STREAM_URL ,
UPLOAD_URL : validatedEnv.VITE_UPLOAD_URL ,
APP_NAME : validatedEnv.VITE_APP_NAME ,
2026-01-11 15:33:12 +00:00
API_VERSION : validatedEnv.VITE_API_VERSION ,
2025-12-03 21:56:50 +00:00
DEBUG : validatedEnv.VITE_DEBUG ,
USE_MSW : validatedEnv.VITE_USE_MSW ,
FCM_VAPID_KEY : validatedEnv.VITE_FCM_VAPID_KEY ,
2025-12-27 00:58:49 +00:00
// FIX #20: Configuration Sentry
SENTRY_DSN : validatedEnv.VITE_SENTRY_DSN ,
2025-12-03 21:56:50 +00:00
} as const ;
// Type pour les variables d'environnement
export type Env = typeof env ;