const nodemailer = require('nodemailer'); const axios = require('axios'); require('dotenv').config(); class NotificationService { constructor() { this.applyConfigFromEnv(); } applyConfigFromEnv() { this.setConfig({ email_enabled: process.env.EMAIL_ENABLED === 'true', email_host: process.env.EMAIL_HOST, email_port: parseInt(process.env.EMAIL_PORT), email_user: process.env.EMAIL_USER, email_pass: process.env.EMAIL_PASS, email_from: process.env.EMAIL_FROM, email_to: process.env.EMAIL_TO, webhook_enabled: process.env.WEBHOOK_ENABLED === 'true', webhook_url: process.env.WEBHOOK_URL, }); } setConfig(config) { this.emailEnabled = !!config.email_enabled; this.webhookEnabled = !!config.webhook_enabled; this.config = config; // Recreate transporter when email config changes; skip if incomplete if ( this.emailEnabled && config.email_host && config.email_port && config.email_user && config.email_pass ) { this.transporter = nodemailer.createTransport({ host: config.email_host, port: parseInt(config.email_port), secure: false, auth: { user: config.email_user, pass: config.email_pass, }, }); } else { this.transporter = null; } } async sendAlert(resource, incident, stats = null) { const isDown = incident.type === 'started'; const statusEmoji = isDown ? '🔴' : '🟢'; const statusText = isDown ? 'DOWN' : 'UP'; const message = `${statusEmoji} ${resource.name} is ${statusText}!\n\nURL: ${resource.url}\nCheck Type: ${resource.type || 'http'}\nTime: ${new Date().toLocaleString()}`; const promises = []; if (this.emailEnabled) { promises.push(this.sendEmail(resource, message, incident.type, stats)); } if (this.webhookEnabled) { promises.push(this.sendWebhook(resource, message, incident.type)); } await Promise.allSettled(promises); } async sendEmail(resource, message, type, stats = null) { if (!this.transporter) { console.error('Email config incomplete; skipping email'); return; } try { let htmlContent = `
Resource: ${resource.name}
Status: ${type === 'started' ? 'DOWN' : 'UP'}
URL: ${resource.url}
Check Type: ${resource.type || 'http'}
Time: ${new Date().toLocaleString()}
`; if (stats) { htmlContent += `Uptime: ${(stats.uptime * 100).toFixed(2)}%
Avg Response Time: ${stats.avgResponseTime.toFixed(0)}ms
Last Check: ${stats.lastCheck || 'Never'}
`; if (stats.recentChecks && stats.recentChecks.length > 0) { const graph = this.generateAsciiGraph(stats.recentChecks); htmlContent += `Recent Response Times (last 12 checks):
${graph}
`;
}
htmlContent += `