feat(notifications): integrate custom notification handling in server threshold alerts

This commit is contained in:
ChristoferMendes
2025-09-26 17:06:31 -03:00
parent 8a8ed58fef
commit 1ce880bd6d
2 changed files with 181 additions and 124 deletions
@@ -2,6 +2,7 @@ import { and, eq } from "drizzle-orm";
import { db } from "../../db"; import { db } from "../../db";
import { notifications } from "../../db/schema"; import { notifications } from "../../db/schema";
import { import {
sendCustomNotification,
sendDiscordNotification, sendDiscordNotification,
sendSlackNotification, sendSlackNotification,
sendTelegramNotification, sendTelegramNotification,
@@ -34,6 +35,7 @@ export const sendServerThresholdNotifications = async (
discord: true, discord: true,
telegram: true, telegram: true,
slack: true, slack: true,
custom: true,
}, },
}); });
@@ -41,7 +43,7 @@ export const sendServerThresholdNotifications = async (
const typeColor = 0xff0000; // Rojo para indicar alerta const typeColor = 0xff0000; // Rojo para indicar alerta
for (const notification of notificationList) { for (const notification of notificationList) {
const { discord, telegram, slack } = notification; const { discord, telegram, slack, custom } = notification;
if (discord) { if (discord) {
const decorate = (decoration: string, text: string) => const decorate = (decoration: string, text: string) =>
@@ -151,5 +153,20 @@ export const sendServerThresholdNotifications = async (
], ],
}); });
} }
if (custom) {
await sendCustomNotification(custom, {
title: `Server ${payload.Type} Alert`,
message: payload.Message,
serverName: payload.ServerName,
type: payload.Type,
currentValue: payload.Value,
threshold: payload.Threshold,
timestamp: date.toISOString(),
date: date.toLocaleString(),
status: "alert",
alertType: "server-threshold",
});
}
} }
}; };
+163 -123
View File
@@ -1,153 +1,193 @@
import type { import type {
discord, custom,
email, discord,
gotify, email,
ntfy, gotify,
slack, ntfy,
telegram, slack,
telegram,
} from "@dokploy/server/db/schema"; } from "@dokploy/server/db/schema";
import nodemailer from "nodemailer"; import nodemailer from "nodemailer";
export const sendEmailNotification = async ( export const sendEmailNotification = async (
connection: typeof email.$inferInsert, connection: typeof email.$inferInsert,
subject: string, subject: string,
htmlContent: string, htmlContent: string
) => { ) => {
try { try {
const { const {
smtpServer, smtpServer,
smtpPort, smtpPort,
username, username,
password, password,
fromAddress, fromAddress,
toAddresses, toAddresses,
} = connection; } = connection;
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
host: smtpServer, host: smtpServer,
port: smtpPort, port: smtpPort,
auth: { user: username, pass: password }, auth: { user: username, pass: password },
}); });
await transporter.sendMail({ await transporter.sendMail({
from: fromAddress, from: fromAddress,
to: toAddresses.join(", "), to: toAddresses.join(", "),
subject, subject,
html: htmlContent, html: htmlContent,
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
export const sendDiscordNotification = async ( export const sendDiscordNotification = async (
connection: typeof discord.$inferInsert, connection: typeof discord.$inferInsert,
embed: any, embed: any
) => { ) => {
// try { // try {
await fetch(connection.webhookUrl, { await fetch(connection.webhookUrl, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ embeds: [embed] }), body: JSON.stringify({ embeds: [embed] }),
}); });
// } catch (err) { // } catch (err) {
// console.log(err); // console.log(err);
// } // }
}; };
export const sendTelegramNotification = async ( export const sendTelegramNotification = async (
connection: typeof telegram.$inferInsert, connection: typeof telegram.$inferInsert,
messageText: string, messageText: string,
inlineButton?: { inlineButton?: {
text: string; text: string;
url: string; url: string;
}[][], }[][]
) => { ) => {
try { try {
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`; const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
await fetch(url, { await fetch(url, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
chat_id: connection.chatId, chat_id: connection.chatId,
message_thread_id: connection.messageThreadId, message_thread_id: connection.messageThreadId,
text: messageText, text: messageText,
parse_mode: "HTML", parse_mode: "HTML",
disable_web_page_preview: true, disable_web_page_preview: true,
reply_markup: { reply_markup: {
inline_keyboard: inlineButton, inline_keyboard: inlineButton,
}, },
}), }),
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
export const sendSlackNotification = async ( export const sendSlackNotification = async (
connection: typeof slack.$inferInsert, connection: typeof slack.$inferInsert,
message: any, message: any
) => { ) => {
try { try {
await fetch(connection.webhookUrl, { await fetch(connection.webhookUrl, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(message), body: JSON.stringify(message),
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
export const sendGotifyNotification = async ( export const sendGotifyNotification = async (
connection: typeof gotify.$inferInsert, connection: typeof gotify.$inferInsert,
title: string, title: string,
message: string, message: string
) => { ) => {
const response = await fetch(`${connection.serverUrl}/message`, { const response = await fetch(`${connection.serverUrl}/message`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Gotify-Key": connection.appToken, "X-Gotify-Key": connection.appToken,
}, },
body: JSON.stringify({ body: JSON.stringify({
title: title, title: title,
message: message, message: message,
priority: connection.priority, priority: connection.priority,
extras: { extras: {
"client::display": { "client::display": {
contentType: "text/plain", contentType: "text/plain",
}, },
}, },
}), }),
}); });
if (!response.ok) { if (!response.ok) {
throw new Error( throw new Error(
`Failed to send Gotify notification: ${response.statusText}`, `Failed to send Gotify notification: ${response.statusText}`
); );
} }
}; };
export const sendNtfyNotification = async ( export const sendNtfyNotification = async (
connection: typeof ntfy.$inferInsert, connection: typeof ntfy.$inferInsert,
title: string, title: string,
tags: string, tags: string,
actions: string, actions: string,
message: string, message: string
) => { ) => {
const response = await fetch(`${connection.serverUrl}/${connection.topic}`, { const response = await fetch(`${connection.serverUrl}/${connection.topic}`, {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${connection.accessToken}`, Authorization: `Bearer ${connection.accessToken}`,
"X-Priority": connection.priority?.toString() || "3", "X-Priority": connection.priority?.toString() || "3",
"X-Title": title, "X-Title": title,
"X-Tags": tags, "X-Tags": tags,
"X-Actions": actions, "X-Actions": actions,
}, },
body: message, body: message,
}); });
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to send ntfy notification: ${response.statusText}`); throw new Error(`Failed to send ntfy notification: ${response.statusText}`);
} }
};
export const sendCustomNotification = async (
connection: typeof custom.$inferInsert,
payload: Record<string, any>
) => {
try {
// Parse headers if provided
let headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (connection.headers) {
try {
headers = { ...headers, ...JSON.parse(connection.headers) };
} catch (error) {
console.error("Error parsing headers:", error);
}
}
// Default body with payload
const body = JSON.stringify(payload);
const response = await fetch(connection.endpoint, {
method: "POST",
headers,
body,
});
if (!response.ok) {
throw new Error(
`Failed to send custom notification: ${response.statusText}`
);
}
return response;
} catch (error) {
console.error("Error sending custom notification:", error);
throw error;
}
}; };