feat: add resend notification functionality

- Introduced a new notification type "resend" to the system.
- Added database schema for resend notifications including fields for apiKey, fromAddress, and toAddress.
- Implemented functions to create, update, and send resend notifications.
- Updated notification router to handle resend notifications with appropriate API endpoints.
- Enhanced existing notification services to support sending notifications via the Resend service.
- Modified various notification utilities to accommodate the new resend functionality.
This commit is contained in:
mhbdev
2026-01-24 21:29:19 +03:30
parent bc6647071f
commit 57eee45dbb
21 changed files with 7677 additions and 54 deletions
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -44,6 +45,7 @@ export const sendBuildErrorNotifications = async ({
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
custom: true,
@@ -52,10 +54,10 @@ export const sendBuildErrorNotifications = async ({
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
const { email, resend, discord, telegram, slack, gotify, ntfy, custom, lark } =
notification;
try {
if (email) {
if (email || resend) {
const template = await renderAsync(
BuildFailedEmail({
projectName,
@@ -66,11 +68,22 @@ export const sendBuildErrorNotifications = async ({
date: date.toLocaleString(),
}),
).catch();
await sendEmailNotification(
email,
"Build failed for dokploy",
template,
);
if (email) {
await sendEmailNotification(
email,
"Build failed for dokploy",
template,
);
}
if (resend) {
await sendResendNotification(
resend,
"Build failed for dokploy",
template,
);
}
}
if (discord) {
@@ -12,6 +12,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -47,6 +48,7 @@ export const sendBuildSuccessNotifications = async ({
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
custom: true,
@@ -55,10 +57,10 @@ export const sendBuildSuccessNotifications = async ({
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
const { email, resend, discord, telegram, slack, gotify, ntfy, custom, lark } =
notification;
try {
if (email) {
if (email || resend) {
const template = await renderAsync(
BuildSuccessEmail({
projectName,
@@ -69,11 +71,22 @@ export const sendBuildSuccessNotifications = async ({
environmentName,
}),
).catch();
await sendEmailNotification(
email,
"Build success for dokploy",
template,
);
if (email) {
await sendEmailNotification(
email,
"Build success for dokploy",
template,
);
}
if (resend) {
await sendResendNotification(
resend,
"Build success for dokploy",
template,
);
}
}
if (discord) {
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -44,6 +45,7 @@ export const sendDatabaseBackupNotifications = async ({
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
custom: true,
@@ -52,10 +54,10 @@ export const sendDatabaseBackupNotifications = async ({
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
const { email, resend, discord, telegram, slack, gotify, ntfy, custom, lark } =
notification;
try {
if (email) {
if (email || resend) {
const template = await renderAsync(
DatabaseBackupEmail({
projectName,
@@ -66,11 +68,22 @@ export const sendDatabaseBackupNotifications = async ({
date: date.toLocaleString(),
}),
).catch();
await sendEmailNotification(
email,
"Database backup for dokploy",
template,
);
if (email) {
await sendEmailNotification(
email,
"Database backup for dokploy",
template,
);
}
if (resend) {
await sendResendNotification(
resend,
"Database backup for dokploy",
template,
);
}
}
if (discord) {
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -31,6 +32,7 @@ export const sendDockerCleanupNotifications = async (
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
custom: true,
@@ -39,19 +41,29 @@ export const sendDockerCleanupNotifications = async (
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
const { email, resend, discord, telegram, slack, gotify, ntfy, custom, lark } =
notification;
try {
if (email) {
if (email || resend) {
const template = await renderAsync(
DockerCleanupEmail({ message, date: date.toLocaleString() }),
).catch();
await sendEmailNotification(
email,
"Docker cleanup for dokploy",
template,
);
if (email) {
await sendEmailNotification(
email,
"Docker cleanup for dokploy",
template,
);
}
if (resend) {
await sendResendNotification(
resend,
"Docker cleanup for dokploy",
template,
);
}
}
if (discord) {
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -25,6 +26,7 @@ export const sendDokployRestartNotifications = async () => {
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
custom: true,
@@ -33,20 +35,30 @@ export const sendDokployRestartNotifications = async () => {
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
const { email, resend, discord, telegram, slack, gotify, ntfy, custom, lark } =
notification;
try {
if (email) {
if (email || resend) {
const template = await renderAsync(
DokployRestartEmail({ date: date.toLocaleString() }),
).catch();
await sendEmailNotification(
email,
"Dokploy Server Restarted",
template,
);
if (email) {
await sendEmailNotification(
email,
"Dokploy Server Restarted",
template,
);
}
if (resend) {
await sendResendNotification(
resend,
"Dokploy Server Restarted",
template,
);
}
}
if (discord) {
@@ -5,10 +5,12 @@ import type {
gotify,
lark,
ntfy,
resend,
slack,
telegram,
} from "@dokploy/server/db/schema";
import nodemailer from "nodemailer";
import { Resend } from "resend";
export const sendEmailNotification = async (
connection: typeof email.$inferInsert,
@@ -45,6 +47,32 @@ export const sendEmailNotification = async (
}
};
export const sendResendNotification = async (
connection: typeof resend.$inferInsert,
subject: string,
htmlContent: string,
) => {
try {
const client = new Resend(connection.apiKey);
const result = await client.emails.send({
from: connection.fromAddress,
to: connection.toAddresses,
subject,
html: htmlContent,
});
if (result.error) {
throw new Error(result.error.message);
}
} catch (err) {
console.log(err);
throw new Error(
`Failed to send Resend notification ${err instanceof Error ? err.message : "Unknown error"}`,
);
}
};
export const sendDiscordNotification = async (
connection: typeof discord.$inferInsert,
embed: any,
@@ -9,6 +9,7 @@ import {
sendEmailNotification,
sendGotifyNotification,
sendNtfyNotification,
sendResendNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -51,15 +52,17 @@ export const sendVolumeBackupNotifications = async ({
discord: true,
telegram: true,
slack: true,
resend: true,
gotify: true,
ntfy: true,
},
});
for (const notification of notificationList) {
const { email, discord, telegram, slack, gotify, ntfy } = notification;
const { email, resend, discord, telegram, slack, gotify, ntfy } =
notification;
if (email) {
if (email || resend) {
const subject = `Volume Backup ${type === "success" ? "Successful" : "Failed"} - ${applicationName}`;
const htmlContent = await renderAsync(
VolumeBackupEmail({
@@ -73,7 +76,12 @@ export const sendVolumeBackupNotifications = async ({
date: date.toISOString(),
}),
);
await sendEmailNotification(email, subject, htmlContent);
if (email) {
await sendEmailNotification(email, subject, htmlContent);
}
if (resend) {
await sendResendNotification(resend, subject, htmlContent);
}
}
if (discord) {