import { plainToClass } from 'class-transformer'; import { IsBoolean, IsEnum, IsNotEmpty, IsNumber, IsString, Max, Min, validateSync } from 'class-validator'; import { NodeEnv } from '../types/NodeEnv'; import { ShopFiatCurrency } from '../types/ShopFiatCurrency'; import { IsBase64 } from '../validation/decorators/isBase64'; import { IsConfirmationTiers } from '../validation/decorators/isConfirmationTiers'; import { IsEnabledPaymentMethods } from '../validation/decorators/isEnabledPaymentMethods'; import { ElectrumNetwork } from '../types/ElectrumNetwork'; import { MoneroNetwork } from '../types/MoneroNetwork'; class EnvironmentVariables { @IsNotEmpty() @IsEnum(NodeEnv) NODE_ENV: NodeEnv; @IsNotEmpty() @IsNumber() BACKEND_PORT: number; @IsNotEmpty() @IsString() POSTGRES_USER: string; @IsNotEmpty() @IsString() POSTGRES_PASSWORD: string; @IsNotEmpty() @IsString() POSTGRES_DB: string; @IsNotEmpty() @IsString() POSTGRES_HOST: string; @IsNotEmpty() @IsNumber() POSTGRES_PORT: number; @IsNotEmpty() @IsBoolean() POSTGRES_MIGRATIONS_RUN: boolean; @IsNotEmpty() @IsString() CORS_ORIGINS: string; @IsNotEmpty() @IsString() JWT_SECRET: string; @IsNotEmpty() @IsNumber() JWT_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() CMS_PASSWORD: string; @IsNotEmpty() @IsString() SHOP_NAME: string; @IsNotEmpty() @IsEnum(ShopFiatCurrency) SHOP_FIAT_CURRENCY: ShopFiatCurrency; @IsNotEmpty() @IsString() MULTER_PRODUCT_THUMB_ALLOWED_MIMES: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(10 * 1024 * 1024) // 10MB MULTER_PRODUCT_THUMB_MAX_FILE_BYTES: number; @IsNotEmpty() @IsString() MULTER_SHOP_LOGO_ALLOWED_MIMES: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(10 * 1024 * 1024) // 10MB MULTER_SHOP_LOGO_MAX_FILE_BYTES: number; @IsNotEmpty() @IsString() MULTER_SHOP_FAVICON_ALLOWED_MIMES: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(10 * 1024 * 1024) // 10MB MULTER_SHOP_FAVICON_MAX_FILE_BYTES: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(256) VALIDATION_PRODUCT_TITLE_MAX_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(256) VALIDATION_CATEGORY_NAME_MAX_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(64) VALIDATION_DISCOUNT_CODE_MAX_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(100) VALIDATION_VARIANT_IMAGES_MAX: number; @IsNotEmpty() @IsString() MULTER_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(50 * 1024 * 1024) // 50MB MULTER_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(20) VALIDATION_DIGITAL_STOCK_ATTACHMENTS_MAX: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(1000) VALIDATION_SHIPPING_NOTE_MIN_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(10000) VALIDATION_SHIPPING_NOTE_MAX_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(10000) VALIDATION_ORDER_MESSAGE_MAX_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(4) @Max(8) CAPTCHA_LENGTH: number; @IsNotEmpty() @IsNumber() @Min(1000) @Max(3_600_000) THROTTLE_TTL_MS: number; @IsNotEmpty() @IsNumber() @Min(1) @Max(10_000) THROTTLE_LIMIT: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_JWT_SECRET: string; @IsNotEmpty() @IsString() SIGNED_COOKIE_FEEDBACK_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_FEEDBACK_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_CART_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_CART_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_CAPTCHA_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_CAPTCHA_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_DISCOUNT_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_DISCOUNT_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_ERROR_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_ERROR_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_CHECKOUT_SESSION_NAME: string; @IsNotEmpty() @IsString() SIGNED_COOKIE_ORDER_AUTH_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_ORDER_AUTH_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() SIGNED_COOKIE_THEME_NAME: string; @IsNotEmpty() @IsNumber() @Min(1) SIGNED_COOKIE_THEME_EXPIRES_IN_MS: number; @IsNotEmpty() @IsString() COINGECKO_API_BASE_URL: string; @IsNotEmpty() @IsString() COINPAPRIKA_API_BASE_URL: string; @IsNotEmpty() @IsNumber() @Min(1) COINPAPRIKA_RATE_FETCH_TIMEOUT_MS: number; @IsNotEmpty() @IsNumber() @Min(1) COINGECKO_RATE_FETCH_TIMEOUT_MS: number; @IsNotEmpty() @IsString() @IsBase64({ byteLength: 32 }) BASE64_ENCRYPTION_KEY: string; @IsNotEmpty() @IsString() @IsConfirmationTiers() MONERO_CONFIRMATION_TIERS: string; @IsNotEmpty() @IsString() @IsConfirmationTiers() BITCOIN_CONFIRMATION_TIERS: string; @IsNotEmpty() @IsString() @IsEnabledPaymentMethods() PAYMENT_METHODS_ENABLED: string; @IsNotEmpty() @IsNumber() @Min(60000) ORDER_CHECKOUT_VALIDITY_MS: number; @IsNotEmpty() @IsNumber() @Min(1) ORDER_CHECKOUT_STATUS_REFRESH_SEC: number; @IsNotEmpty() @IsNumber() @Min(60000) ORDER_SHIPPING_PAYMENT_VALIDITY_MS: number; @IsNotEmpty() @IsNumber() @Min(1) ORDER_DATA_RETENTION_DAYS: number; @IsNotEmpty() @IsNumber() @Min(0) @Max(Number.MAX_SAFE_INTEGER) MONERO_MIN_INCOMING_ATOMIC: number; @IsNotEmpty() @IsNumber() @Min(0) @Max(Number.MAX_SAFE_INTEGER) BITCOIN_MIN_INCOMING_ATOMIC: number; @IsNotEmpty() @IsString() MONERO_DAEMON_ADDRESS: string; @IsNotEmpty() @IsEnum(MoneroNetwork) MONERO_NETWORK: MoneroNetwork; @IsNotEmpty() @IsString() MONERO_WALLET_RPC_HOST: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(65535) MONERO_WALLET_RPC_PORT: number; @IsNotEmpty() @IsString() MONERO_WALLET_RPC_USERNAME: string; @IsNotEmpty() @IsString() MONERO_WALLET_RPC_PASSWORD: string; @IsNotEmpty() @IsNumber() @Min(1000) MONERO_WALLET_RPC_TIMEOUT_MS: number; @IsNotEmpty() @IsEnum(ElectrumNetwork) ELECTRUM_NETWORK: ElectrumNetwork; @IsNotEmpty() @IsString() ELECTRUM_SERVER: string; @IsNotEmpty() @IsString() ELECTRUM_DAEMON_HOST: string; @IsNotEmpty() @IsNumber() @Min(1) @Max(65535) ELECTRUM_DAEMON_PORT: number; @IsNotEmpty() @IsString() ELECTRUM_DAEMON_RPC_USER: string; @IsNotEmpty() @IsString() ELECTRUM_DAEMON_RPC_PASSWORD: string; @IsNotEmpty() @IsNumber() @Min(1000) ELECTRUM_DAEMON_RPC_TIMEOUT_MS: number; @IsNotEmpty() @IsString() ELECTRUM_WALLET_PASSWORD: string; @IsNotEmpty() @IsString() SIMPLEX_WS_URL: string; @IsNotEmpty() @IsString() SIMPLEX_BOT_DISPLAY_NAME: string; } export const validate = (config: Record) => { const validatedConfig = plainToClass(EnvironmentVariables, config, { enableImplicitConversion: true }); const errors = validateSync(validatedConfig, { skipMissingProperties: false }); if (errors.length > 0) { throw new Error(errors.toString()); } return validatedConfig; };