import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DokployBackupEmail from "@dokploy/server/emails/emails/dokploy-backup"; import { renderAsync } from "@react-email/components"; import { format } from "date-fns"; import { eq } from "drizzle-orm"; import { sendCustomNotification, sendDiscordNotification, sendEmailNotification, sendGotifyNotification, sendLarkNotification, sendMattermostNotification, sendNtfyNotification, sendPushoverNotification, sendResendNotification, sendSlackNotification, sendTeamsNotification, sendTelegramNotification, } from "./utils"; export const sendDokployBackupNotifications = async ({ type, errorMessage, backupSize, }: { type: "error" | "success"; errorMessage?: string; backupSize?: string; }) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); const notificationList = await db.query.notifications.findMany({ where: eq(notifications.dokployBackup, true), with: { email: true, discord: true, telegram: true, slack: true, resend: true, gotify: true, ntfy: true, mattermost: true, custom: true, lark: true, pushover: true, teams: true, }, }); for (const notification of notificationList) { const { email, discord, telegram, slack, resend, gotify, ntfy, mattermost, custom, lark, pushover, teams, } = notification; try { if (email || resend) { const template = await renderAsync( DokployBackupEmail({ type, errorMessage, date: date.toLocaleString(), backupSize, }), ).catch(); if (email) { await sendEmailNotification( email, "Dokploy instance backup", template, ); } if (resend) { await sendResendNotification( resend, "Dokploy instance backup", template, ); } } if (discord) { const decorate = (decoration: string, text: string) => `${discord.decoration ? decoration : ""} ${text}`.trim(); await sendDiscordNotification(discord, { title: type === "success" ? decorate(">", "`βœ…` Dokploy Backup Successful") : decorate(">", "`❌` Dokploy Backup Failed"), color: type === "success" ? 0x57f287 : 0xed4245, fields: [ { name: decorate("`πŸ“¦`", "Backup Type"), value: "Complete Dokploy Instance", inline: true, }, ...(backupSize ? [ { name: decorate("`πŸ’Ύ`", "Backup Size"), value: backupSize, inline: true, }, ] : []), { name: decorate("`πŸ“…`", "Date"), value: ``, inline: true, }, { name: decorate("`⌚`", "Time"), value: ``, inline: true, }, { name: decorate("`❓`", "Status"), value: type .replace("error", "Failed") .replace("success", "Successful"), inline: true, }, ...(type === "error" && errorMessage ? [ { name: decorate("`⚠️`", "Error Message"), value: `\`\`\`${errorMessage}\`\`\``, }, ] : []), ], timestamp: date.toISOString(), footer: { text: "Dokploy Instance Backup Notification", }, }); } if (gotify) { const decorate = (decoration: string, text: string) => `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( gotify, decorate( type === "success" ? "βœ…" : "❌", `Dokploy Backup ${type === "success" ? "Successful" : "Failed"}`, ), `${decorate("πŸ“¦", "Backup Type: Complete Dokploy Instance")}` + `${backupSize ? decorate("πŸ’Ύ", `Backup Size: ${backupSize}`) : ""}` + `${decorate("πŸ•’", `Date: ${date.toLocaleString()}`)}` + `${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`, ); } if (ntfy) { await sendNtfyNotification( ntfy, `Dokploy Backup ${type === "success" ? "Successful" : "Failed"}`, `${type === "success" ? "white_check_mark" : "x"}`, "", "πŸ“¦Backup Type: Complete Dokploy Instance\n" + `${backupSize ? `πŸ’ΎBackup Size: ${backupSize}\n` : ""}` + `πŸ•’Date: ${date.toLocaleString()}\n` + `${type === "error" && errorMessage ? `❌Error:\n${errorMessage}` : ""}`, ); } if (telegram) { const isError = type === "error" && errorMessage; const statusEmoji = type === "success" ? "βœ…" : "❌"; const typeStatus = type === "success" ? "Successful" : "Failed"; const errorMsg = isError ? `\n\nError:\n
${errorMessage}
` : ""; const sizeInfo = backupSize ? `\nBackup Size: ${backupSize}` : ""; const messageText = `${statusEmoji} Dokploy Backup ${typeStatus}\n\nBackup Type: Complete Dokploy Instance${sizeInfo}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; await sendTelegramNotification(telegram, messageText); } if (slack) { const { channel } = slack; await sendSlackNotification(slack, { channel: channel, attachments: [ { color: type === "success" ? "#00FF00" : "#FF0000", pretext: type === "success" ? ":white_check_mark: *Dokploy Backup Successful*" : ":x: *Dokploy Backup Failed*", fields: [ ...(type === "error" && errorMessage ? [ { title: "Error Message", value: errorMessage, short: false, }, ] : []), { title: "Backup Type", value: "Complete Dokploy Instance", short: true, }, ...(backupSize ? [ { title: "Backup Size", value: backupSize, short: true, }, ] : []), { title: "Time", value: date.toLocaleString(), short: true, }, { title: "Status", value: type === "success" ? "Successful" : "Failed", short: true, }, ], }, ], }); } if (lark) { const limitCharacter = 800; const truncatedErrorMessage = errorMessage && errorMessage.length > limitCharacter ? errorMessage.substring(0, limitCharacter) : errorMessage; await sendLarkNotification(lark, { msg_type: "interactive", card: { schema: "2.0", config: { update_multi: true, style: { text_size: { normal_v2: { default: "normal", pc: "normal", mobile: "heading", }, }, }, }, header: { title: { tag: "plain_text", content: type === "success" ? "βœ… Dokploy Backup Successful" : "❌ Dokploy Backup Failed", }, subtitle: { tag: "plain_text", content: "", }, template: type === "success" ? "green" : "red", padding: "12px 12px 12px 12px", }, body: { direction: "vertical", padding: "12px 12px 12px 12px", elements: [ { tag: "column_set", columns: [ { tag: "column", width: "weighted", elements: [ { tag: "markdown", content: "**Backup Type:**\nComplete Dokploy Instance", text_align: "left", text_size: "normal_v2", }, { tag: "markdown", content: `**Status:**\n${type === "success" ? "Successful" : "Failed"}`, text_align: "left", text_size: "normal_v2", }, ], vertical_align: "top", weight: 1, }, { tag: "column", width: "weighted", elements: [ ...(backupSize ? [ { tag: "markdown", content: `**Backup Size:**\n${backupSize}`, text_align: "left", text_size: "normal_v2", }, ] : []), { tag: "markdown", content: `**Date:**\n${format(date, "PP pp")}`, text_align: "left", text_size: "normal_v2", }, ], vertical_align: "top", weight: 1, }, ], }, ...(type === "error" && truncatedErrorMessage ? [ { tag: "markdown", content: `**Error Message:**\n\`\`\`\n${truncatedErrorMessage}\n\`\`\``, text_align: "left", text_size: "normal_v2", }, ] : []), ], }, }, }); } if (mattermost) { const statusEmoji = type === "success" ? ":white_check_mark:" : ":x:"; const typeStatus = type === "success" ? "Successful" : "Failed"; await sendMattermostNotification(mattermost, { text: `${statusEmoji} **Dokploy Backup ${typeStatus}** **Backup Type:** Complete Dokploy Instance${backupSize ? `\n**Backup Size:** ${backupSize}` : ""} **Date:** ${date.toLocaleString()} **Status:** ${typeStatus}${type === "error" && errorMessage ? `\n\n**Error:**\n\`\`\`\n${errorMessage}\n\`\`\`` : ""}`, channel: mattermost.channel, username: mattermost.username || "Dokploy Bot", }); } if (custom) { await sendCustomNotification(custom, { title: `Dokploy Backup ${type === "success" ? "Successful" : "Failed"}`, message: `Dokploy instance backup ${type === "success" ? "completed successfully" : "failed"}`, backupType: "Complete Dokploy Instance", ...(backupSize ? { backupSize } : {}), ...(type === "error" && errorMessage ? { errorMessage } : {}), timestamp: date.toISOString(), date: date.toLocaleString(), status: type, type: "dokploy-backup", }); } if (pushover) { await sendPushoverNotification( pushover, `Dokploy Backup ${type === "success" ? "Successful" : "Failed"}`, `Backup Type: Complete Dokploy Instance${backupSize ? `\nBackup Size: ${backupSize}` : ""}\nDate: ${date.toLocaleString()}${type === "error" && errorMessage ? `\nError: ${errorMessage}` : ""}`, ); } if (teams) { await sendTeamsNotification(teams, { title: `${type === "success" ? "βœ…" : "❌"} Dokploy Backup ${type === "success" ? "Successful" : "Failed"}`, facts: [ { name: "Backup Type", value: "Complete Dokploy Instance" }, ...(backupSize ? [{ name: "Backup Size", value: backupSize }] : []), { name: "Date", value: format(date, "PP pp") }, { name: "Status", value: type === "success" ? "Successful" : "Failed", }, ...(type === "error" && errorMessage ? [{ name: "Error Message", value: errorMessage }] : []), ], }); } } catch (error) { console.error(error); } } };