refactor(notifications): change render to renderAsync in emails

This commit is contained in:
Mauricio Siu
2024-07-19 23:56:48 -06:00
parent 18f892096b
commit 44ee326057
11 changed files with 147 additions and 126 deletions

View File

@@ -20,6 +20,12 @@ import {
apiUpdateTelegram,
notifications,
} from "@/server/db/schema";
import {
sendDiscordNotification,
sendEmailNotification,
sendSlackNotification,
sendTelegramNotification,
} from "@/server/utils/notifications/utils";
import { TRPCError } from "@trpc/server";
import { desc } from "drizzle-orm";
import {
@@ -29,10 +35,6 @@ import {
createTelegramNotification,
findNotificationById,
removeNotificationById,
sendDiscordNotification,
sendEmailNotification,
sendSlackNotification,
sendTelegramNotification,
updateDiscordNotification,
updateEmailNotification,
updateSlackNotification,

View File

@@ -146,3 +146,12 @@ export const removeUserByAuthId = async (authId: string) => {
.returning()
.then((res) => res[0]);
};
export const getDokployUrl = async () => {
const admin = await findAdmin();
if (admin.host) {
return `https://${admin.host}`;
}
return `http://${admin.serverIp}:${process.env.PORT}`;
};

View File

@@ -15,7 +15,7 @@ import { createTraefikConfig } from "@/server/utils/traefik/application";
import { generatePassword } from "@/templates/utils";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { findAdmin } from "./admin";
import { findAdmin, getDokployUrl } from "./admin";
import { createDeployment, updateDeploymentStatus } from "./deployment";
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
@@ -140,6 +140,7 @@ export const deployApplication = async ({
descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
const admin = await findAdmin();
const deployment = await createDeployment({
applicationId: applicationId,
@@ -164,7 +165,7 @@ export const deployApplication = async ({
projectName: application.project.name,
applicationName: application.name,
applicationType: "application",
buildLink: deployment.logPath,
buildLink,
});
} catch (error) {
await updateDeploymentStatus(deployment.deploymentId, "error");
@@ -173,8 +174,9 @@ export const deployApplication = async ({
projectName: application.project.name,
applicationName: application.name,
applicationType: "application",
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink: deployment.logPath,
buildLink,
});
console.log(

View File

@@ -5,6 +5,8 @@ import { type apiCreateCompose, compose } from "@/server/db/schema";
import { generateAppName } from "@/server/db/schema/utils";
import { buildCompose } from "@/server/utils/builders/compose";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success";
import { execAsync } from "@/server/utils/process/execAsync";
import { cloneGitRepository } from "@/server/utils/providers/git";
import { cloneGithubRepository } from "@/server/utils/providers/github";
@@ -13,7 +15,7 @@ import { generatePassword } from "@/templates/utils";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { load } from "js-yaml";
import { findAdmin } from "./admin";
import { findAdmin, getDokployUrl } from "./admin";
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
import { validUniqueServerAppName } from "./project";
@@ -142,6 +144,7 @@ export const deployCompose = async ({
}) => {
const compose = await findComposeById(composeId);
const admin = await findAdmin();
const buildLink = `${await getDokployUrl()}/dashboard/project/${compose.projectId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
@@ -161,11 +164,26 @@ export const deployCompose = async ({
await updateCompose(composeId, {
composeStatus: "done",
});
await sendBuildSuccessNotifications({
projectName: compose.project.name,
applicationName: compose.name,
applicationType: "compose",
buildLink,
});
} catch (error) {
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateCompose(composeId, {
composeStatus: "error",
});
await sendBuildErrorNotifications({
projectName: compose.project.name,
applicationName: compose.name,
applicationType: "compose",
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink,
});
throw error;
}
};

View File

@@ -59,6 +59,7 @@ void app.prepare().then(async () => {
await migration();
await sendDokployRestartNotifications();
}
server.listen(PORT);
console.log("Server Started:", PORT);
deploymentWorker.run();

View File

@@ -1,7 +1,7 @@
import BuildFailedEmail from "@/emails/emails/build-failed";
import { db } from "@/server/db";
import { notifications } from "@/server/db/schema";
import { render } from "@react-email/components";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import {
sendDiscordNotification,
@@ -39,20 +39,17 @@ export const sendBuildErrorNotifications = async ({
for (const notification of notificationList) {
const { email, discord, telegram, slack } = notification;
if (email) {
await sendEmailNotification(
email,
"Build failed for dokploy",
render(
BuildFailedEmail({
projectName,
applicationName,
applicationType,
errorMessage,
buildLink,
date: date.toLocaleString(),
}),
),
);
const template = await renderAsync(
BuildFailedEmail({
projectName,
applicationName,
applicationType,
errorMessage: errorMessage,
buildLink,
date: date.toLocaleString(),
}),
).catch();
await sendEmailNotification(email, "Build failed for dokploy", template);
}
if (discord) {
@@ -149,7 +146,7 @@ export const sendBuildErrorNotifications = async ({
{
type: "button",
text: "View Build Details",
url: "https://doks.dev/build-details",
url: buildLink,
},
],
},

View File

@@ -1,7 +1,7 @@
import BuildSuccessEmail from "@/emails/emails/build-success";
import { db } from "@/server/db";
import { notifications } from "@/server/db/schema";
import { render } from "@react-email/components";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import {
sendDiscordNotification,
@@ -38,19 +38,16 @@ export const sendBuildSuccessNotifications = async ({
const { email, discord, telegram, slack } = notification;
if (email) {
await sendEmailNotification(
email,
"Build success for dokploy",
render(
BuildSuccessEmail({
projectName,
applicationName,
applicationType,
buildLink,
date: date.toLocaleString(),
}),
),
);
const template = await renderAsync(
BuildSuccessEmail({
projectName,
applicationName,
applicationType,
buildLink,
date: date.toLocaleString(),
}),
).catch();
await sendEmailNotification(email, "Build success for dokploy", template);
}
if (discord) {
@@ -130,16 +127,12 @@ export const sendBuildSuccessNotifications = async ({
value: date.toLocaleString(),
short: true,
},
{
title: "Build Link",
value: buildLink,
},
],
actions: [
{
type: "button",
text: "View Build Details",
url: "https://doks.dev/build-details",
url: buildLink,
},
],
},

View File

@@ -1,7 +1,7 @@
import DatabaseBackupEmail from "@/emails/emails/database-backup";
import { db } from "@/server/db";
import { notifications } from "@/server/db/schema";
import { render } from "@react-email/components";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import {
sendDiscordNotification,
@@ -38,19 +38,20 @@ export const sendDatabaseBackupNotifications = async ({
const { email, discord, telegram, slack } = notification;
if (email) {
const template = await renderAsync(
DatabaseBackupEmail({
projectName,
applicationName,
databaseType,
type,
errorMessage,
date: date.toLocaleString(),
}),
).catch();
await sendEmailNotification(
email,
"Database backup for dokploy",
render(
DatabaseBackupEmail({
projectName,
applicationName,
databaseType,
type,
errorMessage,
date: date.toLocaleString(),
}),
),
template,
);
}
@@ -168,13 +169,6 @@ export const sendDatabaseBackupNotifications = async ({
value: type === "success" ? "Successful" : "Failed",
},
],
actions: [
{
type: "button",
text: "View Build Details",
url: "https://doks.dev/build-details",
},
],
},
],
});

View File

@@ -1,7 +1,7 @@
import DockerCleanupEmail from "@/emails/emails/docker-cleanup";
import { db } from "@/server/db";
import { notifications } from "@/server/db/schema";
import { render } from "@react-email/components";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import {
sendDiscordNotification,
@@ -28,10 +28,14 @@ export const sendDockerCleanupNotifications = async (
const { email, discord, telegram, slack } = notification;
if (email) {
const template = await renderAsync(
DockerCleanupEmail({ message, date: date.toLocaleString() }),
).catch();
await sendEmailNotification(
email,
"Docker cleanup for dokploy",
render(DockerCleanupEmail({ message, date: date.toLocaleString() })),
template,
);
}
@@ -82,13 +86,6 @@ export const sendDockerCleanupNotifications = async (
short: true,
},
],
actions: [
{
type: "button",
text: "View Build Details",
url: "https://doks.dev/build-details",
},
],
},
],
});

View File

@@ -1,7 +1,7 @@
import DokployRestartEmail from "@/emails/emails/dokploy-restart";
import { db } from "@/server/db";
import { notifications } from "@/server/db/schema";
import { render } from "@react-email/components";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import {
sendDiscordNotification,
@@ -26,17 +26,16 @@ export const sendDokployRestartNotifications = async () => {
const { email, discord, telegram, slack } = notification;
if (email) {
await sendEmailNotification(
email,
"Dokploy Server Restarted",
render(DokployRestartEmail({ date: date.toLocaleString() })),
);
const template = await renderAsync(
DokployRestartEmail({ date: date.toLocaleString() }),
).catch();
await sendEmailNotification(email, "Dokploy Server Restarted", template);
}
if (discord) {
await sendDiscordNotification(discord, {
title: "✅ Dokploy Server Restarted",
color: 0x00ff00,
color: 0xff0000,
fields: [
{
name: "Time",
@@ -67,7 +66,7 @@ export const sendDokployRestartNotifications = async () => {
channel: channel,
attachments: [
{
color: "#FF0000",
color: "#00FF00",
pretext: ":white_check_mark: *Dokploy Server Restarted*",
fields: [
{
@@ -76,13 +75,6 @@ export const sendDokployRestartNotifications = async () => {
short: true,
},
],
actions: [
{
type: "button",
text: "View Build Details",
url: "https://doks.dev/build-details",
},
],
},
],
});

View File

@@ -6,64 +6,80 @@ export const sendEmailNotification = async (
subject: string,
htmlContent: string,
) => {
const { smtpServer, smtpPort, username, password, fromAddress, toAddresses } =
connection;
const transporter = nodemailer.createTransport({
host: smtpServer,
port: smtpPort,
secure: smtpPort === 465,
auth: { user: username, pass: password },
});
try {
const {
smtpServer,
smtpPort,
username,
password,
fromAddress,
toAddresses,
} = connection;
const transporter = nodemailer.createTransport({
host: smtpServer,
port: smtpPort,
secure: smtpPort === 465,
auth: { user: username, pass: password },
});
await transporter.sendMail({
from: fromAddress,
to: toAddresses.join(", "),
subject,
html: htmlContent,
});
await transporter.sendMail({
from: fromAddress,
to: toAddresses.join(", "),
subject,
html: htmlContent,
});
} catch (err) {
console.log(err);
}
};
export const sendDiscordNotification = async (
connection: typeof discord.$inferInsert,
embed: any,
) => {
const response = await fetch(connection.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ embeds: [embed] }),
});
if (!response.ok) throw new Error("Failed to send Discord notification");
try {
await fetch(connection.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ embeds: [embed] }),
});
} catch (err) {
console.log(err);
}
};
export const sendTelegramNotification = async (
connection: typeof telegram.$inferInsert,
messageText: string,
) => {
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: connection.chatId,
text: messageText,
parse_mode: "HTML",
disable_web_page_preview: true,
}),
});
if (!response.ok) throw new Error("Failed to send Telegram notification");
try {
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: connection.chatId,
text: messageText,
parse_mode: "HTML",
disable_web_page_preview: true,
}),
});
} catch (err) {
console.log(err);
}
};
export const sendSlackNotification = async (
connection: typeof slack.$inferInsert,
message: any,
) => {
const response = await fetch(connection.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(message),
});
if (!response.ok) throw new Error("Failed to send Slack notification");
try {
await fetch(connection.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(message),
});
} catch (err) {
console.log(err);
}
};