import { AppConfig, CoingeckoConfig, EncryptionConfig, InvoiceConfig, JwtConfig, CoinPaprikaConfig, MulterConfig, OrderConfig, PostgresConfig, ShopSettingsConfig } from '../types/Config'; import { ElectrumNetwork } from '../types/ElectrumNetwork'; import { ElectrumWalletConfig } from '../types/ElectrumWalletConfig'; import { MoneroNetwork } from '../types/MoneroNetwork'; import { MoneroWalletConfig } from '../types/MoneroWalletConfig'; import { ConfirmationTier } from '../types/ConfirmationTier'; import { NodeEnv } from '../types/NodeEnv'; import { ShopFiatCurrency } from '../types/ShopFiatCurrency'; import { PaymentMethod } from '../modules/payment/types/PaymentMethod'; import { SimplexConfig } from '../types/SimplexConfig'; import { parseEnabledPaymentMethods } from '../utils/payment/parseEnabledPaymentMethods'; const env = (key: string): string => process.env[key] || ''; const envInt = (key: string): number => parseInt(env(key), 10); const isEnabled = (key: string): boolean => env(key) === 'true'; export const getPostgresConfig = (): PostgresConfig => { return { type: 'postgres', url: `postgres://${env('POSTGRES_USER')}:${env('POSTGRES_PASSWORD')}@${env('POSTGRES_HOST')}:${env('POSTGRES_PORT')}/${env('POSTGRES_DB')}`, host: env('POSTGRES_HOST'), username: env('POSTGRES_USER'), password: env('POSTGRES_PASSWORD'), port: envInt('POSTGRES_PORT'), database: env('POSTGRES_DB'), entities: [__dirname + '/../modules/**/entities/*{.ts,.js}'], migrations: [__dirname + '/../database/migrations/*{.ts,.js}'], migrationsRun: isEnabled('POSTGRES_MIGRATIONS_RUN') }; }; export const getAppConfig = (): AppConfig => { const rawOrigins = env('CORS_ORIGINS'); return { port: envInt('BACKEND_PORT'), nodeEnv: env('NODE_ENV') as NodeEnv, corsOrigins: rawOrigins .split(',') .map(o => o.trim()) .filter(Boolean), cmsPassword: env('CMS_PASSWORD'), captcha: { length: envInt('CAPTCHA_LENGTH') }, throttle: { ttlMs: envInt('THROTTLE_TTL_MS'), limit: envInt('THROTTLE_LIMIT') }, signedCookie: { jwtSecret: env('SIGNED_COOKIE_JWT_SECRET'), feedback: { cookieName: env('SIGNED_COOKIE_FEEDBACK_NAME'), expiresInMs: envInt('SIGNED_COOKIE_FEEDBACK_EXPIRES_IN_MS') }, cart: { cookieName: env('SIGNED_COOKIE_CART_NAME'), expiresInMs: envInt('SIGNED_COOKIE_CART_EXPIRES_IN_MS') }, captcha: { cookieName: env('SIGNED_COOKIE_CAPTCHA_NAME'), expiresInMs: envInt('SIGNED_COOKIE_CAPTCHA_EXPIRES_IN_MS') }, discount: { cookieName: env('SIGNED_COOKIE_DISCOUNT_NAME'), expiresInMs: envInt('SIGNED_COOKIE_DISCOUNT_EXPIRES_IN_MS') }, error: { cookieName: env('SIGNED_COOKIE_ERROR_NAME'), expiresInMs: envInt('SIGNED_COOKIE_ERROR_EXPIRES_IN_MS') }, checkoutSession: { cookieName: env('SIGNED_COOKIE_CHECKOUT_SESSION_NAME'), expiresInMs: envInt('ORDER_CHECKOUT_VALIDITY_MS') }, orderAuth: { cookieName: env('SIGNED_COOKIE_ORDER_AUTH_NAME'), expiresInMs: envInt('SIGNED_COOKIE_ORDER_AUTH_EXPIRES_IN_MS') }, theme: { cookieName: env('SIGNED_COOKIE_THEME_NAME'), expiresInMs: envInt('SIGNED_COOKIE_THEME_EXPIRES_IN_MS') } }, validation: { productTitleMaxLength: envInt('VALIDATION_PRODUCT_TITLE_MAX_LENGTH'), categoryNameMaxLength: envInt('VALIDATION_CATEGORY_NAME_MAX_LENGTH'), discountCodeMaxLength: envInt('VALIDATION_DISCOUNT_CODE_MAX_LENGTH'), variantImagesMax: envInt('VALIDATION_VARIANT_IMAGES_MAX'), digitalStockAttachmentsMax: envInt('VALIDATION_DIGITAL_STOCK_ATTACHMENTS_MAX'), shippingNoteMinLength: envInt('VALIDATION_SHIPPING_NOTE_MIN_LENGTH'), shippingNoteMaxLength: envInt('VALIDATION_SHIPPING_NOTE_MAX_LENGTH'), orderMessageMaxLength: envInt('VALIDATION_ORDER_MESSAGE_MAX_LENGTH'), bitcoinWithdrawMaxFeeRateSatVbyte: envInt('VALIDATION_BITCOIN_WITHDRAW_MAX_FEE_RATE_SAT_VBYTE') } }; }; export const getJwtConfig = (): JwtConfig => { return { secret: env('JWT_SECRET'), expiresInMs: envInt('JWT_EXPIRES_IN_MS') }; }; export const getProductThumbMulterConfig = (): MulterConfig => { const allowedMimes = env('MULTER_PRODUCT_THUMB_ALLOWED_MIMES') .split(',') .map(v => v.trim()) .filter(Boolean); const maxFileBytes = envInt('MULTER_PRODUCT_THUMB_MAX_FILE_BYTES'); return { allowedMimes, maxFileBytes }; }; export const getShopLogoMulterConfig = (): MulterConfig => { const allowedMimes = env('MULTER_SHOP_LOGO_ALLOWED_MIMES') .split(',') .map(v => v.trim()) .filter(Boolean); const maxFileBytes = envInt('MULTER_SHOP_LOGO_MAX_FILE_BYTES'); return { allowedMimes, maxFileBytes }; }; export const getShopFaviconMulterConfig = (): MulterConfig => { const allowedMimes = env('MULTER_SHOP_FAVICON_ALLOWED_MIMES') .split(',') .map(v => v.trim()) .filter(Boolean); const maxFileBytes = envInt('MULTER_SHOP_FAVICON_MAX_FILE_BYTES'); return { allowedMimes, maxFileBytes }; }; export const getDigitalStockAttachmentMulterConfig = (): MulterConfig => { const allowedMimes = env('MULTER_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES') .split(',') .map(v => v.trim()) .filter(Boolean); const maxFileBytes = envInt('MULTER_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES'); return { allowedMimes, maxFileBytes }; }; export const getCoingeckoConfig = (): CoingeckoConfig => { return { apiBaseUrl: env('COINGECKO_API_BASE_URL'), rateFetchTimeoutMs: envInt('COINGECKO_RATE_FETCH_TIMEOUT_MS') }; }; export const getCoinPaprikaConfig = (): CoinPaprikaConfig => { return { apiBaseUrl: env('COINPAPRIKA_API_BASE_URL'), rateFetchTimeoutMs: envInt('COINPAPRIKA_RATE_FETCH_TIMEOUT_MS') }; }; export const getEncryptionConfig = (): EncryptionConfig => ({ keyBase64: env('BASE64_ENCRYPTION_KEY') }); export const getShopSettingsConfig = (): ShopSettingsConfig => ({ shopName: env('SHOP_NAME'), shopFiatCurrency: env('SHOP_FIAT_CURRENCY') as ShopFiatCurrency, enabledPaymentMethods: parseEnabledPaymentMethods(env('PAYMENT_METHODS_ENABLED')), monero: { confirmationTiers: JSON.parse(env('MONERO_CONFIRMATION_TIERS')) as ConfirmationTier[] }, bitcoin: { confirmationTiers: JSON.parse(env('BITCOIN_CONFIRMATION_TIERS')) as ConfirmationTier[] } }); export const getOrderConfig = (): OrderConfig => ({ checkoutValidityMs: envInt('ORDER_CHECKOUT_VALIDITY_MS'), shippingPaymentValidityMs: envInt('ORDER_SHIPPING_PAYMENT_VALIDITY_MS'), checkoutStatusRefreshSec: envInt('ORDER_CHECKOUT_STATUS_REFRESH_SEC'), dataRetentionDays: envInt('ORDER_DATA_RETENTION_DAYS') }); export const getInvoiceConfig = (): InvoiceConfig => ({ minByMethod: { [PaymentMethod.Xmr]: String(envInt('MONERO_MIN_INCOMING_ATOMIC')), [PaymentMethod.Btc]: String(envInt('BITCOIN_MIN_INCOMING_ATOMIC')) } }); export const getMoneroWalletConfig = (): MoneroWalletConfig => ({ network: env('MONERO_NETWORK') as MoneroNetwork, rpcUrl: `http://${env('MONERO_WALLET_RPC_HOST')}:${envInt('MONERO_WALLET_RPC_PORT')}/json_rpc`, daemonRpcUrl: `http://${env('MONERO_DAEMON_ADDRESS')}/json_rpc`, username: env('MONERO_WALLET_RPC_USERNAME'), password: env('MONERO_WALLET_RPC_PASSWORD'), rpcTimeoutMs: envInt('MONERO_WALLET_RPC_TIMEOUT_MS') }); export const getElectrumWalletConfig = (): ElectrumWalletConfig => ({ network: env('ELECTRUM_NETWORK') as ElectrumNetwork, server: env('ELECTRUM_SERVER'), rpcUrl: `http://${env('ELECTRUM_DAEMON_HOST')}:${envInt('ELECTRUM_DAEMON_PORT')}`, username: env('ELECTRUM_DAEMON_RPC_USER'), password: env('ELECTRUM_DAEMON_RPC_PASSWORD'), walletPassword: env('ELECTRUM_WALLET_PASSWORD'), rpcTimeoutMs: envInt('ELECTRUM_DAEMON_RPC_TIMEOUT_MS') }); export const getSimplexConfig = (): SimplexConfig => ({ wsUrl: env('SIMPLEX_WS_URL'), botDisplayName: env('SIMPLEX_BOT_DISPLAY_NAME') }); export default () => ({ postgres: getPostgresConfig(), app: getAppConfig(), jwt: getJwtConfig(), coingecko: getCoingeckoConfig(), coinPaprika: getCoinPaprikaConfig(), encryption: getEncryptionConfig(), shopSettings: getShopSettingsConfig(), order: getOrderConfig(), invoice: getInvoiceConfig(), moneroWallet: getMoneroWalletConfig(), electrumWallet: getElectrumWalletConfig(), simplex: getSimplexConfig() });