From e0bde5cec9fad63d1032d686c8af511271e3741a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 19 Jul 2024 01:41:01 -0600 Subject: [PATCH] refactor(notifications): minimize send notifications in more reusable fn --- server/api/routers/notification.ts | 34 +- server/api/services/notification.ts | 605 +++++----------------------- server/server.ts | 3 +- 3 files changed, 132 insertions(+), 510 deletions(-) diff --git a/server/api/routers/notification.ts b/server/api/routers/notification.ts index e7a252fb..4ed5e22f 100644 --- a/server/api/routers/notification.ts +++ b/server/api/routers/notification.ts @@ -21,6 +21,7 @@ import { notifications, } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; +import { desc } from "drizzle-orm"; import { createDiscordNotification, createEmailNotification, @@ -28,16 +29,15 @@ import { createTelegramNotification, findNotificationById, removeNotificationById, - sendDiscordTestNotification, - sendEmailTestNotification, - sendSlackTestNotification, - sendTelegramTestNotification, + sendDiscordNotification, + sendEmailNotification, + sendSlackNotification, + sendTelegramNotification, updateDiscordNotification, updateEmailNotification, updateSlackNotification, updateTelegramNotification, } from "../services/notification"; -import { desc } from "drizzle-orm"; export const notificationRouter = createTRPCRouter({ createSlack: adminProcedure @@ -71,7 +71,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestSlackConnection) .mutation(async ({ input }) => { try { - await sendSlackTestNotification(input); + await sendSlackNotification(input, { + channel: input.channel, + text: "Hi, From Dokploy 👋", + }); return true; } catch (error) { throw new TRPCError({ @@ -112,7 +115,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestTelegramConnection) .mutation(async ({ input }) => { try { - await sendTelegramTestNotification(input); + await sendTelegramNotification(input, "Hi, From Dokploy 👋"); return true; } catch (error) { throw new TRPCError({ @@ -126,6 +129,12 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateDiscord) .mutation(async ({ input }) => { try { + // go to your discord server + // go to settings + // go to integrations + // add a new integration + // select webhook + // copy the webhook url return await createDiscordNotification(input); } catch (error) { throw new TRPCError({ @@ -154,7 +163,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestDiscordConnection) .mutation(async ({ input }) => { try { - await sendDiscordTestNotification(input); + await sendDiscordNotification(input, { + title: "Test Notification", + description: "Hi, From Dokploy 👋", + }); return true; } catch (error) { throw new TRPCError({ @@ -195,7 +207,11 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestEmailConnection) .mutation(async ({ input }) => { try { - await sendEmailTestNotification(input); + await sendEmailNotification( + input, + "Test Email", + "
Hi, From Dokploy 👋
", + ); return true; } catch (error) { throw new TRPCError({ diff --git a/server/api/services/notification.ts b/server/api/services/notification.ts index 518f3a99..cbbb7274 100644 --- a/server/api/services/notification.ts +++ b/server/api/services/notification.ts @@ -420,97 +420,6 @@ export const updateDestinationById = async ( return result[0]; }; -export const sendSlackTestNotification = async ( - slackTestConnection: typeof apiTestSlackConnection._type, -) => { - const { webhookUrl, channel } = slackTestConnection; - - const response = await fetch(webhookUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ text: "Hi, From Dokploy 👋", channel }), - }); - - if (!response.ok) { - throw new Error("Error to send test notification"); - } -}; - -export const sendTelegramTestNotification = async ( - telegramTestConnection: typeof apiTestTelegramConnection._type, -) => { - const { botToken, chatId } = telegramTestConnection; - const url = `https://api.telegram.org/bot${botToken}/sendMessage`; - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - chat_id: chatId, - text: "Hi, From Dokploy 👋", - }), - }); - - if (!response.ok) { - throw new Error("Error to send test notification"); - } -}; - -export const sendDiscordTestNotification = async ( - discordTestConnection: typeof apiTestDiscordConnection._type, -) => { - const { webhookUrl } = discordTestConnection; - // go to your discord server - // go to settings - // go to integrations - // add a new integration - // select webhook - // copy the webhook url - const response = await fetch(webhookUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - content: "Hi, From Dokploy 👋", - }), - }); - - if (!response.ok) { - throw new Error("Error to send test notification"); - } -}; - -export const sendEmailTestNotification = async ( - emailTestConnection: typeof apiTestEmailConnection._type, -) => { - const { smtpServer, smtpPort, username, password, toAddresses, fromAddress } = - emailTestConnection; - const transporter = nodemailer.createTransport({ - host: smtpServer, - port: smtpPort, - secure: smtpPort === 465, - auth: { - user: username, - pass: password, - }, - } as SMTPTransport.Options); - // need to add a valid from address - const mailOptions = { - from: fromAddress, - to: toAddresses?.join(", "), - subject: "Test email", - text: "Hi, From Dokploy 👋", - }; - - await transporter.sendMail(mailOptions); - - console.log("Email notification sent successfully"); -}; - interface BuildFailedEmailProps { projectName: string; applicationName: string; @@ -540,28 +449,10 @@ export const sendBuildErrorNotifications = async ({ for (const notification of notificationList) { const { email, discord, telegram, slack } = notification; if (email) { - const { - smtpServer, - smtpPort, - username, - password, - fromAddress, - toAddresses, - } = email; - const transporter = nodemailer.createTransport({ - host: smtpServer, - port: smtpPort, - secure: smtpPort === 465, - auth: { - user: username, - pass: password, - }, - } as SMTPTransport.Options); - const mailOptions = { - from: fromAddress, - to: toAddresses?.join(", "), - subject: "Build failed for dokploy", - html: render( + await sendEmailNotification( + email, + "Build failed for dokploy", + render( BuildFailedEmail({ projectName, applicationName, @@ -571,13 +462,12 @@ export const sendBuildErrorNotifications = async ({ date: date.toLocaleString(), }), ), - }; - await transporter.sendMail(mailOptions); + ); } if (discord) { const { webhookUrl } = discord; - const embed = { + await sendDiscordNotification(discord, { title: "⚠️ Build Failed", color: 0xff0000, fields: [ @@ -609,59 +499,31 @@ export const sendBuildErrorNotifications = async ({ footer: { text: "Dokploy Build Notification", }, - }; - const response = await fetch(webhookUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - embeds: [embed], - }), }); - - if (!response.ok) { - throw new Error("Error to send test notification"); - } } if (telegram) { - const { botToken, chatId } = telegram; - const messageText = ` - ⚠️ Build Failed - - Project: ${projectName} - Application: ${applicationName} - Type: ${applicationType} - Time: ${date.toLocaleString()} - - Error: -${errorMessage}
-
- Build Details: ${buildLink}
- `;
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- chat_id: chatId,
- text: messageText,
- parse_mode: "HTML",
- disable_web_page_preview: true,
- }),
- });
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
+ await sendTelegramNotification(
+ telegram,
+ `
+ ⚠️ Build Failed
+
+ Project: ${projectName}
+ Application: ${applicationName}
+ Type: ${applicationType}
+ Time: ${date.toLocaleString()}
+
+ Error:
+ ${errorMessage}
+
+ Build Details: ${buildLink}
+ `,
+ );
}
if (slack) {
- const { webhookUrl, channel } = slack;
- const message = {
+ const { channel } = slack;
+ await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
@@ -703,18 +565,7 @@ export const sendBuildErrorNotifications = async ({
],
},
],
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(message),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
}
};
@@ -747,28 +598,10 @@ export const sendBuildSuccessNotifications = async ({
const { email, discord, telegram, slack } = notification;
if (email) {
- const {
- smtpServer,
- smtpPort,
- username,
- password,
- fromAddress,
- toAddresses,
- } = email;
- const transporter = nodemailer.createTransport({
- host: smtpServer,
- port: smtpPort,
- secure: smtpPort === 465,
- auth: {
- user: username,
- pass: password,
- },
- } as SMTPTransport.Options);
- const mailOptions = {
- from: fromAddress,
- to: toAddresses?.join(", "),
- subject: "Build success for dokploy",
- html: render(
+ await sendEmailNotification(
+ email,
+ "Build success for dokploy",
+ render(
BuildSuccessEmail({
projectName,
applicationName,
@@ -777,13 +610,11 @@ export const sendBuildSuccessNotifications = async ({
date: date.toLocaleString(),
}),
),
- };
- await transporter.sendMail(mailOptions);
+ );
}
if (discord) {
- const { webhookUrl } = discord;
- const embed = {
+ await sendDiscordNotification(discord, {
title: "✅ Build Success",
color: 0x00ff00,
fields: [
@@ -811,56 +642,28 @@ export const sendBuildSuccessNotifications = async ({
footer: {
text: "Dokploy Build Notification",
},
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- embeds: [embed],
- }),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
if (telegram) {
- const { botToken, chatId } = telegram;
- const messageText = `
- ✅ Build Success
-
- Project: ${projectName}
- Application: ${applicationName}
- Type: ${applicationType}
- Time: ${date.toLocaleString()}
-
- Build Details: ${buildLink}
- `;
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- chat_id: chatId,
- text: messageText,
- parse_mode: "HTML",
- disable_web_page_preview: true,
- }),
- });
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
+ await sendTelegramNotification(
+ telegram,
+ `
+ ✅ Build Success
+
+ Project: ${projectName}
+ Application: ${applicationName}
+ Type: ${applicationType}
+ Time: ${date.toLocaleString()}
+
+ Build Details: ${buildLink}
+ `,
+ );
}
if (slack) {
const { webhookUrl, channel } = slack;
- const message = {
+ await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
@@ -901,18 +704,7 @@ export const sendBuildSuccessNotifications = async ({
],
},
],
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(message),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
}
};
@@ -945,28 +737,10 @@ export const sendDatabaseBackupNotifications = async ({
const { email, discord, telegram, slack } = notification;
if (email) {
- const {
- smtpServer,
- smtpPort,
- username,
- password,
- fromAddress,
- toAddresses,
- } = email;
- const transporter = nodemailer.createTransport({
- host: smtpServer,
- port: smtpPort,
- secure: smtpPort === 465,
- auth: {
- user: username,
- pass: password,
- },
- } as SMTPTransport.Options);
- const mailOptions = {
- from: fromAddress,
- to: toAddresses?.join(", "),
- subject: "Database backup for dokploy",
- html: render(
+ await sendEmailNotification(
+ email,
+ "Database backup for dokploy",
+ render(
DatabaseBackupEmail({
projectName,
applicationName,
@@ -976,13 +750,11 @@ export const sendDatabaseBackupNotifications = async ({
date: date.toLocaleString(),
}),
),
- };
- await transporter.sendMail(mailOptions);
+ );
}
if (discord) {
- const { webhookUrl } = discord;
- const embed = {
+ await sendDiscordNotification(discord, {
title:
type === "success"
? "✅ Database Backup Successful"
@@ -1013,36 +785,23 @@ export const sendDatabaseBackupNotifications = async ({
name: "Type",
value: type,
},
+ ...(type === "error" && errorMessage
+ ? [
+ {
+ name: "Error Message",
+ value: errorMessage,
+ },
+ ]
+ : []),
],
timestamp: date.toISOString(),
footer: {
text: "Dokploy Database Backup Notification",
},
- };
-
- if (type === "error" && errorMessage) {
- embed.fields.push({
- name: "Error Message",
- value: errorMessage as unknown as string,
- });
- }
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- embeds: [embed],
- }),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
if (telegram) {
- const { botToken, chatId } = telegram;
const statusEmoji = type === "success" ? "✅" : "❌";
const messageText = `
${statusEmoji} Database Backup ${type === "success" ? "Successful" : "Failed"}
@@ -1055,28 +814,12 @@ export const sendDatabaseBackupNotifications = async ({
Status: ${type === "success" ? "Successful" : "Failed"}
${type === "error" && errorMessage ? `Error: ${errorMessage}` : ""}
`;
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- chat_id: chatId,
- text: messageText,
- parse_mode: "HTML",
- disable_web_page_preview: true,
- }),
- });
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
+ await sendTelegramNotification(telegram, messageText);
}
if (slack) {
- const { webhookUrl, channel } = slack;
- const message = {
+ const { channel } = slack;
+ await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
@@ -1086,6 +829,15 @@ export const sendDatabaseBackupNotifications = async ({
? ":white_check_mark: *Database Backup Successful*"
: ":x: *Database Backup Failed*",
fields: [
+ ...(type === "error" && errorMessage
+ ? [
+ {
+ title: "Error Message",
+ value: errorMessage,
+ short: false,
+ },
+ ]
+ : []),
{
title: "Project",
value: projectName,
@@ -1124,25 +876,7 @@ export const sendDatabaseBackupNotifications = async ({
],
},
],
- };
-
- if (type === "error" && errorMessage) {
- message.attachments[0].fields.push({
- title: "Error Message",
- value: errorMessage,
- });
- }
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(message),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
}
};
@@ -1165,40 +899,15 @@ export const sendDockerCleanupNotifications = async (
const { email, discord, telegram, slack } = notification;
if (email) {
- const {
- smtpServer,
- smtpPort,
- username,
- password,
- fromAddress,
- toAddresses,
- } = email;
- const transporter = nodemailer.createTransport({
- host: smtpServer,
- port: smtpPort,
- secure: smtpPort === 465,
- auth: {
- user: username,
- pass: password,
- },
- } as SMTPTransport.Options);
- const mailOptions = {
- from: fromAddress,
- to: toAddresses?.join(", "),
- subject: "Docker cleanup for dokploy",
- html: render(
- DockerCleanupEmail({
- message,
- date: date.toLocaleString(),
- }),
- ),
- };
- await transporter.sendMail(mailOptions);
+ await sendEmailNotification(
+ email,
+ "Docker cleanup for dokploy",
+ render(DockerCleanupEmail({ message, date: date.toLocaleString() })),
+ );
}
if (discord) {
- const { webhookUrl } = discord;
- const embed = {
+ await sendDiscordNotification(discord, {
title: "✅ Docker Cleanup",
color: 0x00ff00,
fields: [
@@ -1211,51 +920,23 @@ export const sendDockerCleanupNotifications = async (
footer: {
text: "Dokploy Docker Cleanup Notification",
},
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- embeds: [embed],
- }),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
if (telegram) {
- const { botToken, chatId } = telegram;
- const messageText = `
- ✅ Docker Cleanup
- Message: ${message}
- Time: ${date.toLocaleString()}
- `;
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- chat_id: chatId,
- text: messageText,
- parse_mode: "HTML",
- disable_web_page_preview: true,
- }),
- });
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
+ await sendTelegramNotification(
+ telegram,
+ `
+ ✅ Docker Cleanup
+ Message: ${message}
+ Time: ${date.toLocaleString()}
+ `,
+ );
}
if (slack) {
- const { webhookUrl, channel } = slack;
- const messageResponse = {
+ const { channel } = slack;
+ await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
@@ -1281,24 +962,13 @@ export const sendDockerCleanupNotifications = async (
],
},
],
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(messageResponse),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
}
};
export const sendEmailNotification = async (
- connection: typeof email.$inferSelect,
+ connection: typeof email.$inferInsert,
subject: string,
htmlContent: string,
) => {
@@ -1320,7 +990,7 @@ export const sendEmailNotification = async (
};
export const sendDiscordNotification = async (
- connection: typeof discord.$inferSelect,
+ connection: typeof discord.$inferInsert,
embed: any,
) => {
const response = await fetch(connection.webhookUrl, {
@@ -1333,7 +1003,7 @@ export const sendDiscordNotification = async (
};
export const sendTelegramNotification = async (
- connection: typeof telegram.$inferSelect,
+ connection: typeof telegram.$inferInsert,
messageText: string,
) => {
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
@@ -1352,7 +1022,7 @@ export const sendTelegramNotification = async (
};
export const sendSlackNotification = async (
- connection: typeof slack.$inferSelect,
+ connection: typeof slack.$inferInsert,
message: any,
) => {
const response = await fetch(connection.webhookUrl, {
@@ -1380,41 +1050,17 @@ export const sendDokployRestartNotifications = async () => {
const { email, discord, telegram, slack } = notification;
if (email) {
- const {
- smtpServer,
- smtpPort,
- username,
- password,
- fromAddress,
- toAddresses,
- } = email;
- const transporter = nodemailer.createTransport({
- host: smtpServer,
- port: smtpPort,
- secure: smtpPort === 465,
- auth: {
- user: username,
- pass: password,
- },
- } as SMTPTransport.Options);
- const mailOptions = {
- from: fromAddress,
- to: toAddresses?.join(", "),
- subject: "Dokploy Server Restarted",
- html: render(
- DokployRestartEmail({
- date: date.toLocaleString(),
- }),
- ),
- };
- await transporter.sendMail(mailOptions);
+ await sendEmailNotification(
+ email,
+ "Dokploy Server Restarted",
+ render(DokployRestartEmail({ date: date.toLocaleString() })),
+ );
}
if (discord) {
- const { webhookUrl } = discord;
- const embed = {
+ await sendDiscordNotification(discord, {
title: "✅ Dokploy Server Restarted",
- color: 0xff0000,
+ color: 0x00ff00,
fields: [
{
name: "Time",
@@ -1426,50 +1072,22 @@ export const sendDokployRestartNotifications = async () => {
footer: {
text: "Dokploy Restart Notification",
},
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- embeds: [embed],
- }),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
if (telegram) {
- const { botToken, chatId } = telegram;
- const messageText = `
- ✅ Dokploy Serverd Restarted
- Time: ${date.toLocaleString()}
- `;
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- chat_id: chatId,
- text: messageText,
- parse_mode: "HTML",
- disable_web_page_preview: true,
- }),
- });
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
+ await sendTelegramNotification(
+ telegram,
+ `
+ ✅ Dokploy Serverd Restarted
+ Time: ${date.toLocaleString()}
+ `,
+ );
}
if (slack) {
- const { webhookUrl, channel } = slack;
- const message = {
+ const { channel } = slack;
+ await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
@@ -1491,18 +1109,7 @@ export const sendDokployRestartNotifications = async () => {
],
},
],
- };
- const response = await fetch(webhookUrl, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(message),
});
-
- if (!response.ok) {
- throw new Error("Error to send test notification");
- }
}
}
};
diff --git a/server/server.ts b/server/server.ts
index 2efee269..0e8649cf 100644
--- a/server/server.ts
+++ b/server/server.ts
@@ -57,9 +57,8 @@ void app.prepare().then(async () => {
// Timeout to wait for the database to be ready
await new Promise((resolve) => setTimeout(resolve, 7000));
await migration();
+ await sendDokployRestartNotifications();
}
- await sendDokployRestartNotifications();
-
server.listen(PORT);
console.log("Server Started:", PORT);
deploymentWorker.run();