mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(notifications: add app build error providerd
This commit is contained in:
@@ -398,7 +398,19 @@ export const UpdateNotification = ({ notificationId }: Props) => {
|
|||||||
<FormItem className="w-full">
|
<FormItem className="w-full">
|
||||||
<FormLabel>SMTP Port</FormLabel>
|
<FormLabel>SMTP Port</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="587" {...field} />
|
<Input
|
||||||
|
placeholder="587"
|
||||||
|
{...field}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
if (value) {
|
||||||
|
const port = Number.parseInt(value);
|
||||||
|
if (port > 0 && port < 65536) {
|
||||||
|
field.onChange(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { getAdvancedStats } from "@/server/monitoring/utilts";
|
|||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
import { generatePassword } from "@/templates/utils";
|
import { generatePassword } from "@/templates/utils";
|
||||||
import { generateAppName } from "@/server/db/schema/utils";
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
import { sendBuildFailedEmail } from "./notification";
|
import { sendBuildErrorNotifications } from "./notification";
|
||||||
export type Application = typeof applications.$inferSelect;
|
export type Application = typeof applications.$inferSelect;
|
||||||
|
|
||||||
export const createApplication = async (
|
export const createApplication = async (
|
||||||
@@ -161,7 +161,7 @@ export const deployApplication = async ({
|
|||||||
console.log("Error on build", error);
|
console.log("Error on build", error);
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updateApplicationStatus(applicationId, "error");
|
await updateApplicationStatus(applicationId, "error");
|
||||||
await sendBuildFailedEmail({
|
await sendBuildErrorNotifications({
|
||||||
projectName: application.project.name,
|
projectName: application.project.name,
|
||||||
applicationName: application.appName,
|
applicationName: application.appName,
|
||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
|
|||||||
@@ -507,38 +507,6 @@ export const sendEmailTestNotification = async (
|
|||||||
console.log("Email notification sent successfully");
|
console.log("Email notification sent successfully");
|
||||||
};
|
};
|
||||||
|
|
||||||
// export const sendInvitationEmail = async (
|
|
||||||
// emailTestConnection: typeof apiTestEmailConnection._type,
|
|
||||||
// inviteLink: string,
|
|
||||||
// toEmail: string,
|
|
||||||
// ) => {
|
|
||||||
// const { smtpServer, smtpPort, username, password, 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: toEmail,
|
|
||||||
// subject: "Invitation to join Dokploy",
|
|
||||||
// html: InvitationTemplate({
|
|
||||||
// inviteLink: inviteLink,
|
|
||||||
// toEmail: toEmail,
|
|
||||||
// }),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// await transporter.sendMail(mailOptions);
|
|
||||||
|
|
||||||
// console.log("Email notification sent successfully");
|
|
||||||
// };
|
|
||||||
|
|
||||||
export const sendBuildFailedEmail = async ({
|
export const sendBuildFailedEmail = async ({
|
||||||
projectName,
|
projectName,
|
||||||
applicationName,
|
applicationName,
|
||||||
@@ -600,3 +568,210 @@ export const sendBuildFailedEmail = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export const
|
||||||
|
|
||||||
|
export const sendBuildErrorNotifications = async ({
|
||||||
|
projectName,
|
||||||
|
applicationName,
|
||||||
|
applicationType,
|
||||||
|
errorMessage,
|
||||||
|
buildLink,
|
||||||
|
}: {
|
||||||
|
projectName: string;
|
||||||
|
applicationName: string;
|
||||||
|
applicationType: string;
|
||||||
|
errorMessage: string;
|
||||||
|
buildLink: string;
|
||||||
|
}) => {
|
||||||
|
const date = new Date();
|
||||||
|
const notificationList = await db.query.notifications.findMany({
|
||||||
|
where: eq(notifications.appBuildError, true),
|
||||||
|
with: {
|
||||||
|
email: true,
|
||||||
|
discord: true,
|
||||||
|
telegram: true,
|
||||||
|
slack: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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(
|
||||||
|
BuildFailedEmail({
|
||||||
|
projectName,
|
||||||
|
applicationName,
|
||||||
|
applicationType,
|
||||||
|
errorMessage,
|
||||||
|
buildLink,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
await transporter.sendMail(mailOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discord) {
|
||||||
|
const { webhookUrl } = discord;
|
||||||
|
const embed = {
|
||||||
|
title: "⚠️ Build Failed",
|
||||||
|
color: 0xff0000, // Rojo
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Project",
|
||||||
|
value: projectName,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Application",
|
||||||
|
value: applicationName,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Type",
|
||||||
|
value: applicationType,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error",
|
||||||
|
value: errorMessage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Build Link",
|
||||||
|
value: buildLink,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
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 = `
|
||||||
|
<b>⚠️ Build Failed</b>
|
||||||
|
|
||||||
|
<b>Project:</b> ${projectName}
|
||||||
|
<b>Application:</b> ${applicationName}
|
||||||
|
<b>Type:</b> ${applicationType}
|
||||||
|
<b>Time:</b> ${date.toLocaleString()}
|
||||||
|
|
||||||
|
<b>Error:</b>
|
||||||
|
<pre>${errorMessage}</pre>
|
||||||
|
|
||||||
|
<b>Build Details:</b> ${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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slack) {
|
||||||
|
const { webhookUrl, channel } = slack;
|
||||||
|
const message = {
|
||||||
|
channel: channel,
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
color: "#FF0000",
|
||||||
|
pretext: ":warning: *Build Failed*",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
title: "Project",
|
||||||
|
value: projectName,
|
||||||
|
short: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Application",
|
||||||
|
value: applicationName,
|
||||||
|
short: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Type",
|
||||||
|
value: applicationType,
|
||||||
|
short: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Time",
|
||||||
|
value: date.toLocaleString(),
|
||||||
|
short: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Error",
|
||||||
|
value: `\`\`\`${errorMessage}\`\`\``,
|
||||||
|
short: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
text: "View Build Details",
|
||||||
|
url: "https://doks.dev/build-details",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user