90 lines
3.1 KiB
TypeScript
90 lines
3.1 KiB
TypeScript
/**
|
|
* Main entry point for unified email worker
|
|
*
|
|
* Startup sequence:
|
|
* 1. Load configuration and domains
|
|
* 2. Start Prometheus metrics server
|
|
* 3. Start health check server
|
|
* 4. Initialize UnifiedWorker
|
|
* 5. Register signal handlers for graceful shutdown
|
|
*/
|
|
|
|
import { config, loadDomains } from './config.js';
|
|
import { log } from './logger.js';
|
|
import { startMetricsServer, type MetricsCollector } from './metrics.js';
|
|
import { startHealthServer } from './health.js';
|
|
import { UnifiedWorker } from './worker/index.js';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Banner
|
|
// ---------------------------------------------------------------------------
|
|
function printBanner(domains: string[]): void {
|
|
log('╔══════════════════════════════════════════════════╗');
|
|
log('║ Unified Email Worker (TypeScript) ║');
|
|
log('║ Version 2.0.0 ║');
|
|
log('╚══════════════════════════════════════════════════╝');
|
|
log('');
|
|
log(`Domains (${domains.length}):`);
|
|
for (const d of domains) {
|
|
log(` • ${d}`);
|
|
}
|
|
log('');
|
|
log(`SMTP: ${config.smtpHost}:${config.smtpPort}`);
|
|
log(`Internal SMTP: port ${config.internalSmtpPort}`);
|
|
log(`Poll interval: ${config.pollInterval}s`);
|
|
log(`Metrics: port ${config.metricsPort}`);
|
|
log(`Health: port ${config.healthPort}`);
|
|
log('');
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main
|
|
// ---------------------------------------------------------------------------
|
|
async function main(): Promise<void> {
|
|
// 1. Load domains
|
|
const domains = loadDomains();
|
|
if (domains.length === 0) {
|
|
log('❌ No domains configured. Set DOMAINS env var or provide DOMAINS_FILE.', 'ERROR');
|
|
process.exit(1);
|
|
}
|
|
|
|
printBanner(domains);
|
|
|
|
// 2. Metrics server
|
|
const metrics: MetricsCollector | null = await startMetricsServer(config.metricsPort);
|
|
|
|
// 3. Unified worker
|
|
const worker = new UnifiedWorker(domains, metrics);
|
|
|
|
// 4. Health server
|
|
startHealthServer(config.healthPort, domains, () => worker.getStats());
|
|
|
|
// 5. Signal handlers
|
|
let shuttingDown = false;
|
|
|
|
const shutdown = async (signal: string) => {
|
|
if (shuttingDown) return;
|
|
shuttingDown = true;
|
|
log(`\n🛑 Received ${signal}. Shutting down gracefully...`);
|
|
await worker.stop();
|
|
log('👋 Goodbye.');
|
|
process.exit(0);
|
|
};
|
|
|
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
|
|
// 6. Start
|
|
await worker.start();
|
|
|
|
// Keep alive (event loop stays open due to HTTP servers + SQS polling)
|
|
log('✅ Worker is running. Press Ctrl+C to stop.');
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
main().catch((err) => {
|
|
log(`💥 Fatal startup error: ${err.message ?? err}`, 'CRITICAL');
|
|
log(err.stack ?? '', 'CRITICAL');
|
|
process.exit(1);
|
|
});
|