import { plainToInstance } from 'class-transformer'; import { IsEnum, IsNumber, IsOptional, IsString, validateSync } from 'class-validator'; enum Environment { Development = 'development', Production = 'production', Test = 'test', } export class EnvironmentVariables { @IsEnum(Environment) @IsOptional() NODE_ENV: Environment = Environment.Development; @IsNumber() @IsOptional() PORT: number = 3000; @IsString() DATABASE_URL: string; @IsString() @IsOptional() REDIS_HOST: string = 'localhost'; @IsNumber() @IsOptional() REDIS_PORT: number = 6379; @IsString() @IsOptional() REDIS_PASSWORD?: string; @IsString() JWT_USER_SECRET: string; @IsString() @IsOptional() JWT_USER_EXPIRES_IN: string = '7d'; @IsString() JWT_ADMIN_ACCESS_SECRET: string; @IsString() @IsOptional() JWT_ADMIN_ACCESS_EXPIRES_IN: string = '1h'; @IsString() JWT_ADMIN_REFRESH_SECRET: string; @IsString() @IsOptional() JWT_ADMIN_REFRESH_EXPIRES_IN: string = '7d'; @IsString() @IsOptional() RAYGAN_SMS_USERNAME?: string; @IsString() @IsOptional() RAYGAN_SMS_PASSWORD?: string; @IsString() @IsOptional() RAYGAN_SMS_LINE_NUMBER?: string; @IsString() @IsOptional() RAYGAN_SMS_API_BASE_URL: string = 'https://api.raygansms.com'; @IsString() @IsOptional() SMS_DRIVER: string = 'mock'; @IsString() @IsOptional() STORAGE_DRIVER: string = 'local'; @IsString() @IsOptional() STORAGE_LOCAL_ROOT: string = './uploads'; @IsString() @IsOptional() STORAGE_PUBLIC_URL: string = 'http://localhost:3000/uploads'; @IsString() @IsOptional() CORS_ADMIN_ORIGIN: string = 'http://localhost:5173'; @IsString() @IsOptional() CORS_CLIENT_ORIGIN: string = 'http://localhost:5174'; } export function validateEnv(config: Record) { const validatedConfig = plainToInstance(EnvironmentVariables, config, { enableImplicitConversion: true, }); const errors = validateSync(validatedConfig, { skipMissingProperties: false }); if (errors.length > 0) { throw new Error(`Config validation error: ${errors.toString()}`); } return validatedConfig; }