const utils = require('./utils'), fileHelper = require('./file_helper'), { PromoModes } = require('./constants'); // Canonical price key → Stripe price ID map (keyed by lookup_key / price key) const _prices = { // Essential packages ess_1: process.env.ESS_1, ess_1_1: process.env.ESS_1_1, ess_2: process.env.ESS_2, ess_3: process.env.ESS_3, ess_4: process.env.ESS_4, ess_5: process.env.ESS_5, // Enterprise packages ent_1: process.env.ENT_1, ent_2: process.env.ENT_2, ent_3: process.env.ENT_3, ent_4: process.env.ENT_4, addon_1: process.env.ADDON_1 }; // Inverted map: Stripe price ID → price key (for reverse lookups) const _priceMap = Object.entries(_prices).reduce((m, [key, id]) => { if (id) m[id] = key; return m; }, {}); // Normalize production flag once to avoid repeated parsing const IS_PROD = utils.stringToBoolean(process.env.PRODUCTION) || false; const getQueueName = (queueName, defaultName) => { const baseQueue = process.env[queueName] || defaultName; return IS_PROD ? baseQueue : `dev_${baseQueue}`; }; module.exports = { HTTP2_ENABLED: utils.stringToBoolean(process.env.HTTP2_ENABLED) || false, HTTP2_ADVERTISE_H2: utils.stringToBoolean(process.env.HTTP2_ADVERTISE_H2) || false, FATAL_REPORT_ENABLED: utils.stringToBoolean(process.env.FATAL_REPORT_ENABLED) || false, FATAL_REPORT_FILE: fileHelper.getAppPath(process.env.FATAL_REPORT_FILE || './agm_server.rlog'), FATAL_REPORT_EMAIL_ENABLED: utils.stringToBoolean(process.env.FATAL_REPORT_EMAIL_ENABLED) || false, FATAL_REPORT_EMAIL_TO: process.env.FATAL_REPORT_EMAIL_TO, FATAL_EXIT_ON_ERROR: utils.stringToBoolean(process.env.FATAL_EXIT_ON_ERROR) ?? true, FATAL_EXIT_DELAY_MS: Number(process.env.FATAL_EXIT_DELAY_MS) || 1500, FATAL_THROTTLE_MS: Number(process.env.FATAL_THROTTLE_MS) || 2 * 60 * 1000, AGM_PORT: process.env.AGM_PORT, PRODUCTION: IS_PROD, DEBUG: process.env.DEBUG, LOG_ALL_ERRORS: utils.stringToBoolean(process.env.LOG_ALL_ERRORS) || false, MAX_REQ_BDY_MB: process.env.MAX_REQ_BDY_MB, MAX_UPLOAD_SIZE_MB: process.env.MAX_UPLOAD_SIZE_MB, MAX_UPLOAD_FILES: process.env.MAX_UPLOAD_FILES, MAX_SESSION_SECS: process.env.MAX_SESSION_SECS || 3600 * 8, // Cursor-based pagination configuration PAGINATION_DEFAULT_LIMIT: Number(process.env.PAGINATION_DEFAULT_LIMIT) || 1000, PAGINATION_MAX_LIMIT: Number(process.env.PAGINATION_MAX_LIMIT) || 14000, NO_EMAIL_MODE: utils.stringToBoolean(process.env.NO_EMAIL_MODE) || false, // Make sure and safe to force minimum 13 months to archive jobs ARCHIVE_JOBS_DAYS: Math.max((+process.env.ARCHIVE_JOBS_DAYS || 365) + 31, 365 + 31), TOKEN_SECRET: process.env.TOKEN_SECRET, PWD_RESET_VALID_HRS: process.env.PWD_RESET_VALID_HRS || '3h', APP_RATE_MINS: Number(process.env.APP_RATE_MINS) || 5, APP_RATE_REQS: Number(process.env.APP_RATE_REQS) || 200, APP_RATE_SKIPFAIL: utils.stringToBoolean(process.env.APP_RATE_SKIPFAIL), // true: trust all proxies, ['ip address', 'other ip address'] or number: number of proxies between user and server APP_RATE_TRUST_PROXIES: Number(process.env.APP_RATE_TRUST_PROXIES) || 1, // Make APP_URL default to prod host or local dev APP_URL: IS_PROD ? (process.env.APP_URL || 'https://agmission.agnav.com') : (process.env.APP_URL || 'http://localhost:4200'), UPLOAD_DIR: fileHelper.getAppPath(process.env.UPLOAD_DIR), UNZIP_DIR: fileHelper.getAppPath(process.env.UNZIP_DIR), REPORT_DIR: fileHelper.getAppPath(process.env.REPORT_DIR), TEMP_DIR: fileHelper.getAppPath(process.env.TEMP_DIR), SSL_KEY: process.env.SSL_KEY, SSL_CERT: process.env.SSL_CERT, CAPTCHA_SITESEC: process.env.CAPTCHA_SITESEC, ENABLE_SUBSCRIPTION: utils.stringToBoolean(process.env.ENABLE_SUBSCRIPTION) || false, // Stripe STRIPE_SEC_KEY: process.env.STRIPE_SEC_KEY, STRIPE_PUB_KEY: process.env.STRIPE_PUB_KEY, STRIPE_WH_SEC: process.env.STRIPE_WH_SEC, STRIPE_API_VERSION: process.env.STRIPE_API_VERSION, PRICES: _prices, // Inverted map: Stripe price ID → price key (lookup_key) PRICE_MAP: _priceMap, AGN_BILL_MGT_EMAIL: process.env.AGN_BILL_MGT_EMAIL, AGM_ADM_EMAIL: process.env.AGM_ADM_EMAIL, NEW_ACC_TRIAL_DAYS: process.env.NEW_ACC_TRIAL_DAYS || 30, NEW_ACC_VALID_TIME: process.env.NEW_ACC_VALID_TIME || '3d', EMAIL_VER_VALID_TIME: process.env.EMAIL_VER_VALID_TIME || '4h', // DB connection info DB_HOSTS: process.env.DB_HOSTS, DB_NAME: process.env.DB_NAME, DB_USR: process.env.DB_USR, DB_PWD: process.env.DB_PWD, DB_AUTH_SOURCE: process.env.DB_AUTH_SOURCE, DB_REPLSET: process.env.DB_REPLSET, DB_MAX_POOLSIZE: process.env.DB_MAX_POOLSIZE, DB_USE_TLS: utils.stringToBoolean(process.env.DB_USE_TLS), DB_TLS_CA_FILE: process.env.DB_TLS_CA_FILE, DB_TLS_CERT_FILE: process.env.DB_TLS_CERT_FILE, DB_USE_X509: utils.stringToBoolean(process.env.DB_USE_X509), DB_DISABLE_HOSTNAME_VERIFY: utils.stringToBoolean(process.env.DB_DISABLE_HOSTNAME_VERIFY), // Allow disabling SSL validation - not recommended but sometimes needed for self-signed certs DB_DISABLE_SSL_VALIDATE: utils.stringToBoolean(process.env.DB_DISABLE_SSL_VALIDATE), // New parameter to identify ScaleGrid hosting DB_USE_SCALEGRID: utils.stringToBoolean(process.env.DB_USE_SCALEGRID) || false, // For RabbitMq queue client QUEUE_PORT: process.env.QUEUE_PORT, QUEUE_HOST: process.env.QUEUE_HOST, QUEUE_USR: process.env.QUEUE_USR, QUEUE_PWD: process.env.QUEUE_PWD, QUEUE_VHOST: process.env.QUEUE_VHOST, QUEUE_NAME_JOBS: getQueueName(process.env.QUEUE_NAME_JOBS, 'jobs'), QUEUE_HEARTBEAT: Number(process.env.QUEUE_HEARTBEAT), // Partner queue name with automatic dev/prod prefix QUEUE_NAME_PARTNER: getQueueName(process.env.QUEUE_NAME_PARTNER, 'partner_tasks'), REDIS_PWD: process.env.REDIS_PWD, JOBS_CACHE_TTL: Number(process.env.JOBS_CACHE_TTL) || 60, // seconds; set to 0 to disable SMTP_HOST: process.env.SMTP_HOST, SMTP_PORT: process.env.SMTP_PORT, SMTP_SECURE: process.env.SMTP_SECURE, SMTP_USR: process.env.SMTP_USR, SMTP_PWD: process.env.SMTP_PWD, INV_IMG_VIR_DIR: process.env.INV_IMG_VIR_DIR ?? '/uploads/invoice_settings', INV_UPLOAD_DIR: fileHelper.getAppPath(process.env.INV_UPLOAD_DIR ?? './uploads/invoice_settings'), INV_MAX_UPLOAD_SIZE_MB: process.env.INV_MAX_UPLOAD_SIZE_MB || 5, INV_PROCESS_LIMIT: Number(process.env.INV_PROCESS_LIMIT || 100), INV_OVERPAID_THRESHOLD: Math.max(1, Number(process.env.INV_OVERPAID_THRESHOLD || 5)), INV_MAX_OVERDUE_DAYS: Number(process.env.INV_MAX_OVERDUE_DAYS || 30), // For Obstacle worker FAA_DOF_URL: process.env.FAA_DOF_URL || 'https://www.faa.gov/air_traffic/flight_info/aeronav/digital_products/dof/', AREAS_UPLOAD_DIR: fileHelper.getAppPath(process.env.AREAS_UPLOAD_DIR ?? './uploads/areas'), // Partner System Configuration Settings PARTNER_SYNC_INTERVAL: Number(process.env.PARTNER_SYNC_INTERVAL) || 300000, // 5 minutes PARTNER_HEALTH_CHECK_INTERVAL: Number(process.env.PARTNER_HEALTH_CHECK_INTERVAL) || 60000, // 1 minute PARTNER_MAX_CONCURRENT_JOBS: Number(process.env.PARTNER_MAX_CONCURRENT_JOBS) || 10, PARTNER_ENCRYPT_CREDENTIALS: utils.stringToBoolean(process.env.PARTNER_ENCRYPT_CREDENTIALS) || true, PARTNER_JOB_TIMEOUT: process.env.PARTNER_JOB_TIMEOUT, PARTNER_METRICS_ENABLED: process.env.PARTNER_METRICS_ENABLED, PARTNER_DETAILED_LOGGING: process.env.PARTNER_DETAILED_LOGGING, PARTNER_MAX_RETRIES: Number(process.env.PARTNER_MAX_RETRIES || process.env.AGM_MAX_RETRIES || 5), // SatLoc Configuration (Customer-specific credentials in PartnerSystemUser records) SATLOC_API_ENDPOINT: process.env.SATLOC_API_ENDPOINT || 'https://www.satloccloudfc.com/api/Satloc', SATLOC_API_KEY: process.env.SATLOC_API_KEY, SATLOC_API_SECRET: process.env.SATLOC_API_SECRET, SATLOC_API_TIMEOUT: process.env.SATLOC_API_TIMEOUT, SATLOC_RETRY_ATTEMPTS: process.env.SATLOC_RETRY_ATTEMPTS, SATLOC_RETRY_DELAY: process.env.SATLOC_RETRY_DELAY, SATLOC_RATE_LIMIT: process.env.SATLOC_RATE_LIMIT, SATLOC_BURST_LIMIT: process.env.SATLOC_BURST_LIMIT, SATLOC_REALTIME_ENABLED: process.env.SATLOC_REALTIME_ENABLED, SATLOC_FILE_UPLOAD_ENABLED: process.env.SATLOC_FILE_UPLOAD_ENABLED, SATLOC_MAX_FILE_SIZE: process.env.SATLOC_MAX_FILE_SIZE, SATLOC_STORAGE_PATH: process.env.SATLOC_STORAGE_PATH, SATLOC_MAX_POSITIONS_PER_JOB: process.env.SATLOC_MAX_POSITIONS_PER_JOB ? parseInt(process.env.SATLOC_MAX_POSITIONS_PER_JOB, 10) : undefined, SATLOC_1ST_ASSIGNMENT_ALWAYS_MATCH: utils.stringToBoolean(process.env.SATLOC_1ST_ASSIGNMENT_ALWAYS_MATCH) || false, // AgIDronex Configuration (for future expansion) AGIDRONEX_API_ENDPOINT: process.env.AGIDRONEX_API_ENDPOINT, AGIDRONEX_API_KEY: process.env.AGIDRONEX_API_KEY, AGIDRONEX_API_SECRET: process.env.AGIDRONEX_API_SECRET, AGIDRONEX_API_TIMEOUT: process.env.AGIDRONEX_API_TIMEOUT, AGIDRONEX_RETRY_ATTEMPTS: process.env.AGIDRONEX_RETRY_ATTEMPTS, AGIDRONEX_RETRY_DELAY: process.env.AGIDRONEX_RETRY_DELAY, AGIDRONEX_RATE_LIMIT: process.env.AGIDRONEX_RATE_LIMIT, AGIDRONEX_BURST_LIMIT: process.env.AGIDRONEX_BURST_LIMIT, AGIDRONEX_REALTIME_ENABLED: process.env.AGIDRONEX_REALTIME_ENABLED, AGIDRONEX_FILE_UPLOAD_ENABLED: process.env.AGIDRONEX_FILE_UPLOAD_ENABLED, AGIDRONEX_MAX_FILE_SIZE: process.env.AGIDRONEX_MAX_FILE_SIZE, PROMO_MIN_EXPIRY_DAYS: Number(process.env.PROMO_MIN_EXPIRY_DAYS) || 3, // Days before a promo schedule phase ends to send the advance expiry warning email (0 = disabled) PROMO_EXPIRY_WARNING_DAYS: Number(process.env.PROMO_EXPIRY_WARNING_DAYS) || 15, // Promotion mode (global kill switch for all promo applications) // 'enabled' (DEFAULT) - Promotions enabled (targeting controlled by PromoEligibility) // 'disabled' - Never apply promotions (kill switch OFF) // Affects: /update, /retrieveNextInvoices, /activePromos endpoints PROMO_MODE: process.env.PROMO_MODE || PromoModes.ENABLED, // Dead Letter Queue (DLQ) Configuration DLQ_RETENTION_DAYS: Number(process.env.DLQ_RETENTION_DAYS) || 365, // How long to keep messages in DLQ before auto-archive DLQ_ARCHIVE_PATH: fileHelper.getAppPath(process.env.DLQ_ARCHIVE_PATH || './dlq_archives'), // Where to store archived DLQ messages DLQ_ALERT_ENABLED: utils.stringToBoolean(process.env.DLQ_ALERT_ENABLED) ?? true, // Send admin alerts for DLQ buildup DLQ_ALERT_THRESHOLD: Number(process.env.DLQ_ALERT_THRESHOLD) || 20, // Warning threshold for DLQ message count DLQ_ALERT_CRITICAL: Number(process.env.DLQ_ALERT_CRITICAL) || 50, // Critical threshold for DLQ message count DLQ_ALERT_INTERVAL_MS: Number(process.env.DLQ_ALERT_INTERVAL_MS) || 300000, // Check interval (5 minutes) DLQ_CONSUMER_ENABLED: utils.stringToBoolean(process.env.DLQ_CONSUMER_ENABLED) || false // Enable DLQ consumer (manual control) }