const SMTPServer = require('smtp-server').SMTPServer; const config = require('./config'); const { parseEmail } = require('./services/emailParser'); const { sendToWebhook } = require('./services/webhookService'); require('aws-sdk/lib/maintenance_mode_message').suppress = true; const Queue = require('better-queue'); const winston = require('winston'); require('winston-daily-rotate-file'); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.Console(), new winston.transports.DailyRotateFile({ filename: 'logs/application-%DATE%.log', datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '20m', maxFiles: '90d' }) ] }); function validateConfig() { const requiredKeys = ['PORT', 'SMTP_SECURE', 'WEBHOOK_URL', 'WEBHOOK_CONCURRENCY']; for (const key of requiredKeys) { if (!(key in config)) { throw new Error(`Missing required configuration: ${key}`); } } } const webhookQueue = new Queue(async function (parsed, cb) { const maxRetries = 3; let retries = 0; const attemptWebhook = async () => { try { await sendToWebhook(parsed); logger.info('Successfully sent to webhook'); cb(null); } catch (error) { logger.error('Webhook error:', { message: error.message, stack: error.stack }); if (error.response) { logger.error('Webhook response error:', { status: error.response.status, data: error.response.data }); } if (retries < maxRetries) { retries++; logger.info(`Retrying webhook (attempt ${retries}/${maxRetries})`); setTimeout(attemptWebhook, 1000 * retries); } else { cb(error); } } }; attemptWebhook(); }, { concurrent: config.WEBHOOK_CONCURRENCY || 5 }); const server = new SMTPServer({ onData(stream, session, callback) { parseEmail(stream) .then(parsed => { webhookQueue.push(parsed); logger.info('Email added to queue', { queueSize: webhookQueue.getStats().total }); callback(); }) .catch(error => { logger.error('Parsing error:', { message: error.message, stack: error.stack }); callback(new Error('Failed to parse email')); }); }, onError(error) { logger.error('SMTP server error:', { message: error.message, stack: error.stack }); }, disabledCommands: ['AUTH'], secure: config.SMTP_SECURE }); server.listen(config.PORT, '0.0.0.0', err => { if (err) { logger.error('Failed to start SMTP server:', { message: err.message, stack: err.stack }); process.exit(1); } logger.info(`SMTP server listening on port ${config.PORT} on all interfaces`); }); function gracefulShutdown(reason) { logger.info(`Shutting down: ${reason}`); server.close(() => { logger.info('Server closed. Exiting process.'); process.exit(0); }); } process.on('uncaughtException', (err) => { logger.error('Uncaught exception:', { message: err.message, stack: err.stack }); gracefulShutdown('Uncaught exception'); }); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Rejection:', { reason: reason, promise: promise }); gracefulShutdown('Unhandled rejection'); }); process.on('SIGTERM', () => { gracefulShutdown('SIGTERM signal received'); }); process.on('SIGINT', () => { gracefulShutdown('SIGINT signal received'); }); // Add configuration validation at startup try { validateConfig(); } catch (error) { logger.error('Configuration error:', { message: error.message }); process.exit(1); }