pingping/backend/src/services/notificationService.ts

186 lines
5.8 KiB
TypeScript

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, '<br>')
};
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();