import { plainToInstance } from 'class-transformer'; import { IsInt, IsNotEmpty, IsOptional, IsString, IsUrl, Max, Min, validateSync } from 'class-validator'; /** Environment variables read by the app. A missing or malformed required one stops the startup. */ export class EnvironmentVariables { @IsString() @IsNotEmpty() DATABASE_URL!: string; /** HTTPS only: the Hiboutik credentials are sent with HTTP Basic auth. */ @IsUrl({ protocols: ['https'], require_protocol: true }) HIBOUTIK_BASE_URL!: string; @IsString() @IsNotEmpty() HIBOUTIK_USERNAME!: string; @IsString() @IsNotEmpty() HIBOUTIK_PASSWORD!: string; @IsOptional() @IsInt() @Min(1) @Max(65535) PORT?: number; /** Comma-separated list of origins allowed by CORS. Empty or unset: CORS stays disabled. */ @IsOptional() @IsString() CORS_ORIGINS?: string; /** Number of reverse proxies in front of the app (so the real client IP is used for rate limiting). Unset: no proxy. */ @IsOptional() @IsInt() @Min(1) @Max(10) TRUST_PROXY?: number; } /** `ConfigModule` validation hook: fails fast with the names of the faulty variables (never their values, they may be secrets). */ export function validateEnv(config: Record): Record { const validated = plainToInstance(EnvironmentVariables, config, { enableImplicitConversion: true }); const errors = validateSync(validated, { whitelist: false }); if (errors.length > 0) { const details = errors.map((error) => `${error.property}: ${Object.values(error.constraints ?? {}).join(', ')}`).join('\n '); throw new Error(`Invalid environment configuration:\n ${details}`); } return config; }