@@ -1428,7 +1630,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
isLoadingGotify ||
isLoadingNtfy ||
isLoadingLark ||
- isLoadingCustom
+ isLoadingCustom ||
+ isLoadingPushover
}
variant="secondary"
type="button"
@@ -1497,6 +1700,22 @@ export const HandleNotifications = ({ notificationId }: Props) => {
endpoint: data.endpoint,
headers: headersRecord,
});
+ } else if (data.type === "pushover") {
+ if (
+ data.priority === 2 &&
+ (data.retry == null || data.expire == null)
+ ) {
+ throw new Error(
+ "Retry and expire are required for emergency priority (2)",
+ );
+ }
+ await testPushoverConnection({
+ userKey: data.userKey,
+ apiToken: data.apiToken,
+ priority: data.priority,
+ retry: data.priority === 2 ? data.retry : undefined,
+ expire: data.priority === 2 ? data.expire : undefined,
+ });
}
toast.success("Connection Success");
} catch (error) {
diff --git a/apps/dokploy/components/icons/notification-icons.tsx b/apps/dokploy/components/icons/notification-icons.tsx
index cc54327a8..87bb6c0ae 100644
--- a/apps/dokploy/components/icons/notification-icons.tsx
+++ b/apps/dokploy/components/icons/notification-icons.tsx
@@ -231,3 +231,29 @@ export const NtfyIcon = ({ className }: Props) => {
);
};
+
+export const PushoverIcon = ({ className }: Props) => {
+ return (
+
+ );
+};
diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json
index bd6c62c46..3f67c5d17 100644
--- a/apps/dokploy/drizzle/meta/_journal.json
+++ b/apps/dokploy/drizzle/meta/_journal.json
@@ -939,6 +939,13 @@
"when": 1766301478005,
"tag": "0133_striped_the_order",
"breakpoints": true
+ },
+ {
+ "idx": 134,
+ "version": "7",
+ "when": 1767871040249,
+ "tag": "0134_strong_hercules",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts
index 303168b9f..c22ce7aa5 100644
--- a/apps/dokploy/server/api/routers/notification.ts
+++ b/apps/dokploy/server/api/routers/notification.ts
@@ -5,6 +5,7 @@ import {
createGotifyNotification,
createLarkNotification,
createNtfyNotification,
+ createPushoverNotification,
createSlackNotification,
createTelegramNotification,
findNotificationById,
@@ -17,6 +18,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendServerThresholdNotifications,
sendSlackNotification,
sendTelegramNotification,
@@ -26,6 +28,7 @@ import {
updateGotifyNotification,
updateLarkNotification,
updateNtfyNotification,
+ updatePushoverNotification,
updateSlackNotification,
updateTelegramNotification,
} from "@dokploy/server";
@@ -46,6 +49,7 @@ import {
apiCreateGotify,
apiCreateLark,
apiCreateNtfy,
+ apiCreatePushover,
apiCreateSlack,
apiCreateTelegram,
apiFindOneNotification,
@@ -55,6 +59,7 @@ import {
apiTestGotifyConnection,
apiTestLarkConnection,
apiTestNtfyConnection,
+ apiTestPushoverConnection,
apiTestSlackConnection,
apiTestTelegramConnection,
apiUpdateCustom,
@@ -63,6 +68,7 @@ import {
apiUpdateGotify,
apiUpdateLark,
apiUpdateNtfy,
+ apiUpdatePushover,
apiUpdateSlack,
apiUpdateTelegram,
notifications,
@@ -342,6 +348,7 @@ export const notificationRouter = createTRPCRouter({
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
orderBy: desc(notifications.createdAt),
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
@@ -634,6 +641,62 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
+ createPushover: adminProcedure
+ .input(apiCreatePushover)
+ .mutation(async ({ input, ctx }) => {
+ try {
+ return await createPushoverNotification(
+ input,
+ ctx.session.activeOrganizationId,
+ );
+ } catch (error) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Error creating the notification",
+ cause: error,
+ });
+ }
+ }),
+ updatePushover: adminProcedure
+ .input(apiUpdatePushover)
+ .mutation(async ({ input, ctx }) => {
+ try {
+ const notification = await findNotificationById(input.notificationId);
+ if (
+ IS_CLOUD &&
+ notification.organizationId !== ctx.session.activeOrganizationId
+ ) {
+ throw new TRPCError({
+ code: "UNAUTHORIZED",
+ message: "You are not authorized to update this notification",
+ });
+ }
+ return await updatePushoverNotification({
+ ...input,
+ organizationId: ctx.session.activeOrganizationId,
+ });
+ } catch (error) {
+ throw error;
+ }
+ }),
+ testPushoverConnection: adminProcedure
+ .input(apiTestPushoverConnection)
+ .mutation(async ({ input }) => {
+ try {
+ await sendPushoverNotification(
+ input,
+ "Test Notification",
+ "Hi, From Dokploy 👋",
+ );
+ return true;
+ } catch (error) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Error testing the notification",
+ cause: error,
+ });
+ }
+ }),
getEmailProviders: adminProcedure.query(async ({ ctx }) => {
return await db.query.notifications.findMany({
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts
index 44dadac8f..3075459ba 100644
--- a/packages/server/src/db/schema/notification.ts
+++ b/packages/server/src/db/schema/notification.ts
@@ -19,6 +19,7 @@ export const notificationType = pgEnum("notificationType", [
"email",
"gotify",
"ntfy",
+ "pushover",
"custom",
"lark",
]);
@@ -64,6 +65,9 @@ export const notifications = pgTable("notification", {
larkId: text("larkId").references(() => lark.larkId, {
onDelete: "cascade",
}),
+ pushoverId: text("pushoverId").references(() => pushover.pushoverId, {
+ onDelete: "cascade",
+ }),
organizationId: text("organizationId")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
@@ -149,6 +153,18 @@ export const lark = pgTable("lark", {
webhookUrl: text("webhookUrl").notNull(),
});
+export const pushover = pgTable("pushover", {
+ pushoverId: text("pushoverId")
+ .notNull()
+ .primaryKey()
+ .$defaultFn(() => nanoid()),
+ userKey: text("userKey").notNull(),
+ apiToken: text("apiToken").notNull(),
+ priority: integer("priority").notNull().default(0),
+ retry: integer("retry"),
+ expire: integer("expire"),
+});
+
export const notificationsRelations = relations(notifications, ({ one }) => ({
slack: one(slack, {
fields: [notifications.slackId],
@@ -182,6 +198,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
fields: [notifications.larkId],
references: [lark.larkId],
}),
+ pushover: one(pushover, {
+ fields: [notifications.pushoverId],
+ references: [pushover.pushoverId],
+ }),
organization: one(organization, {
fields: [notifications.organizationId],
references: [organization.id],
@@ -439,6 +459,69 @@ export const apiTestLarkConnection = apiCreateLark.pick({
webhookUrl: true,
});
+export const apiCreatePushover = notificationsSchema
+ .pick({
+ appBuildError: true,
+ databaseBackup: true,
+ volumeBackup: true,
+ dokployRestart: true,
+ name: true,
+ appDeploy: true,
+ dockerCleanup: true,
+ serverThreshold: true,
+ })
+ .extend({
+ userKey: z.string().min(1),
+ apiToken: z.string().min(1),
+ priority: z.number().min(-2).max(2).default(0),
+ retry: z.number().min(30).nullish(),
+ expire: z.number().min(1).max(10800).nullish(),
+ })
+ .refine(
+ (data) =>
+ data.priority !== 2 || (data.retry != null && data.expire != null),
+ {
+ message: "Retry and expire are required for emergency priority (2)",
+ path: ["retry"],
+ },
+ );
+
+export const apiUpdatePushover = z.object({
+ notificationId: z.string().min(1),
+ pushoverId: z.string().min(1),
+ organizationId: z.string().optional(),
+ userKey: z.string().min(1).optional(),
+ apiToken: z.string().min(1).optional(),
+ priority: z.number().min(-2).max(2).optional(),
+ retry: z.number().min(30).nullish(),
+ expire: z.number().min(1).max(10800).nullish(),
+ appBuildError: z.boolean().optional(),
+ databaseBackup: z.boolean().optional(),
+ volumeBackup: z.boolean().optional(),
+ dokployRestart: z.boolean().optional(),
+ name: z.string().optional(),
+ appDeploy: z.boolean().optional(),
+ dockerCleanup: z.boolean().optional(),
+ serverThreshold: z.boolean().optional(),
+});
+
+export const apiTestPushoverConnection = z
+ .object({
+ userKey: z.string().min(1),
+ apiToken: z.string().min(1),
+ priority: z.number().min(-2).max(2),
+ retry: z.number().min(30).nullish(),
+ expire: z.number().min(1).max(10800).nullish(),
+ })
+ .refine(
+ (data) =>
+ data.priority !== 2 || (data.retry != null && data.expire != null),
+ {
+ message: "Retry and expire are required for emergency priority (2)",
+ path: ["retry"],
+ },
+ );
+
export const apiSendTest = notificationsSchema
.extend({
botToken: z.string(),
diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts
index e9a21c1f7..b2e15ed91 100644
--- a/packages/server/src/services/domain.ts
+++ b/packages/server/src/services/domain.ts
@@ -1,9 +1,9 @@
import dns from "node:dns";
import { promisify } from "node:util";
import { db } from "@dokploy/server/db";
+import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import { generateRandomDomain } from "@dokploy/server/templates";
import { manageDomain } from "@dokploy/server/utils/traefik/domain";
-import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { type apiCreateDomain, domains } from "../db/schema";
diff --git a/packages/server/src/services/notification.ts b/packages/server/src/services/notification.ts
index ca6b4ded6..453a61ca0 100644
--- a/packages/server/src/services/notification.ts
+++ b/packages/server/src/services/notification.ts
@@ -6,6 +6,7 @@ import {
type apiCreateGotify,
type apiCreateLark,
type apiCreateNtfy,
+ type apiCreatePushover,
type apiCreateSlack,
type apiCreateTelegram,
type apiUpdateCustom,
@@ -14,6 +15,7 @@ import {
type apiUpdateGotify,
type apiUpdateLark,
type apiUpdateNtfy,
+ type apiUpdatePushover,
type apiUpdateSlack,
type apiUpdateTelegram,
custom,
@@ -23,6 +25,7 @@ import {
lark,
notifications,
ntfy,
+ pushover,
slack,
telegram,
} from "@dokploy/server/db/schema";
@@ -694,6 +697,7 @@ export const findNotificationById = async (notificationId: string) => {
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
if (!notification) {
@@ -817,3 +821,99 @@ export const updateNotificationById = async (
return result[0];
};
+
+export const createPushoverNotification = async (
+ input: typeof apiCreatePushover._type,
+ organizationId: string,
+) => {
+ await db.transaction(async (tx) => {
+ const newPushover = await tx
+ .insert(pushover)
+ .values({
+ userKey: input.userKey,
+ apiToken: input.apiToken,
+ priority: input.priority,
+ retry: input.retry,
+ expire: input.expire,
+ })
+ .returning()
+ .then((value) => value[0]);
+
+ if (!newPushover) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Error input: Inserting pushover",
+ });
+ }
+
+ const newDestination = await tx
+ .insert(notifications)
+ .values({
+ pushoverId: newPushover.pushoverId,
+ name: input.name,
+ appDeploy: input.appDeploy,
+ appBuildError: input.appBuildError,
+ databaseBackup: input.databaseBackup,
+ volumeBackup: input.volumeBackup,
+ dokployRestart: input.dokployRestart,
+ dockerCleanup: input.dockerCleanup,
+ serverThreshold: input.serverThreshold,
+ notificationType: "pushover",
+ organizationId: organizationId,
+ })
+ .returning()
+ .then((value) => value[0]);
+
+ if (!newDestination) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Error input: Inserting notification",
+ });
+ }
+
+ return newDestination;
+ });
+};
+
+export const updatePushoverNotification = async (
+ input: typeof apiUpdatePushover._type,
+) => {
+ await db.transaction(async (tx) => {
+ const newDestination = await tx
+ .update(notifications)
+ .set({
+ name: input.name,
+ appDeploy: input.appDeploy,
+ appBuildError: input.appBuildError,
+ databaseBackup: input.databaseBackup,
+ volumeBackup: input.volumeBackup,
+ dokployRestart: input.dokployRestart,
+ dockerCleanup: input.dockerCleanup,
+ organizationId: input.organizationId,
+ serverThreshold: input.serverThreshold,
+ })
+ .where(eq(notifications.notificationId, input.notificationId))
+ .returning()
+ .then((value) => value[0]);
+
+ if (!newDestination) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Error Updating notification",
+ });
+ }
+
+ await tx
+ .update(pushover)
+ .set({
+ userKey: input.userKey,
+ apiToken: input.apiToken,
+ priority: input.priority,
+ retry: input.retry,
+ expire: input.expire,
+ })
+ .where(eq(pushover.pushoverId, input.pushoverId));
+
+ return newDestination;
+ });
+};
diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts
index f05fa8134..3c2497324 100644
--- a/packages/server/src/utils/notifications/build-error.ts
+++ b/packages/server/src/utils/notifications/build-error.ts
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -48,12 +49,22 @@ export const sendBuildErrorNotifications = async ({
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
- notification;
+ const {
+ email,
+ discord,
+ telegram,
+ slack,
+ gotify,
+ ntfy,
+ custom,
+ lark,
+ pushover,
+ } = notification;
try {
if (email) {
const template = await renderAsync(
@@ -349,6 +360,14 @@ export const sendBuildErrorNotifications = async ({
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ "Build Failed",
+ `Project: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${date.toLocaleString()}\nError: ${errorMessage}`,
+ );
+ }
} catch (error) {
console.log(error);
}
diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts
index e120d107b..d1bc04796 100644
--- a/packages/server/src/utils/notifications/build-success.ts
+++ b/packages/server/src/utils/notifications/build-success.ts
@@ -12,6 +12,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -51,12 +52,22 @@ export const sendBuildSuccessNotifications = async ({
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
- notification;
+ const {
+ email,
+ discord,
+ telegram,
+ slack,
+ gotify,
+ ntfy,
+ custom,
+ lark,
+ pushover,
+ } = notification;
try {
if (email) {
const template = await renderAsync(
@@ -363,6 +374,14 @@ export const sendBuildSuccessNotifications = async ({
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ "Build Success",
+ `Project: ${projectName}\nApplication: ${applicationName}\nEnvironment: ${environmentName}\nType: ${applicationType}\nDate: ${date.toLocaleString()}`,
+ );
+ }
} catch (error) {
console.log(error);
}
diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts
index e0754b715..1b2b49bf1 100644
--- a/packages/server/src/utils/notifications/database-backup.ts
+++ b/packages/server/src/utils/notifications/database-backup.ts
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -48,12 +49,22 @@ export const sendDatabaseBackupNotifications = async ({
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
- notification;
+ const {
+ email,
+ discord,
+ telegram,
+ slack,
+ gotify,
+ ntfy,
+ custom,
+ lark,
+ pushover,
+ } = notification;
try {
if (email) {
const template = await renderAsync(
@@ -377,6 +388,14 @@ export const sendDatabaseBackupNotifications = async ({
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ `Database Backup ${type === "success" ? "Successful" : "Failed"}`,
+ `Project: ${projectName}\nApplication: ${applicationName}\nDatabase: ${databaseType}\nDatabase Name: ${databaseName}\nDate: ${date.toLocaleString()}${type === "error" && errorMessage ? `\nError: ${errorMessage}` : ""}`,
+ );
+ }
} catch (error) {
console.log(error);
}
diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts
index 061f892ff..834ff489c 100644
--- a/packages/server/src/utils/notifications/docker-cleanup.ts
+++ b/packages/server/src/utils/notifications/docker-cleanup.ts
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -35,12 +36,22 @@ export const sendDockerCleanupNotifications = async (
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
- notification;
+ const {
+ email,
+ discord,
+ telegram,
+ slack,
+ gotify,
+ ntfy,
+ custom,
+ lark,
+ pushover,
+ } = notification;
try {
if (email) {
const template = await renderAsync(
@@ -230,6 +241,14 @@ export const sendDockerCleanupNotifications = async (
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ "Docker Cleanup",
+ `Date: ${date.toLocaleString()}\nMessage: ${message}`,
+ );
+ }
} catch (error) {
console.log(error);
}
diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts
index 095ca4a6a..f93f31ac5 100644
--- a/packages/server/src/utils/notifications/dokploy-restart.ts
+++ b/packages/server/src/utils/notifications/dokploy-restart.ts
@@ -11,6 +11,7 @@ import {
sendGotifyNotification,
sendLarkNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -29,12 +30,22 @@ export const sendDokployRestartNotifications = async () => {
ntfy: true,
custom: true,
lark: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
- notification;
+ const {
+ email,
+ discord,
+ telegram,
+ slack,
+ gotify,
+ ntfy,
+ custom,
+ lark,
+ pushover,
+ } = notification;
try {
if (email) {
@@ -219,6 +230,14 @@ export const sendDokployRestartNotifications = async () => {
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ "Dokploy Server Restarted",
+ `Date: ${date.toLocaleString()}`,
+ );
+ }
} catch (error) {
console.log(error);
}
diff --git a/packages/server/src/utils/notifications/server-threshold.ts b/packages/server/src/utils/notifications/server-threshold.ts
index cb3484c55..bafe95cfa 100644
--- a/packages/server/src/utils/notifications/server-threshold.ts
+++ b/packages/server/src/utils/notifications/server-threshold.ts
@@ -5,6 +5,7 @@ import {
sendCustomNotification,
sendDiscordNotification,
sendLarkNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -38,6 +39,7 @@ export const sendServerThresholdNotifications = async (
slack: true,
custom: true,
lark: true,
+ pushover: true,
},
});
@@ -45,7 +47,7 @@ export const sendServerThresholdNotifications = async (
const typeColor = 0xff0000; // Rojo para indicar alerta
for (const notification of notificationList) {
- const { discord, telegram, slack, custom, lark } = notification;
+ const { discord, telegram, slack, custom, lark, pushover } = notification;
if (discord) {
const decorate = (decoration: string, text: string) =>
@@ -266,5 +268,13 @@ export const sendServerThresholdNotifications = async (
},
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ `Server ${payload.Type} Alert`,
+ `Server: ${payload.ServerName}\nType: ${payload.Type}\nCurrent: ${payload.Value.toFixed(2)}%\nThreshold: ${payload.Threshold.toFixed(2)}%\nMessage: ${payload.Message}\nTime: ${date.toLocaleString()}`,
+ );
+ }
}
};
diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts
index 02a226f23..170b90e8a 100644
--- a/packages/server/src/utils/notifications/utils.ts
+++ b/packages/server/src/utils/notifications/utils.ts
@@ -5,6 +5,7 @@ import type {
gotify,
lark,
ntfy,
+ pushover,
slack,
telegram,
} from "@dokploy/server/db/schema";
@@ -223,3 +224,33 @@ export const sendLarkNotification = async (
console.log(err);
}
};
+
+export const sendPushoverNotification = async (
+ connection: typeof pushover.$inferInsert,
+ title: string,
+ message: string,
+) => {
+ const formData = new URLSearchParams();
+ formData.append("token", connection.apiToken);
+ formData.append("user", connection.userKey);
+ formData.append("title", title);
+ formData.append("message", message);
+ formData.append("priority", connection.priority?.toString() || "0");
+
+ // For emergency priority (2), retry and expire are required
+ if (connection.priority === 2) {
+ formData.append("retry", connection.retry?.toString() || "30");
+ formData.append("expire", connection.expire?.toString() || "3600");
+ }
+
+ const response = await fetch("https://api.pushover.net/1/messages.json", {
+ method: "POST",
+ body: formData,
+ });
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to send Pushover notification: ${response.statusText}`,
+ );
+ }
+};
diff --git a/packages/server/src/utils/notifications/volume-backup.ts b/packages/server/src/utils/notifications/volume-backup.ts
index bec85f399..44e2b5fb3 100644
--- a/packages/server/src/utils/notifications/volume-backup.ts
+++ b/packages/server/src/utils/notifications/volume-backup.ts
@@ -9,6 +9,7 @@ import {
sendEmailNotification,
sendGotifyNotification,
sendNtfyNotification,
+ sendPushoverNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
@@ -53,11 +54,13 @@ export const sendVolumeBackupNotifications = async ({
slack: true,
gotify: true,
ntfy: true,
+ pushover: true,
},
});
for (const notification of notificationList) {
- const { email, discord, telegram, slack, gotify, ntfy } = notification;
+ const { email, discord, telegram, slack, gotify, ntfy, pushover } =
+ notification;
if (email) {
const subject = `Volume Backup ${type === "success" ? "Successful" : "Failed"} - ${applicationName}`;
@@ -270,5 +273,13 @@ export const sendVolumeBackupNotifications = async ({
],
});
}
+
+ if (pushover) {
+ await sendPushoverNotification(
+ pushover,
+ `Volume Backup ${type === "success" ? "Successful" : "Failed"}`,
+ `Project: ${projectName}\nApplication: ${applicationName}\nVolume: ${volumeName}\nService Type: ${serviceType}${backupSize ? `\nBackup Size: ${backupSize}` : ""}\nDate: ${date.toLocaleString()}${type === "error" && errorMessage ? `\nError: ${errorMessage}` : ""}`,
+ );
+ }
}
};