mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-15 16:35:17 +00:00
feat[notifications]: Add dokployBackup notification type support
This commit adds support for the dokployBackup notification type across all relevant services and schemas.
This commit is contained in:
@@ -44,6 +44,7 @@ const notificationBaseSchema = z.object({
|
|||||||
appDeploy: z.boolean().default(false),
|
appDeploy: z.boolean().default(false),
|
||||||
appBuildError: z.boolean().default(false),
|
appBuildError: z.boolean().default(false),
|
||||||
databaseBackup: z.boolean().default(false),
|
databaseBackup: z.boolean().default(false),
|
||||||
|
dokployBackup: z.boolean().default(false),
|
||||||
dokployRestart: z.boolean().default(false),
|
dokployRestart: z.boolean().default(false),
|
||||||
dockerCleanup: z.boolean().default(false),
|
dockerCleanup: z.boolean().default(false),
|
||||||
serverThreshold: z.boolean().default(false),
|
serverThreshold: z.boolean().default(false),
|
||||||
@@ -231,6 +232,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
dockerCleanup: notification.dockerCleanup,
|
dockerCleanup: notification.dockerCleanup,
|
||||||
webhookUrl: notification.slack?.webhookUrl,
|
webhookUrl: notification.slack?.webhookUrl,
|
||||||
channel: notification.slack?.channel || "",
|
channel: notification.slack?.channel || "",
|
||||||
@@ -244,6 +246,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
botToken: notification.telegram?.botToken,
|
botToken: notification.telegram?.botToken,
|
||||||
messageThreadId: notification.telegram?.messageThreadId || "",
|
messageThreadId: notification.telegram?.messageThreadId || "",
|
||||||
chatId: notification.telegram?.chatId,
|
chatId: notification.telegram?.chatId,
|
||||||
@@ -258,6 +261,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
type: notification.notificationType,
|
type: notification.notificationType,
|
||||||
webhookUrl: notification.discord?.webhookUrl,
|
webhookUrl: notification.discord?.webhookUrl,
|
||||||
decoration: notification.discord?.decoration || undefined,
|
decoration: notification.discord?.decoration || undefined,
|
||||||
@@ -271,6 +275,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
type: notification.notificationType,
|
type: notification.notificationType,
|
||||||
smtpServer: notification.email?.smtpServer,
|
smtpServer: notification.email?.smtpServer,
|
||||||
smtpPort: notification.email?.smtpPort,
|
smtpPort: notification.email?.smtpPort,
|
||||||
@@ -288,6 +293,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
type: notification.notificationType,
|
type: notification.notificationType,
|
||||||
appToken: notification.gotify?.appToken,
|
appToken: notification.gotify?.appToken,
|
||||||
decoration: notification.gotify?.decoration || undefined,
|
decoration: notification.gotify?.decoration || undefined,
|
||||||
@@ -302,6 +308,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
type: notification.notificationType,
|
type: notification.notificationType,
|
||||||
accessToken: notification.ntfy?.accessToken,
|
accessToken: notification.ntfy?.accessToken,
|
||||||
topic: notification.ntfy?.topic,
|
topic: notification.ntfy?.topic,
|
||||||
@@ -317,6 +324,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: notification.appDeploy,
|
appDeploy: notification.appDeploy,
|
||||||
dokployRestart: notification.dokployRestart,
|
dokployRestart: notification.dokployRestart,
|
||||||
databaseBackup: notification.databaseBackup,
|
databaseBackup: notification.databaseBackup,
|
||||||
|
dokployBackup: notification.dokployBackup,
|
||||||
type: notification.notificationType,
|
type: notification.notificationType,
|
||||||
webhookUrl: notification.lark?.webhookUrl,
|
webhookUrl: notification.lark?.webhookUrl,
|
||||||
name: notification.name,
|
name: notification.name,
|
||||||
@@ -345,6 +353,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy,
|
appDeploy,
|
||||||
dokployRestart,
|
dokployRestart,
|
||||||
databaseBackup,
|
databaseBackup,
|
||||||
|
dokployBackup,
|
||||||
dockerCleanup,
|
dockerCleanup,
|
||||||
serverThreshold,
|
serverThreshold,
|
||||||
} = data;
|
} = data;
|
||||||
@@ -355,6 +364,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
webhookUrl: data.webhookUrl,
|
webhookUrl: data.webhookUrl,
|
||||||
channel: data.channel,
|
channel: data.channel,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -369,6 +379,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
botToken: data.botToken,
|
botToken: data.botToken,
|
||||||
messageThreadId: data.messageThreadId || "",
|
messageThreadId: data.messageThreadId || "",
|
||||||
chatId: data.chatId,
|
chatId: data.chatId,
|
||||||
@@ -384,6 +395,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
webhookUrl: data.webhookUrl,
|
webhookUrl: data.webhookUrl,
|
||||||
decoration: data.decoration,
|
decoration: data.decoration,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -398,6 +410,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
smtpServer: data.smtpServer,
|
smtpServer: data.smtpServer,
|
||||||
smtpPort: data.smtpPort,
|
smtpPort: data.smtpPort,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
@@ -416,6 +429,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
serverUrl: data.serverUrl,
|
serverUrl: data.serverUrl,
|
||||||
appToken: data.appToken,
|
appToken: data.appToken,
|
||||||
priority: data.priority,
|
priority: data.priority,
|
||||||
@@ -431,6 +445,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
serverUrl: data.serverUrl,
|
serverUrl: data.serverUrl,
|
||||||
accessToken: data.accessToken,
|
accessToken: data.accessToken,
|
||||||
topic: data.topic,
|
topic: data.topic,
|
||||||
@@ -446,6 +461,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
appDeploy: appDeploy,
|
appDeploy: appDeploy,
|
||||||
dokployRestart: dokployRestart,
|
dokployRestart: dokployRestart,
|
||||||
databaseBackup: databaseBackup,
|
databaseBackup: databaseBackup,
|
||||||
|
dokployBackup: dokployBackup,
|
||||||
webhookUrl: data.webhookUrl,
|
webhookUrl: data.webhookUrl,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
dockerCleanup: dockerCleanup,
|
dockerCleanup: dockerCleanup,
|
||||||
@@ -1130,6 +1146,27 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="dokployBackup"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm gap-2">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>Dokploy Backup</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Trigger the action when a dokploy backup is created.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="dockerCleanup"
|
name="dockerCleanup"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "notification" ADD COLUMN "dokployBackup" boolean DEFAULT false NOT NULL;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -834,6 +834,13 @@
|
|||||||
"when": 1761415824484,
|
"when": 1761415824484,
|
||||||
"tag": "0118_loose_anita_blake",
|
"tag": "0118_loose_anita_blake",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 119,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1761595174595,
|
||||||
|
"tag": "0119_wakeful_luke_cage",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ export const notifications = pgTable("notification", {
|
|||||||
appBuildError: boolean("appBuildError").notNull().default(false),
|
appBuildError: boolean("appBuildError").notNull().default(false),
|
||||||
databaseBackup: boolean("databaseBackup").notNull().default(false),
|
databaseBackup: boolean("databaseBackup").notNull().default(false),
|
||||||
dokployRestart: boolean("dokployRestart").notNull().default(false),
|
dokployRestart: boolean("dokployRestart").notNull().default(false),
|
||||||
|
dokployBackup: boolean("dokployBackup").notNull().default(false),
|
||||||
dockerCleanup: boolean("dockerCleanup").notNull().default(false),
|
dockerCleanup: boolean("dockerCleanup").notNull().default(false),
|
||||||
serverThreshold: boolean("serverThreshold").notNull().default(false),
|
serverThreshold: boolean("serverThreshold").notNull().default(false),
|
||||||
notificationType: notificationType("notificationType").notNull(),
|
notificationType: notificationType("notificationType").notNull(),
|
||||||
@@ -169,6 +170,7 @@ export const apiCreateSlack = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -196,6 +198,7 @@ export const apiCreateTelegram = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -225,6 +228,7 @@ export const apiCreateDiscord = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -255,6 +259,7 @@ export const apiCreateEmail = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -290,6 +295,7 @@ export const apiCreateGotify = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -323,6 +329,7 @@ export const apiCreateNtfy = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
@@ -359,6 +366,7 @@ export const apiCreateLark = notificationsSchema
|
|||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
databaseBackup: true,
|
databaseBackup: true,
|
||||||
|
dokployBackup: true,
|
||||||
dokployRestart: true,
|
dokployRestart: true,
|
||||||
name: true,
|
name: true,
|
||||||
appDeploy: true,
|
appDeploy: true,
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Container,
|
||||||
|
Head,
|
||||||
|
Heading,
|
||||||
|
Html,
|
||||||
|
Img,
|
||||||
|
Preview,
|
||||||
|
Section,
|
||||||
|
Tailwind,
|
||||||
|
Text,
|
||||||
|
} from "@react-email/components";
|
||||||
|
|
||||||
|
export type TemplateProps = {
|
||||||
|
type: "error" | "success";
|
||||||
|
errorMessage?: string;
|
||||||
|
date: string;
|
||||||
|
backupSize?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DokployBackupEmail = ({
|
||||||
|
type = "success",
|
||||||
|
errorMessage,
|
||||||
|
date = "2023-05-01T00:00:00.000Z",
|
||||||
|
backupSize,
|
||||||
|
}: TemplateProps) => {
|
||||||
|
const previewText = `Dokploy instance backup was ${type === "success" ? "successful ✅" : "failed ❌"}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Preview>{previewText}</Preview>
|
||||||
|
<Tailwind
|
||||||
|
config={{
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
brand: "#007291",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Head />
|
||||||
|
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||||
|
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||||
|
<Section className="mt-[32px]">
|
||||||
|
<Img
|
||||||
|
src={
|
||||||
|
"https://raw.githubusercontent.com/Dokploy/dokploy/refs/heads/canary/apps/dokploy/logo.png"
|
||||||
|
}
|
||||||
|
width="100"
|
||||||
|
height="50"
|
||||||
|
alt="Dokploy"
|
||||||
|
className="my-0 mx-auto"
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||||
|
Dokploy Instance Backup
|
||||||
|
</Heading>
|
||||||
|
<Text className="text-black text-[14px] leading-[24px]">
|
||||||
|
Hello,
|
||||||
|
</Text>
|
||||||
|
<Text className="text-black text-[14px] leading-[24px]">
|
||||||
|
Your Dokploy instance backup was{" "}
|
||||||
|
{type === "success"
|
||||||
|
? "successful ✅"
|
||||||
|
: "failed. Please check the error message below. ❌"}
|
||||||
|
.
|
||||||
|
</Text>
|
||||||
|
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||||
|
<Text className="!leading-3 font-bold">Details: </Text>
|
||||||
|
<Text className="!leading-3">
|
||||||
|
Backup Type: <strong>Complete Dokploy Instance</strong>
|
||||||
|
</Text>
|
||||||
|
<Text className="!leading-3">
|
||||||
|
Content: <strong>/etc/dokploy + PostgreSQL Database</strong>
|
||||||
|
</Text>
|
||||||
|
{backupSize && (
|
||||||
|
<Text className="!leading-3">
|
||||||
|
Backup Size: <strong>{backupSize}</strong>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Text className="!leading-3">
|
||||||
|
Date: <strong>{date}</strong>
|
||||||
|
</Text>
|
||||||
|
<Text className="!leading-3">
|
||||||
|
Status: <strong>{type === "success" ? "Successful" : "Failed"}</strong>
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
{type === "error" && errorMessage ? (
|
||||||
|
<Section className="flex text-black text-[14px] mt-4 leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||||
|
<Text className="!leading-3 font-bold">Reason: </Text>
|
||||||
|
<Text className="text-[12px] leading-[24px]">
|
||||||
|
{errorMessage || "Error message not provided"}
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
) : null}
|
||||||
|
</Container>
|
||||||
|
</Body>
|
||||||
|
</Tailwind>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DokployBackupEmail;
|
||||||
@@ -57,6 +57,7 @@ export const createSlackNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "slack",
|
notificationType: "slack",
|
||||||
@@ -88,6 +89,7 @@ export const updateSlackNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -148,6 +150,7 @@ export const createTelegramNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "telegram",
|
notificationType: "telegram",
|
||||||
@@ -179,6 +182,7 @@ export const updateTelegramNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -239,6 +243,7 @@ export const createDiscordNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "discord",
|
notificationType: "discord",
|
||||||
@@ -270,6 +275,7 @@ export const updateDiscordNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -333,6 +339,7 @@ export const createEmailNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "email",
|
notificationType: "email",
|
||||||
@@ -364,6 +371,7 @@ export const updateEmailNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -429,6 +437,7 @@ export const createGotifyNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "gotify",
|
notificationType: "gotify",
|
||||||
@@ -459,6 +468,7 @@ export const updateGotifyNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -519,6 +529,7 @@ export const createNtfyNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "ntfy",
|
notificationType: "ntfy",
|
||||||
@@ -549,6 +560,7 @@ export const updateNtfyNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -637,6 +649,7 @@ export const createLarkNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "lark",
|
notificationType: "lark",
|
||||||
@@ -668,6 +681,7 @@ export const updateLarkNotification = async (
|
|||||||
appDeploy: input.appDeploy,
|
appDeploy: input.appDeploy,
|
||||||
appBuildError: input.appBuildError,
|
appBuildError: input.appBuildError,
|
||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployBackup: input.dokployBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import { mkdtemp, rm } from "node:fs/promises";
|
import { mkdtemp, rm, stat } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
||||||
@@ -9,9 +9,19 @@ import {
|
|||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
import { findDestinationById } from "@dokploy/server/services/destination";
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
|
import { sendDokployBackupNotifications } from "../notifications/dokploy-backup";
|
||||||
import { execAsync } from "../process/execAsync";
|
import { execAsync } from "../process/execAsync";
|
||||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||||
|
|
||||||
|
function formatBytes(bytes?: number) {
|
||||||
|
if (bytes === undefined) return "Unknown size";
|
||||||
|
if (bytes === 0) return "0 B";
|
||||||
|
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||||
|
const value = bytes / Math.pow(1024, i);
|
||||||
|
return `${value.toFixed(2)} ${sizes[i]} (${bytes} bytes)`;
|
||||||
|
}
|
||||||
|
|
||||||
export const runWebServerBackup = async (backup: BackupSchedule) => {
|
export const runWebServerBackup = async (backup: BackupSchedule) => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +33,7 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
|||||||
description: "Web Server Backup",
|
description: "Web Server Backup",
|
||||||
});
|
});
|
||||||
const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
|
const writeStream = createWriteStream(deployment.logPath, { flags: "a" });
|
||||||
|
let computedBackupSize: number | undefined;
|
||||||
try {
|
try {
|
||||||
const destination = await findDestinationById(backup.destinationId);
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const rcloneFlags = getS3Credentials(destination);
|
const rcloneFlags = getS3Credentials(destination);
|
||||||
@@ -79,11 +89,24 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
|||||||
|
|
||||||
writeStream.write("Zipped database and filesystem\n");
|
writeStream.write("Zipped database and filesystem\n");
|
||||||
|
|
||||||
const uploadCommand = `rclone copyto ${rcloneFlags.join(" ")} "${tempDir}/${backupFileName}" "${s3Path}"`;
|
const zipPath = join(tempDir, backupFileName);
|
||||||
|
try {
|
||||||
|
const { size } = await stat(zipPath);
|
||||||
|
computedBackupSize = size;
|
||||||
|
writeStream.write(`Backup size: ${size} bytes\n`);
|
||||||
|
} catch {
|
||||||
|
// If stat fails, keep undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadCommand = `rclone copyto ${rcloneFlags.join(" ")} "${zipPath}" "${s3Path}"`;
|
||||||
writeStream.write("Running command to upload backup to S3\n");
|
writeStream.write("Running command to upload backup to S3\n");
|
||||||
await execAsync(uploadCommand);
|
await execAsync(uploadCommand);
|
||||||
writeStream.write("Uploaded backup to S3 ✅\n");
|
writeStream.write("Uploaded backup to S3 ✅\n");
|
||||||
writeStream.end();
|
writeStream.end();
|
||||||
|
await sendDokployBackupNotifications({
|
||||||
|
type: "success",
|
||||||
|
backupSize: formatBytes(computedBackupSize),
|
||||||
|
});
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -100,6 +123,12 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
|||||||
error instanceof Error ? error.message : "Unknown error\n",
|
error instanceof Error ? error.message : "Unknown error\n",
|
||||||
);
|
);
|
||||||
writeStream.end();
|
writeStream.end();
|
||||||
|
await sendDokployBackupNotifications({
|
||||||
|
type: "error",
|
||||||
|
// @ts-ignore
|
||||||
|
errorMessage: error?.message || "Error message not provided",
|
||||||
|
backupSize: formatBytes(computedBackupSize),
|
||||||
|
});
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,322 @@
|
|||||||
|
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 {
|
||||||
|
sendDiscordNotification,
|
||||||
|
sendEmailNotification,
|
||||||
|
sendLarkNotification,
|
||||||
|
sendGotifyNotification,
|
||||||
|
sendNtfyNotification,
|
||||||
|
sendSlackNotification,
|
||||||
|
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,
|
||||||
|
gotify: true,
|
||||||
|
ntfy: true,
|
||||||
|
lark: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const notification of notificationList) {
|
||||||
|
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
||||||
|
notification;
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
const template = await renderAsync(
|
||||||
|
DokployBackupEmail({
|
||||||
|
type,
|
||||||
|
errorMessage,
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
backupSize,
|
||||||
|
}),
|
||||||
|
).catch();
|
||||||
|
await sendEmailNotification(
|
||||||
|
email,
|
||||||
|
"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: `<t:${unixDate}:D>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: decorate("`⌚`", "Time"),
|
||||||
|
value: `<t:${unixDate}:t>`,
|
||||||
|
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\n<b>Error:</b>\n<pre>${errorMessage}</pre>`
|
||||||
|
: "";
|
||||||
|
const sizeInfo = backupSize ? `\n<b>Backup Size:</b> ${backupSize}` : "";
|
||||||
|
|
||||||
|
const messageText = `<b>${statusEmoji} Dokploy Backup ${typeStatus}</b>\n\n<b>Backup Type:</b> Complete Dokploy Instance${sizeInfo}\n<b>Date:</b> ${format(date, "PP")}\n<b>Time:</b> ${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",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user