feat: add mattermost notification provider

Add comprehensive Mattermost integration as a new notification provider:

## Backend Implementation:
- Add `mattermost` to notificationType enum and database schema
- Create mattermost table with webhookUrl, channel, username fields
- Implement CRUD operations: createMattermostNotification, updateMattermostNotification
- Add API routes: createMattermost, updateMattermost, testMattermostConnection
- Add sendMattermostNotification utility with proper payload formatting

## Frontend Implementation:
- Add MattermostIcon component with provided SVG logo
- Extend notification form with Mattermost schema validation
- Add webhook URL (required), channel and username (optional) form fields
- Integrate test connection functionality
- Add Mattermost to provider selection UI

## Notification Integration:
- Integrate across all notification types:
  - Build success/error notifications
  - Database backup notifications
  - Docker cleanup notifications
  - Dokploy restart notifications
  - Server threshold alerts
- Format messages using Markdown for Mattermost compatibility
- Handle optional channel (#prefix) and username override
- Graceful fallback for empty optional fields

## Features:
- Webhook-based messaging to Mattermost channels
- Optional channel targeting and custom username display
- Consistent formatting with other notification providers
- Full CRUD support with proper validation
- Test connection capability

Closes: Support for Mattermost team communication platform

# Conflicts:
#	apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
#	apps/dokploy/components/icons/notification-icons.tsx
#	apps/dokploy/server/api/routers/notification.ts
#	packages/server/src/db/schema/notification.ts
#	packages/server/src/services/notification.ts
#	packages/server/src/utils/notifications/build-error.ts
#	packages/server/src/utils/notifications/build-success.ts
#	packages/server/src/utils/notifications/database-backup.ts
#	packages/server/src/utils/notifications/docker-cleanup.ts
#	packages/server/src/utils/notifications/dokploy-restart.ts
#	packages/server/src/utils/notifications/server-threshold.ts
#	packages/server/src/utils/notifications/utils.ts
This commit is contained in:
Hootan
2025-09-29 18:46:25 +02:00
parent dadef000d5
commit a0c87358eb
12 changed files with 459 additions and 14 deletions

View File

@@ -4,6 +4,7 @@ import {
type apiCreateEmail,
type apiCreateLark,
type apiCreateGotify,
type apiCreateMattermost,
type apiCreateNtfy,
type apiCreateSlack,
type apiCreateTelegram,
@@ -11,6 +12,7 @@ import {
type apiUpdateEmail,
type apiUpdateLark,
type apiUpdateGotify,
type apiUpdateMattermost,
type apiUpdateNtfy,
type apiUpdateSlack,
type apiUpdateTelegram,
@@ -18,6 +20,7 @@ import {
email,
lark,
gotify,
mattermost,
notifications,
ntfy,
slack,
@@ -588,6 +591,7 @@ export const findNotificationById = async (notificationId: string) => {
email: true,
gotify: true,
ntfy: true,
mattermost: true,
lark: true,
},
});
@@ -711,3 +715,95 @@ export const updateNotificationById = async (
return result[0];
};
export const createMattermostNotification = async (
input: typeof apiCreateMattermost._type,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newMattermost = await tx
.insert(mattermost)
.values({
webhookUrl: input.webhookUrl,
channel: input.channel,
username: input.username,
})
.returning()
.then((value) => value[0]);
if (!newMattermost) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting mattermost",
});
}
const newDestination = await tx
.insert(notifications)
.values({
mattermostId: newMattermost.mattermostId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "mattermost",
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 updateMattermostNotification = async (
input: typeof apiUpdateMattermost._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,
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(mattermost)
.set({
webhookUrl: input.webhookUrl,
channel: input.channel,
username: input.username,
})
.where(eq(mattermost.mattermostId, input.mattermostId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};