186 lines
5.8 KiB
TypeScript
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();
|