Merge branch 'canary' into feat/add-mattermost-notification-provider

Resolves merge conflicts between mattermost notification provider (this PR)
and new canary features (resend, teams, SSO, patches, etc).

All notification providers are now included:
- slack, telegram, discord, email, gotify, ntfy
- mattermost (this PR)
- resend, pushover, custom, lark, teams (from canary)
This commit is contained in:
Hootan
2026-02-28 00:49:31 +01:00
463 changed files with 99829 additions and 8878 deletions

View File

@@ -8,7 +8,9 @@ import {
type apiCreateMattermost,
type apiCreateNtfy,
type apiCreatePushover,
type apiCreateResend,
type apiCreateSlack,
type apiCreateTeams,
type apiCreateTelegram,
type apiUpdateCustom,
type apiUpdateDiscord,
@@ -18,7 +20,9 @@ import {
type apiUpdateMattermost,
type apiUpdateNtfy,
type apiUpdatePushover,
type apiUpdateResend,
type apiUpdateSlack,
type apiUpdateTeams,
type apiUpdateTelegram,
custom,
discord,
@@ -29,16 +33,19 @@ import {
notifications,
ntfy,
pushover,
resend,
slack,
teams,
telegram,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import type { z } from "zod";
export type Notification = typeof notifications.$inferSelect;
export const createSlackNotification = async (
input: typeof apiCreateSlack._type,
input: z.infer<typeof apiCreateSlack>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -88,7 +95,7 @@ export const createSlackNotification = async (
};
export const updateSlackNotification = async (
input: typeof apiUpdateSlack._type,
input: z.infer<typeof apiUpdateSlack>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -130,7 +137,7 @@ export const updateSlackNotification = async (
};
export const createTelegramNotification = async (
input: typeof apiCreateTelegram._type,
input: z.infer<typeof apiCreateTelegram>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -181,7 +188,7 @@ export const createTelegramNotification = async (
};
export const updateTelegramNotification = async (
input: typeof apiUpdateTelegram._type,
input: z.infer<typeof apiUpdateTelegram>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -224,7 +231,7 @@ export const updateTelegramNotification = async (
};
export const createDiscordNotification = async (
input: typeof apiCreateDiscord._type,
input: z.infer<typeof apiCreateDiscord>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -274,7 +281,7 @@ export const createDiscordNotification = async (
};
export const updateDiscordNotification = async (
input: typeof apiUpdateDiscord._type,
input: z.infer<typeof apiUpdateDiscord>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -316,7 +323,7 @@ export const updateDiscordNotification = async (
};
export const createEmailNotification = async (
input: typeof apiCreateEmail._type,
input: z.infer<typeof apiCreateEmail>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -370,7 +377,7 @@ export const createEmailNotification = async (
};
export const updateEmailNotification = async (
input: typeof apiUpdateEmail._type,
input: z.infer<typeof apiUpdateEmail>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -415,8 +422,102 @@ export const updateEmailNotification = async (
});
};
export const createResendNotification = async (
input: z.infer<typeof apiCreateResend>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newResend = await tx
.insert(resend)
.values({
apiKey: input.apiKey,
fromAddress: input.fromAddress,
toAddresses: input.toAddresses,
})
.returning()
.then((value) => value[0]);
if (!newResend) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting resend",
});
}
const newDestination = await tx
.insert(notifications)
.values({
resendId: newResend.resendId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
volumeBackup: input.volumeBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "resend",
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
if (!newDestination) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting notification",
});
}
return newDestination;
});
};
export const updateResendNotification = async (
input: z.infer<typeof apiUpdateResend>,
) => {
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(resend)
.set({
apiKey: input.apiKey,
fromAddress: input.fromAddress,
toAddresses: input.toAddresses,
})
.where(eq(resend.resendId, input.resendId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
export const createGotifyNotification = async (
input: typeof apiCreateGotify._type,
input: z.infer<typeof apiCreateGotify>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -467,7 +568,7 @@ export const createGotifyNotification = async (
};
export const updateGotifyNotification = async (
input: typeof apiUpdateGotify._type,
input: z.infer<typeof apiUpdateGotify>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -508,7 +609,7 @@ export const updateGotifyNotification = async (
};
export const createNtfyNotification = async (
input: typeof apiCreateNtfy._type,
input: z.infer<typeof apiCreateNtfy>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -559,7 +660,7 @@ export const createNtfyNotification = async (
};
export const updateNtfyNotification = async (
input: typeof apiUpdateNtfy._type,
input: z.infer<typeof apiUpdateNtfy>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -600,7 +701,7 @@ export const updateNtfyNotification = async (
};
export const createCustomNotification = async (
input: typeof apiCreateCustom._type,
input: z.infer<typeof apiCreateCustom>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -649,7 +750,7 @@ export const createCustomNotification = async (
};
export const updateCustomNotification = async (
input: typeof apiUpdateCustom._type,
input: z.infer<typeof apiUpdateCustom>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -696,12 +797,14 @@ export const findNotificationById = async (notificationId: string) => {
telegram: true,
discord: true,
email: true,
resend: true,
gotify: true,
ntfy: true,
mattermost: true,
custom: true,
lark: true,
pushover: true,
teams: true,
},
});
if (!notification) {
@@ -723,7 +826,7 @@ export const removeNotificationById = async (notificationId: string) => {
};
export const createLarkNotification = async (
input: typeof apiCreateLark._type,
input: z.infer<typeof apiCreateLark>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -771,7 +874,7 @@ export const createLarkNotification = async (
};
export const updateLarkNotification = async (
input: typeof apiUpdateLark._type,
input: z.infer<typeof apiUpdateLark>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx
@@ -811,6 +914,96 @@ export const updateLarkNotification = async (
});
};
export const createTeamsNotification = async (
input: z.infer<typeof apiCreateTeams>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newTeams = await tx
.insert(teams)
.values({
webhookUrl: input.webhookUrl,
})
.returning()
.then((value) => value[0]);
if (!newTeams) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting teams",
});
}
const newDestination = await tx
.insert(notifications)
.values({
teamsId: newTeams.teamsId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
volumeBackup: input.volumeBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "teams",
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
if (!newDestination) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting notification",
});
}
return newDestination;
});
};
export const updateTeamsNotification = async (
input: z.infer<typeof apiUpdateTeams>,
) => {
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(teams)
.set({
webhookUrl: input.webhookUrl,
})
.where(eq(teams.teamsId, input.teamsId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
export const updateNotificationById = async (
notificationId: string,
notificationData: Partial<Notification>,
@@ -919,7 +1112,7 @@ export const updateMattermostNotification = async (
};
export const createPushoverNotification = async (
input: typeof apiCreatePushover._type,
input: z.infer<typeof apiCreatePushover>,
organizationId: string,
) => {
await db.transaction(async (tx) => {
@@ -972,7 +1165,7 @@ export const createPushoverNotification = async (
};
export const updatePushoverNotification = async (
input: typeof apiUpdatePushover._type,
input: z.infer<typeof apiUpdatePushover>,
) => {
await db.transaction(async (tx) => {
const newDestination = await tx