Files
dokploy/packages/server/src/services/notification.ts
T
Mauricio SiuandGitHub 74a0f5e992 Feat/monitoring (#1267) Cloud Version
* feat: add start monitoring remote servers

* reafctor: update

* refactor: update

* refactor: update

* refactor: update

* refactor: update

* refactor: update

* refactor: update

* refactor:

* refactor: add metrics

* feat: add disk monitoring

* refactor: translate to english

* refacotor: add stats

* refactor: remove color

* feat: add log server metrics

* refactor: remove unused deps

* refactor: add origin

* refactor: add logs

* refactor: update

* feat: add series monitoring

* refactor: add system monitoring

* feat: add benchmark to optimize data

* refactor: update fn

* refactor: remove comments

* refactor: update

* refactor: exclude items

* feat: add refresh rate

* feat: add monitoring remote servers

* refactor: update

* refactor: remove unsued volumes

* refactor: update monitoring

* refactor: add more presets

* feat: add container metrics

* feat: add docker monitoring

* refactor: update conversion

* refactor: remove unused code

* refactor: update

* refactor: add docker compose logs

* refactor: add docker cli

* refactor: add install curl

* refactor: add get update

* refactor: add monitoring remote servers

* refactor: add containers config

* feat: add container specification

* refactor: update path

* refactor: add server filter

* refactor: simplify logic

* fix: verify if file exist before get stats

* refactor: update

* refactor: remove unused deps

* test: add test for containers

* refactor: update

* refactor add memory collector

* refactor: update

* refactor: update

* refactor: update

* refactor: remove

* refactor: add memory

* refactor: add server memory usage

* refactor: change memory

* refactor: update

* refactor: update

* refactor: add container metrics

* refactor: comment code

* refactor: mount proc bind

* refactor: change interval with node cron

* refactor: remove opening file

* refactor: use streams

* refactor: remove unused ws

* refactor: disable live when is all

* refactor: add sqlite

* refactor: update

* feat: add golang benchmark

* refactor: update go

* refactor: update dockerfile

* refactor: update db

* refactor: add env

* refactor: separate logic

* refactor: split logic

* refactor: update logs

* refactor: update dockerfile

* refactor: hide .env

* refactor: update

* chore: hide ,.ebnv

* refactor: add end angle

* refactor: update

* refactor: update

* refactor: update

* refactor: update

* refactor: update

* refactor: update monitoring

* refactor: add mount db

* refactor: add metrics and url callback

* refactor: add middleware

* refactor: add threshold property

* feat: add memory and cpu threshold notification

* feat: send notifications to the server

* feat: add metrics for dokploy server

* refactor: add dokploy server to monitoring

* refactor: update methods

* refactor: add admin to useeffect

* refactor: stop monitoring containers if elements are 0

* refactor: cancel request if appName is empty

* refactor: reuse methods

* chore; add feat monitoring

* refactor: set base url

* refactor: adjust monitoring

* refactor: delete migrations

* feat: add columns

* fix: add missing flag

* refactor: add free metrics

* refactor: add paid monitoring

* refactor: update methods

* feat: improve ui

* feat: add container stats

* refactor: add all container metrics

* refactor: add color primary

* refactor: change default rate limiting refresher

* refactor: update retention days

* refactor: use json instead of individual properties

* refactor: lint

* refactor: pass json env

* refactor: update

* refactor: delete

* refactor: update

* refactor: fix types

* refactor: add retention days

* chore: add license

* refactor: create db

* refactor: update path

* refactor: update setup

* refactor: update

* refactor: create files

* refactor: update

* refactor: delete

* refactor: update

* refactor: update token metrics

* fix: typechecks

* refactor: setup web server

* refactor: update error handling and add monitoring

* refactor: add local storage save

* refactor: add spacing

* refactor: update

* refactor: upgrade drizzle

* refactor: delete

* refactor: uppgrade drizzle kit

* refactor: update search with jsonB

* chore: upgrade drizzle

* chore: update packages

* refactor: add missing type

* refactor: add serverType

* refactor: update url

* refactor: update

* refactor: update

* refactor: hide monitoring on self hosted

* refactor: update server

* refactor: update

* refactor: update

* refactor: pin node version
2025-02-02 14:08:06 -06:00

526 lines
12 KiB
TypeScript

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,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
export type Notification = typeof notifications.$inferSelect;
export const createSlackNotification = async (
input: typeof apiCreateSlack._type,
adminId: string,
) => {
await db.transaction(async (tx) => {
const newSlack = await tx
.insert(slack)
.values({
channel: input.channel,
webhookUrl: input.webhookUrl,
})
.returning()
.then((value) => value[0]);
if (!newSlack) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting slack",
});
}
const newDestination = await tx
.insert(notifications)
.values({
slackId: newSlack.slackId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "slack",
adminId: adminId,
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 updateSlackNotification = async (
input: typeof apiUpdateSlack._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,
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(slack)
.set({
channel: input.channel,
webhookUrl: input.webhookUrl,
})
.where(eq(slack.slackId, input.slackId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
export const createTelegramNotification = async (
input: typeof apiCreateTelegram._type,
adminId: string,
) => {
await db.transaction(async (tx) => {
const newTelegram = await tx
.insert(telegram)
.values({
botToken: input.botToken,
chatId: input.chatId,
})
.returning()
.then((value) => value[0]);
if (!newTelegram) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting telegram",
});
}
const newDestination = await tx
.insert(notifications)
.values({
telegramId: newTelegram.telegramId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "telegram",
adminId: adminId,
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 updateTelegramNotification = async (
input: typeof apiUpdateTelegram._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,
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(telegram)
.set({
botToken: input.botToken,
chatId: input.chatId,
})
.where(eq(telegram.telegramId, input.telegramId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
export const createDiscordNotification = async (
input: typeof apiCreateDiscord._type,
adminId: string,
) => {
await db.transaction(async (tx) => {
const newDiscord = await tx
.insert(discord)
.values({
webhookUrl: input.webhookUrl,
decoration: input.decoration,
})
.returning()
.then((value) => value[0]);
if (!newDiscord) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting discord",
});
}
const newDestination = await tx
.insert(notifications)
.values({
discordId: newDiscord.discordId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "discord",
adminId: adminId,
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 updateDiscordNotification = async (
input: typeof apiUpdateDiscord._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,
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(discord)
.set({
webhookUrl: input.webhookUrl,
decoration: input.decoration,
})
.where(eq(discord.discordId, input.discordId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
export const createEmailNotification = async (
input: typeof apiCreateEmail._type,
adminId: string,
) => {
await db.transaction(async (tx) => {
const newEmail = await tx
.insert(email)
.values({
smtpServer: input.smtpServer,
smtpPort: input.smtpPort,
username: input.username,
password: input.password,
fromAddress: input.fromAddress,
toAddresses: input.toAddresses,
})
.returning()
.then((value) => value[0]);
if (!newEmail) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Inserting email",
});
}
const newDestination = await tx
.insert(notifications)
.values({
emailId: newEmail.emailId,
name: input.name,
appDeploy: input.appDeploy,
appBuildError: input.appBuildError,
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "email",
adminId: adminId,
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 updateEmailNotification = async (
input: typeof apiUpdateEmail._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,
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(email)
.set({
smtpServer: input.smtpServer,
smtpPort: input.smtpPort,
username: input.username,
password: input.password,
fromAddress: input.fromAddress,
toAddresses: input.toAddresses,
})
.where(eq(email.emailId, input.emailId))
.returning()
.then((value) => value[0]);
return newDestination;
});
};
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),
with: {
slack: true,
telegram: true,
discord: true,
email: true,
gotify: true,
},
});
if (!notification) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Notification not found",
});
}
return notification;
};
export const removeNotificationById = async (notificationId: string) => {
const result = await db
.delete(notifications)
.where(eq(notifications.notificationId, notificationId))
.returning();
return result[0];
};
export const updateNotificationById = async (
notificationId: string,
notificationData: Partial<Notification>,
) => {
const result = await db
.update(notifications)
.set({
...notificationData,
})
.where(eq(notifications.notificationId, notificationId))
.returning();
return result[0];
};