diff --git a/src/client/pages/Settings/NotificationList.tsx b/src/client/pages/Settings/NotificationList.tsx index 6e642a6..889bdb0 100644 --- a/src/client/pages/Settings/NotificationList.tsx +++ b/src/client/pages/Settings/NotificationList.tsx @@ -14,7 +14,7 @@ import { useCurrentWorkspaceId } from '../../store/user'; export const NotificationList: React.FC = React.memo(() => { const [open, setOpen] = useState(false); const currentWorkspaceId = useCurrentWorkspaceId(); - const { data: list = [], refetch } = trpc.notification.getAll.useQuery({ + const { data: list = [], refetch } = trpc.notification.all.useQuery({ workspaceId: currentWorkspaceId!, }); const [editingFormData, setEditingFormData] = useState< diff --git a/src/server/model/monitor/index.ts b/src/server/model/monitor/index.ts index 1414f80..ba455cf 100644 --- a/src/server/model/monitor/index.ts +++ b/src/server/model/monitor/index.ts @@ -1,13 +1,17 @@ -import { Monitor } from '@prisma/client'; -import { createSubscribeInitializer, subscribeEventBus } from '../../ws/shared'; +import { Monitor, Notification } from '@prisma/client'; +import { subscribeEventBus } from '../../ws/shared'; import { prisma } from '../_client'; import { monitorProviders } from './provider'; +import { sendNotification } from '../notification'; +import dayjs from 'dayjs'; export type MonitorUpsertData = Pick< Monitor, 'workspaceId' | 'name' | 'type' | 'interval' > & { id?: string; active?: boolean; payload: Record }; +type MonitorWithNotification = Monitor & { notifications: Notification[] }; + class MonitorManager { private monitorRunner: Record = {}; private isStarted = false; @@ -15,8 +19,8 @@ class MonitorManager { /** * create or update */ - async upsert(data: MonitorUpsertData): Promise { - let monitor: Monitor; + async upsert(data: MonitorUpsertData): Promise { + let monitor: MonitorWithNotification; if (data.id) { // update monitor = await prisma.monitor.update({ @@ -24,6 +28,9 @@ class MonitorManager { id: data.id, }, data: { ...data }, + include: { + notifications: true, + }, }); return monitor; @@ -31,6 +38,9 @@ class MonitorManager { // create monitor = await prisma.monitor.create({ data: { ...data }, + include: { + notifications: true, + }, }); } @@ -63,6 +73,9 @@ class MonitorManager { where: { active: true, }, + include: { + notifications: true, + }, }); Promise.all( @@ -88,7 +101,7 @@ class MonitorRunner { isStopped = false; timer: NodeJS.Timeout | null = null; - constructor(public monitor: Monitor) {} + constructor(public monitor: Monitor & { notifications: Notification[] }) {} /** * Start single monitor @@ -114,7 +127,7 @@ class MonitorRunner { }, interval * 1000); }; - async function run() { + const run = async () => { let value = 0; try { value = await provider.run(monitor); @@ -132,6 +145,12 @@ class MonitorRunner { 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') { await prisma.monitorEvent.create({ data: { @@ -140,6 +159,12 @@ class MonitorRunner { type: 'UP', }, }); + await this.notify( + `[${monitor.name}] ✅ Up`, + `[${monitor.name}] ✅ Up\nTime: ${dayjs().format( + 'YYYY-MM-DD HH:mm:ss' + )}` + ); } // insert into data @@ -154,7 +179,7 @@ class MonitorRunner { // Run next loop nextAction(); - } + }; run(); @@ -177,6 +202,17 @@ class MonitorRunner { this.stopMonitor(); 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(); diff --git a/src/server/model/notification/index.ts b/src/server/model/notification/index.ts index cd8fcda..1d9e8f1 100644 --- a/src/server/model/notification/index.ts +++ b/src/server/model/notification/index.ts @@ -3,6 +3,7 @@ import { notificationProviders } from './provider'; export async function sendNotification( notification: Pick, + title: string, message: string ) { const type = notification.type; @@ -11,5 +12,5 @@ export async function sendNotification( throw new Error('Not match type:' + type); } - await notificationProviders[type].send(notification, message); + await notificationProviders[type].send(notification, title, message); } diff --git a/src/server/model/notification/provider/smtp.ts b/src/server/model/notification/provider/smtp.ts index 33f055f..fb0a7c9 100644 --- a/src/server/model/notification/provider/smtp.ts +++ b/src/server/model/notification/provider/smtp.ts @@ -17,7 +17,7 @@ interface SMTPPayload { // Fork from https://github.com/louislam/uptime-kuma/blob/HEAD/server/notification-providers/smtp.js export const smtp: NotificationProvider = { - send: async (notification, message) => { + send: async (notification, title, message) => { const payload = notification.payload as unknown as SMTPPayload; const config: SMTPTransport.Options = { @@ -34,7 +34,7 @@ export const smtp: NotificationProvider = { }; } - const subject = message; + const subject = title; const bodyTextContent = message; const transporter = nodemailer.createTransport(config); diff --git a/src/server/model/notification/provider/type.ts b/src/server/model/notification/provider/type.ts index 65556b4..e1fa951 100644 --- a/src/server/model/notification/provider/type.ts +++ b/src/server/model/notification/provider/type.ts @@ -3,6 +3,7 @@ import { Notification } from '@prisma/client'; export interface NotificationProvider { send: ( notification: Pick, + title: string, message: string ) => Promise; } diff --git a/src/server/trpc/routers/notification.ts b/src/server/trpc/routers/notification.ts index 0c4126f..2d27fea 100644 --- a/src/server/trpc/routers/notification.ts +++ b/src/server/trpc/routers/notification.ts @@ -4,7 +4,7 @@ import { prisma } from '../../model/_client'; import { sendNotification } from '../../model/notification'; export const notificationRouter = router({ - getAll: workspaceProcedure.query(({ input }) => { + all: workspaceProcedure.query(({ input }) => { const workspaceId = input.workspaceId; return prisma.notification.findMany({ @@ -23,7 +23,11 @@ export const notificationRouter = router({ }) ) .mutation(async ({ input }) => { - await sendNotification(input, `${input.name} + Notification Testing`); + await sendNotification( + input, + `${input.name} Notification Testing`, + `This is Notification Testing` + ); }), upsert: workspaceOwnerProcedure .input(