import axios from 'axios'; import nodemailer from 'nodemailer'; import { Notification, NotificationType } from '../models/Notification'; import { Monitor } from '../models/Monitor'; import { MonitorResult } from '../models/MonitorResult'; import { config } from '../config'; import { AppDataSource } from '../index'; class NotificationService { // 发送服务宕机通知 async sendDownNotification(monitor: Monitor, result: MonitorResult) { try { const notificationRepository = AppDataSource.getRepository(Notification); const notifications = await notificationRepository.find({ where: { active: true } }); const title = `🔴 服务宕机: ${monitor.name}`; const content = this.generateDownNotificationContent(monitor, result); for (const notification of notifications) { await this.sendNotification(notification, title, content); } console.log(`已发送服务宕机通知: ${monitor.name}`); } catch (error: any) { console.error('发送服务宕机通知失败:', error); } } // 发送服务恢复通知 async sendRecoveryNotification(monitor: Monitor, result: MonitorResult) { try { const notificationRepository = AppDataSource.getRepository(Notification); const notifications = await notificationRepository.find({ where: { active: true } }); const title = `🟢 服务恢复: ${monitor.name}`; const content = this.generateRecoveryNotificationContent(monitor, result); for (const notification of notifications) { await this.sendNotification(notification, title, content); } console.log(`已发送服务恢复通知: ${monitor.name}`); } catch (error: any) { console.error('发送服务恢复通知失败:', error); } } // 发送测试通知 async sendTestNotification(notification: Notification) { try { const title = '📝 测试通知'; const content = '这是一条测试通知,如果您收到此消息,说明通知配置正确。'; const result = await this.sendNotification(notification, title, content); return result; } catch (error: any) { console.error('发送测试通知失败:', error); throw error; } } // 发送通知 private async sendNotification(notification: Notification, title: string, content: string) { try { const config = JSON.parse(notification.config); if (notification.type === NotificationType.FEISHU) { return await this.sendFeishuNotification(config.webhook, title, content); } else if (notification.type === NotificationType.EMAIL) { return await this.sendEmailNotification(config, title, content); } } catch (error: any) { console.error(`发送通知失败 (${notification.name}):`, error); throw error; } } // 发送飞书通知 private async sendFeishuNotification(webhook: string, title: string, content: string) { try { const message = { msg_type: 'post', content: { post: { zh_cn: { title: title, content: [ [ { tag: 'text', text: content } ] ] } } } }; const response = await axios.post(webhook, message); return response.data; } catch (error: any) { console.error('发送飞书通知失败:', error); throw error; } } // 发送邮件通知 private async sendEmailNotification(config: any, title: string, content: string) { try { const transporter = nodemailer.createTransport({ host: config.host, port: config.port, secure: config.secure, auth: { user: config.user, pass: config.pass } }); const mailOptions = { from: config.from, to: config.to, subject: title, text: content, html: content.replace(/\n/g, '
') }; const info = await transporter.sendMail(mailOptions); return info; } catch (error: any) { console.error('发送邮件通知失败:', error); throw error; } } // 生成服务宕机通知内容 private generateDownNotificationContent(monitor: Monitor, result: MonitorResult): string { let content = `服务 ${monitor.name} (${monitor.host}:${monitor.port}) 已宕机\n\n`; content += `- 监控类型: ${monitor.type}\n`; content += `- 宕机时间: ${result.createdAt.toLocaleString()}\n`; content += `- 连续失败次数: ${monitor.consecutiveFailures}\n`; if (result.responseTime) { content += `- 响应时间: ${result.responseTime}ms\n`; } if (result.statusCode) { content += `- HTTP状态码: ${result.statusCode}\n`; } if (result.error) { content += `- 错误信息: ${result.error}\n`; } if (result.dnsResolution) { content += `\nDNS解析结果:\n${result.dnsResolution}\n`; } if (result.traceroute) { content += `\nTraceroute结果:\n${result.traceroute}\n`; } return content; } // 生成服务恢复通知内容 private generateRecoveryNotificationContent(monitor: Monitor, result: MonitorResult): string { let content = `服务 ${monitor.name} (${monitor.host}:${monitor.port}) 已恢复\n\n`; content += `- 监控类型: ${monitor.type}\n`; content += `- 恢复时间: ${result.createdAt.toLocaleString()}\n`; if (result.responseTime) { content += `- 响应时间: ${result.responseTime}ms\n`; } if (result.statusCode) { content += `- HTTP状态码: ${result.statusCode}\n`; } return content; } } export const notificationService = new NotificationService();