/** * Configuration management for unified email worker * * All settings are read from environment variables with sensible defaults. * Domain helpers (bucket name, queue name, internal check) are co-located here * so every module can import { config, domainToBucket, ... } from './config'. */ import { readFileSync, existsSync } from 'node:fs'; // --------------------------------------------------------------------------- // Config object // --------------------------------------------------------------------------- export const config = { // AWS awsRegion: process.env.AWS_REGION ?? 'us-east-2', // Domains domainsList: process.env.DOMAINS ?? '', domainsFile: process.env.DOMAINS_FILE ?? '/etc/email-worker/domains.txt', // Worker workerThreads: parseInt(process.env.WORKER_THREADS ?? '10', 10), pollInterval: parseInt(process.env.POLL_INTERVAL ?? '20', 10), maxMessages: parseInt(process.env.MAX_MESSAGES ?? '10', 10), visibilityTimeout: parseInt(process.env.VISIBILITY_TIMEOUT ?? '300', 10), // SMTP delivery (local DMS) smtpHost: process.env.SMTP_HOST ?? 'localhost', smtpPort: parseInt(process.env.SMTP_PORT ?? '25', 10), smtpUseTls: (process.env.SMTP_USE_TLS ?? 'false').toLowerCase() === 'true', smtpUser: process.env.SMTP_USER ?? '', smtpPass: process.env.SMTP_PASS ?? '', smtpPoolSize: parseInt(process.env.SMTP_POOL_SIZE ?? '5', 10), // Internal SMTP port (for OOO / forwards to managed domains) internalSmtpPort: parseInt(process.env.INTERNAL_SMTP_PORT ?? '25', 10), // DynamoDB tables rulesTable: process.env.DYNAMODB_RULES_TABLE ?? 'email-rules', messagesTable: process.env.DYNAMODB_MESSAGES_TABLE ?? 'ses-outbound-messages', blockedTable: process.env.DYNAMODB_BLOCKED_TABLE ?? 'email-blocked-senders', // Bounce handling bounceLookupRetries: parseInt(process.env.BOUNCE_LOOKUP_RETRIES ?? '3', 10), bounceLookupDelay: parseFloat(process.env.BOUNCE_LOOKUP_DELAY ?? '1.0'), // Monitoring metricsPort: parseInt(process.env.METRICS_PORT ?? '8000', 10), healthPort: parseInt(process.env.HEALTH_PORT ?? '8080', 10), } as const; export type Config = typeof config; // --------------------------------------------------------------------------- // Managed domains (populated by loadDomains()) // --------------------------------------------------------------------------- const managedDomains = new Set(); /** * Load domains from env var and/or file, populate the internal set. */ export function loadDomains(): string[] { const domains: string[] = []; // From env if (config.domainsList) { for (const d of config.domainsList.split(',')) { const trimmed = d.trim(); if (trimmed) domains.push(trimmed); } } // From file if (existsSync(config.domainsFile)) { const content = readFileSync(config.domainsFile, 'utf-8'); for (const line of content.split('\n')) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { domains.push(trimmed); } } } // Deduplicate const unique = [...new Set(domains)]; managedDomains.clear(); for (const d of unique) { managedDomains.add(d.toLowerCase()); } return unique; } // --------------------------------------------------------------------------- // Domain helpers // --------------------------------------------------------------------------- /** Check whether an email address belongs to one of our managed domains */ export function isInternalAddress(email: string): boolean { const atIdx = email.indexOf('@'); if (atIdx < 0) return false; return managedDomains.has(email.slice(atIdx + 1).toLowerCase()); } /** Convert domain to SQS queue name: bizmatch.net → bizmatch-net-queue */ export function domainToQueueName(domain: string): string { return domain.replace(/\./g, '-') + '-queue'; } /** Convert domain to S3 bucket name: bizmatch.net → bizmatch-net-emails */ export function domainToBucketName(domain: string): string { return domain.replace(/\./g, '-') + '-emails'; }