feat: add monitor event send notification

This commit is contained in:
moonrailgun 2023-10-20 00:28:46 +08:00
parent af49417899
commit 0346dc21d9
6 changed files with 55 additions and 13 deletions

View File

@ -14,7 +14,7 @@ import { useCurrentWorkspaceId } from '../../store/user';
export const NotificationList: React.FC = React.memo(() => { export const NotificationList: React.FC = React.memo(() => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const currentWorkspaceId = useCurrentWorkspaceId(); const currentWorkspaceId = useCurrentWorkspaceId();
const { data: list = [], refetch } = trpc.notification.getAll.useQuery({ const { data: list = [], refetch } = trpc.notification.all.useQuery({
workspaceId: currentWorkspaceId!, workspaceId: currentWorkspaceId!,
}); });
const [editingFormData, setEditingFormData] = useState< const [editingFormData, setEditingFormData] = useState<

View File

@ -1,13 +1,17 @@
import { Monitor } from '@prisma/client'; import { Monitor, Notification } from '@prisma/client';
import { createSubscribeInitializer, subscribeEventBus } from '../../ws/shared'; import { subscribeEventBus } from '../../ws/shared';
import { prisma } from '../_client'; import { prisma } from '../_client';
import { monitorProviders } from './provider'; import { monitorProviders } from './provider';
import { sendNotification } from '../notification';
import dayjs from 'dayjs';
export type MonitorUpsertData = Pick< export type MonitorUpsertData = Pick<
Monitor, Monitor,
'workspaceId' | 'name' | 'type' | 'interval' 'workspaceId' | 'name' | 'type' | 'interval'
> & { id?: string; active?: boolean; payload: Record<string, any> }; > & { id?: string; active?: boolean; payload: Record<string, any> };
type MonitorWithNotification = Monitor & { notifications: Notification[] };
class MonitorManager { class MonitorManager {
private monitorRunner: Record<string, MonitorRunner> = {}; private monitorRunner: Record<string, MonitorRunner> = {};
private isStarted = false; private isStarted = false;
@ -15,8 +19,8 @@ class MonitorManager {
/** /**
* create or update * create or update
*/ */
async upsert(data: MonitorUpsertData): Promise<Monitor> { async upsert(data: MonitorUpsertData): Promise<MonitorWithNotification> {
let monitor: Monitor; let monitor: MonitorWithNotification;
if (data.id) { if (data.id) {
// update // update
monitor = await prisma.monitor.update({ monitor = await prisma.monitor.update({
@ -24,6 +28,9 @@ class MonitorManager {
id: data.id, id: data.id,
}, },
data: { ...data }, data: { ...data },
include: {
notifications: true,
},
}); });
return monitor; return monitor;
@ -31,6 +38,9 @@ class MonitorManager {
// create // create
monitor = await prisma.monitor.create({ monitor = await prisma.monitor.create({
data: { ...data }, data: { ...data },
include: {
notifications: true,
},
}); });
} }
@ -63,6 +73,9 @@ class MonitorManager {
where: { where: {
active: true, active: true,
}, },
include: {
notifications: true,
},
}); });
Promise.all( Promise.all(
@ -88,7 +101,7 @@ class MonitorRunner {
isStopped = false; isStopped = false;
timer: NodeJS.Timeout | null = null; timer: NodeJS.Timeout | null = null;
constructor(public monitor: Monitor) {} constructor(public monitor: Monitor & { notifications: Notification[] }) {}
/** /**
* Start single monitor * Start single monitor
@ -114,7 +127,7 @@ class MonitorRunner {
}, interval * 1000); }, interval * 1000);
}; };
async function run() { const run = async () => {
let value = 0; let value = 0;
try { try {
value = await provider.run(monitor); value = await provider.run(monitor);
@ -132,6 +145,12 @@ class MonitorRunner {
type: 'DOWN', type: 'DOWN',
}, },
}); });
await this.notify(
`[${monitor.name}] 🔴 Down`,
`[${monitor.name}] 🔴 Down\nTime: ${dayjs().format(
'YYYY-MM-DD HH:mm:ss'
)}`
);
} else if (value > 0 && currentStatus === 'DOWN') { } else if (value > 0 && currentStatus === 'DOWN') {
await prisma.monitorEvent.create({ await prisma.monitorEvent.create({
data: { data: {
@ -140,6 +159,12 @@ class MonitorRunner {
type: 'UP', type: 'UP',
}, },
}); });
await this.notify(
`[${monitor.name}] ✅ Up`,
`[${monitor.name}] ✅ Up\nTime: ${dayjs().format(
'YYYY-MM-DD HH:mm:ss'
)}`
);
} }
// insert into data // insert into data
@ -154,7 +179,7 @@ class MonitorRunner {
// Run next loop // Run next loop
nextAction(); nextAction();
} };
run(); run();
@ -177,6 +202,17 @@ class MonitorRunner {
this.stopMonitor(); this.stopMonitor();
this.startMonitor(); this.startMonitor();
} }
async notify(title: string, message: string) {
const notifications = this.monitor.notifications;
await Promise.all(
notifications.map((n) =>
sendNotification(n, title, message).catch((err) => {
console.error(err);
})
)
);
}
} }
export const monitorManager = new MonitorManager(); export const monitorManager = new MonitorManager();

View File

@ -3,6 +3,7 @@ import { notificationProviders } from './provider';
export async function sendNotification( export async function sendNotification(
notification: Pick<Notification, 'name' | 'type' | 'payload'>, notification: Pick<Notification, 'name' | 'type' | 'payload'>,
title: string,
message: string message: string
) { ) {
const type = notification.type; const type = notification.type;
@ -11,5 +12,5 @@ export async function sendNotification(
throw new Error('Not match type:' + type); throw new Error('Not match type:' + type);
} }
await notificationProviders[type].send(notification, message); await notificationProviders[type].send(notification, title, message);
} }

View File

@ -17,7 +17,7 @@ interface SMTPPayload {
// Fork from https://github.com/louislam/uptime-kuma/blob/HEAD/server/notification-providers/smtp.js // Fork from https://github.com/louislam/uptime-kuma/blob/HEAD/server/notification-providers/smtp.js
export const smtp: NotificationProvider = { export const smtp: NotificationProvider = {
send: async (notification, message) => { send: async (notification, title, message) => {
const payload = notification.payload as unknown as SMTPPayload; const payload = notification.payload as unknown as SMTPPayload;
const config: SMTPTransport.Options = { const config: SMTPTransport.Options = {
@ -34,7 +34,7 @@ export const smtp: NotificationProvider = {
}; };
} }
const subject = message; const subject = title;
const bodyTextContent = message; const bodyTextContent = message;
const transporter = nodemailer.createTransport(config); const transporter = nodemailer.createTransport(config);

View File

@ -3,6 +3,7 @@ import { Notification } from '@prisma/client';
export interface NotificationProvider { export interface NotificationProvider {
send: ( send: (
notification: Pick<Notification, 'name' | 'type' | 'payload'>, notification: Pick<Notification, 'name' | 'type' | 'payload'>,
title: string,
message: string message: string
) => Promise<void>; ) => Promise<void>;
} }

View File

@ -4,7 +4,7 @@ import { prisma } from '../../model/_client';
import { sendNotification } from '../../model/notification'; import { sendNotification } from '../../model/notification';
export const notificationRouter = router({ export const notificationRouter = router({
getAll: workspaceProcedure.query(({ input }) => { all: workspaceProcedure.query(({ input }) => {
const workspaceId = input.workspaceId; const workspaceId = input.workspaceId;
return prisma.notification.findMany({ return prisma.notification.findMany({
@ -23,7 +23,11 @@ export const notificationRouter = router({
}) })
) )
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
await sendNotification(input, `${input.name} + Notification Testing`); await sendNotification(
input,
`${input.name} Notification Testing`,
`This is Notification Testing`
);
}), }),
upsert: workspaceOwnerProcedure upsert: workspaceOwnerProcedure
.input( .input(