mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(notifications): implement gotify provider
This commit is contained in:
@@ -10,6 +10,7 @@ export const notificationType = pgEnum("notificationType", [
|
||||
"telegram",
|
||||
"discord",
|
||||
"email",
|
||||
"gotify",
|
||||
]);
|
||||
|
||||
export const notifications = pgTable("notification", {
|
||||
@@ -39,6 +40,9 @@ export const notifications = pgTable("notification", {
|
||||
emailId: text("emailId").references(() => email.emailId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
gotifyId: text("gotifyId").references(() => gotify.gotifyId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
@@ -84,6 +88,17 @@ export const email = pgTable("email", {
|
||||
toAddresses: text("toAddress").array().notNull(),
|
||||
});
|
||||
|
||||
export const gotify = pgTable("gotify", {
|
||||
gotifyId: text("gotifyId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
serverUrl: text("serverUrl").notNull(),
|
||||
appToken: text("appToken").notNull(),
|
||||
priority: integer("priority").notNull().default(5),
|
||||
decoration: boolean("decoration"),
|
||||
});
|
||||
|
||||
export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
slack: one(slack, {
|
||||
fields: [notifications.slackId],
|
||||
@@ -101,6 +116,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
fields: [notifications.emailId],
|
||||
references: [email.emailId],
|
||||
}),
|
||||
gotify: one(gotify, {
|
||||
fields: [notifications.gotifyId],
|
||||
references: [gotify.gotifyId],
|
||||
}),
|
||||
admin: one(admins, {
|
||||
fields: [notifications.adminId],
|
||||
references: [admins.adminId],
|
||||
@@ -224,6 +243,37 @@ export const apiTestEmailConnection = apiCreateEmail.pick({
|
||||
fromAddress: true,
|
||||
});
|
||||
|
||||
export const apiCreateGotify = notificationsSchema
|
||||
.pick({
|
||||
appBuildError: true,
|
||||
databaseBackup: true,
|
||||
dokployRestart: true,
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
})
|
||||
.extend({
|
||||
serverUrl: z.string().min(1),
|
||||
appToken: z.string().min(1),
|
||||
priority: z.number().min(1),
|
||||
decoration: z.boolean(),
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiUpdateGotify = apiCreateGotify.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
gotifyId: z.string().min(1),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestGotifyConnection = apiCreateGotify.pick({
|
||||
serverUrl: true,
|
||||
appToken: true,
|
||||
priority: true,
|
||||
}).extend({
|
||||
decoration: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const apiFindOneNotification = notificationsSchema
|
||||
.pick({
|
||||
notificationId: true,
|
||||
@@ -242,5 +292,8 @@ export const apiSendTest = notificationsSchema
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
toAddresses: z.array(z.string()),
|
||||
serverUrl: z.string(),
|
||||
appToken: z.string(),
|
||||
priority: z.number(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
@@ -2,14 +2,17 @@ import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
type apiCreateDiscord,
|
||||
type apiCreateEmail,
|
||||
type apiCreateGotify,
|
||||
type apiCreateSlack,
|
||||
type apiCreateTelegram,
|
||||
type apiUpdateDiscord,
|
||||
type apiUpdateEmail,
|
||||
type apiUpdateGotify,
|
||||
type apiUpdateSlack,
|
||||
type apiUpdateTelegram,
|
||||
discord,
|
||||
email,
|
||||
gotify,
|
||||
notifications,
|
||||
slack,
|
||||
telegram,
|
||||
@@ -379,6 +382,96 @@ export const updateEmailNotification = async (
|
||||
});
|
||||
};
|
||||
|
||||
export const createGotifyNotification = async (
|
||||
input: typeof apiCreateGotify._type,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newGotify = await tx
|
||||
.insert(gotify)
|
||||
.values({
|
||||
serverUrl: input.serverUrl,
|
||||
appToken: input.appToken,
|
||||
priority: input.priority,
|
||||
decoration: input.decoration,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newGotify) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting gotify",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
gotifyId: newGotify.gotifyId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "gotify",
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDestination) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting notification",
|
||||
});
|
||||
}
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateGotifyNotification = async (
|
||||
input: typeof apiUpdateGotify._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,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.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(gotify)
|
||||
.set({
|
||||
serverUrl: input.serverUrl,
|
||||
appToken: input.appToken,
|
||||
priority: input.priority,
|
||||
decoration: input.decoration,
|
||||
})
|
||||
.where(eq(gotify.gotifyId, input.gotifyId));
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const findNotificationById = async (notificationId: string) => {
|
||||
const notification = await db.query.notifications.findFirst({
|
||||
where: eq(notifications.notificationId, notificationId),
|
||||
@@ -387,6 +480,7 @@ export const findNotificationById = async (notificationId: string) => {
|
||||
telegram: true,
|
||||
discord: true,
|
||||
email: true,
|
||||
gotify: true,
|
||||
},
|
||||
});
|
||||
if (!notification) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
email,
|
||||
slack,
|
||||
telegram,
|
||||
gotify,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
@@ -87,3 +88,31 @@ export const sendSlackNotification = async (
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendGotifyNotification = async (
|
||||
connection: typeof gotify.$inferInsert,
|
||||
title: string,
|
||||
message: string,
|
||||
) => {
|
||||
const response = await fetch(`${connection.serverUrl}/message`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Gotify-Key": connection.appToken,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: title,
|
||||
message: message,
|
||||
priority: connection.priority,
|
||||
extras: {
|
||||
"client::display": {
|
||||
"contentType": "text/plain"
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to send Gotify notification: ${response.statusText}`);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user