import { plainToInstance } from 'class-transformer'; import { IsString, IsUrl, IsInt, Min, Max, IsOptional, validateSync, IsEnum, IsNumber, IsBoolean, } from 'class-validator'; enum NodeEnv { Development = 'development', Production = 'production', Test = 'test', } class EnvironmentVariables { @IsEnum(NodeEnv) @IsOptional() NODE_ENV: NodeEnv = NodeEnv.Development; // @IsString() // MYSQL_URL!: string; @IsString() MONGO_URL!: string; @IsString() JWT_SECRET!: string; @IsInt() @Min(60) @Max(86400) @IsOptional() JWT_EXPIRES_IN_SECONDS: number = 43200; // 12 hours @IsString() @IsOptional() ENCRYPTION_KEY?: string; @IsInt() @Min(1024) @Max(65535) @IsOptional() PORT: number = 3000; // ===== Redis Config ===== @IsString() REDIS_HOST!: string; @IsNumber() REDIS_PORT!: number; @IsOptional() @IsString() REDIS_PASSWORD?: string; // empty string is allowed @IsOptional() @IsNumber() REDIS_DB?: number; @IsOptional() @IsString() REDIS_KEY_PREFIX?: string; // If you later add TLS: @IsOptional() @IsBoolean() REDIS_TLS?: boolean; } export function validateEnvironment(config: Record) { const validatedConfig = plainToInstance(EnvironmentVariables, config, { enableImplicitConversion: true, }); const errors = validateSync(validatedConfig, { skipMissingProperties: false, }); if (errors.length > 0) { const messages = errors .map((error) => { const constraints = error.constraints; return constraints ? Object.values(constraints).join(', ') : ''; }) .filter(Boolean); throw new Error(`Environment validation failed:\n${messages.join('\n')}`); } return validatedConfig; }