diff --git a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
index f8eb74985..94199c142 100644
--- a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
+++ b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
@@ -5,31 +5,31 @@ import { useFieldArray, useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import {
- DiscordIcon,
- GotifyIcon,
- LarkIcon,
- NtfyIcon,
- SlackIcon,
- TelegramIcon,
+ DiscordIcon,
+ GotifyIcon,
+ LarkIcon,
+ NtfyIcon,
+ SlackIcon,
+ TelegramIcon,
} from "@/components/icons/notification-icons";
import { Button } from "@/components/ui/button";
import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
} from "@/components/ui/dialog";
import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -39,1353 +39,1380 @@ import { api } from "@/utils/api";
import { KeyValueInput } from "./key-value-input";
const notificationBaseSchema = z.object({
- name: z.string().min(1, {
- message: "Name is required",
- }),
- appDeploy: z.boolean().default(false),
- appBuildError: z.boolean().default(false),
- databaseBackup: z.boolean().default(false),
- dokployRestart: z.boolean().default(false),
- dockerCleanup: z.boolean().default(false),
- serverThreshold: z.boolean().default(false),
+ name: z.string().min(1, {
+ message: "Name is required",
+ }),
+ appDeploy: z.boolean().default(false),
+ appBuildError: z.boolean().default(false),
+ databaseBackup: z.boolean().default(false),
+ volumeBackup: z.boolean().default(false),
+ dokployRestart: z.boolean().default(false),
+ dockerCleanup: z.boolean().default(false),
+ serverThreshold: z.boolean().default(false),
});
export const notificationSchema = z.discriminatedUnion("type", [
- z
- .object({
- type: z.literal("slack"),
- webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
- channel: z.string(),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("telegram"),
- botToken: z.string().min(1, { message: "Bot Token is required" }),
- chatId: z.string().min(1, { message: "Chat ID is required" }),
- messageThreadId: z.string().optional(),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("discord"),
- webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
- decoration: z.boolean().default(true),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("email"),
- smtpServer: z.string().min(1, { message: "SMTP Server is required" }),
- smtpPort: z.number().min(1, { message: "SMTP Port is required" }),
- username: z.string().min(1, { message: "Username is required" }),
- password: z.string().min(1, { message: "Password is required" }),
- fromAddress: z.string().min(1, { message: "From Address is required" }),
- toAddresses: z
- .array(
- z.string().min(1, { message: "Email is required" }).email({
- message: "Email is invalid",
- })
- )
- .min(1, { message: "At least one email is required" }),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("gotify"),
- serverUrl: z.string().min(1, { message: "Server URL is required" }),
- appToken: z.string().min(1, { message: "App Token is required" }),
- priority: z.number().min(1).max(10).default(5),
- decoration: z.boolean().default(true),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("ntfy"),
- serverUrl: z.string().min(1, { message: "Server URL is required" }),
- topic: z.string().min(1, { message: "Topic is required" }),
- accessToken: z.string().optional(),
- priority: z.number().min(1).max(5).default(3),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("custom"),
- endpoint: z.string().min(1, { message: "Endpoint URL is required" }),
- headers: z.string().optional(),
- })
- .merge(notificationBaseSchema),
- z
- .object({
- type: z.literal("lark"),
- webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
- })
- .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("slack"),
+ webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
+ channel: z.string(),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("telegram"),
+ botToken: z.string().min(1, { message: "Bot Token is required" }),
+ chatId: z.string().min(1, { message: "Chat ID is required" }),
+ messageThreadId: z.string().optional(),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("discord"),
+ webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
+ decoration: z.boolean().default(true),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("email"),
+ smtpServer: z.string().min(1, { message: "SMTP Server is required" }),
+ smtpPort: z.number().min(1, { message: "SMTP Port is required" }),
+ username: z.string().min(1, { message: "Username is required" }),
+ password: z.string().min(1, { message: "Password is required" }),
+ fromAddress: z.string().min(1, { message: "From Address is required" }),
+ toAddresses: z
+ .array(
+ z.string().min(1, { message: "Email is required" }).email({
+ message: "Email is invalid",
+ }),
+ )
+ .min(1, { message: "At least one email is required" }),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("gotify"),
+ serverUrl: z.string().min(1, { message: "Server URL is required" }),
+ appToken: z.string().min(1, { message: "App Token is required" }),
+ priority: z.number().min(1).max(10).default(5),
+ decoration: z.boolean().default(true),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("ntfy"),
+ serverUrl: z.string().min(1, { message: "Server URL is required" }),
+ topic: z.string().min(1, { message: "Topic is required" }),
+ accessToken: z.string().optional(),
+ priority: z.number().min(1).max(5).default(3),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("custom"),
+ endpoint: z.string().min(1, { message: "Endpoint URL is required" }),
+ headers: z.string().optional(),
+ })
+ .merge(notificationBaseSchema),
+ z
+ .object({
+ type: z.literal("lark"),
+ webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
+ })
+ .merge(notificationBaseSchema),
]);
export const notificationsMap = {
- slack: {
- icon:
,
- label: "Slack",
- },
- telegram: {
- icon:
,
- label: "Telegram",
- },
- discord: {
- icon:
,
- label: "Discord",
- },
- lark: {
- icon:
,
- label: "Lark",
- },
- email: {
- icon:
,
- label: "Email",
- },
- gotify: {
- icon:
,
- label: "Gotify",
- },
- ntfy: {
- icon:
,
- label: "ntfy",
- },
- custom: {
- icon:
,
- label: "Custom",
- },
+ slack: {
+ icon:
,
+ label: "Slack",
+ },
+ telegram: {
+ icon:
,
+ label: "Telegram",
+ },
+ discord: {
+ icon:
,
+ label: "Discord",
+ },
+ lark: {
+ icon:
,
+ label: "Lark",
+ },
+ email: {
+ icon:
,
+ label: "Email",
+ },
+ gotify: {
+ icon:
,
+ label: "Gotify",
+ },
+ ntfy: {
+ icon:
,
+ label: "ntfy",
+ },
+ custom: {
+ icon:
,
+ label: "Custom",
+ },
};
export type NotificationSchema = z.infer
;
interface Props {
- notificationId?: string;
+ notificationId?: string;
}
export const HandleNotifications = ({ notificationId }: Props) => {
- const utils = api.useUtils();
- const [visible, setVisible] = useState(false);
- const { data: isCloud } = api.settings.isCloud.useQuery();
+ const utils = api.useUtils();
+ const [visible, setVisible] = useState(false);
+ const { data: isCloud } = api.settings.isCloud.useQuery();
- const { data: notification } = api.notification.one.useQuery(
- {
- notificationId: notificationId || "",
- },
- {
- enabled: !!notificationId,
- }
- );
- const { mutateAsync: testSlackConnection, isLoading: isLoadingSlack } =
- api.notification.testSlackConnection.useMutation();
- const { mutateAsync: testTelegramConnection, isLoading: isLoadingTelegram } =
- api.notification.testTelegramConnection.useMutation();
- const { mutateAsync: testDiscordConnection, isLoading: isLoadingDiscord } =
- api.notification.testDiscordConnection.useMutation();
- const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } =
- api.notification.testEmailConnection.useMutation();
- const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } =
- api.notification.testGotifyConnection.useMutation();
- const { mutateAsync: testNtfyConnection, isLoading: isLoadingNtfy } =
- api.notification.testNtfyConnection.useMutation();
- const { mutateAsync: testCustomConnection, isLoading: isLoadingCustom } =
- api.notification.testCustomConnection.useMutation();
- const { mutateAsync: testLarkConnection, isLoading: isLoadingLark } =
- api.notification.testLarkConnection.useMutation();
- const slackMutation = notificationId
- ? api.notification.updateSlack.useMutation()
- : api.notification.createSlack.useMutation();
- const telegramMutation = notificationId
- ? api.notification.updateTelegram.useMutation()
- : api.notification.createTelegram.useMutation();
- const discordMutation = notificationId
- ? api.notification.updateDiscord.useMutation()
- : api.notification.createDiscord.useMutation();
- const emailMutation = notificationId
- ? api.notification.updateEmail.useMutation()
- : api.notification.createEmail.useMutation();
- const gotifyMutation = notificationId
- ? api.notification.updateGotify.useMutation()
- : api.notification.createGotify.useMutation();
- const ntfyMutation = notificationId
- ? api.notification.updateNtfy.useMutation()
- : api.notification.createNtfy.useMutation();
- const customMutation = notificationId
- ? api.notification.updateCustom.useMutation()
- : api.notification.createCustom.useMutation();
- const larkMutation = notificationId
- ? api.notification.updateLark.useMutation()
- : api.notification.createLark.useMutation();
+ const { data: notification } = api.notification.one.useQuery(
+ {
+ notificationId: notificationId || "",
+ },
+ {
+ enabled: !!notificationId,
+ },
+ );
+ const { mutateAsync: testSlackConnection, isLoading: isLoadingSlack } =
+ api.notification.testSlackConnection.useMutation();
+ const { mutateAsync: testTelegramConnection, isLoading: isLoadingTelegram } =
+ api.notification.testTelegramConnection.useMutation();
+ const { mutateAsync: testDiscordConnection, isLoading: isLoadingDiscord } =
+ api.notification.testDiscordConnection.useMutation();
+ const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } =
+ api.notification.testEmailConnection.useMutation();
+ const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } =
+ api.notification.testGotifyConnection.useMutation();
+ const { mutateAsync: testNtfyConnection, isLoading: isLoadingNtfy } =
+ api.notification.testNtfyConnection.useMutation();
+ const { mutateAsync: testLarkConnection, isLoading: isLoadingLark } =
+ api.notification.testLarkConnection.useMutation();
- const form = useForm({
- defaultValues: {
- type: "slack",
- webhookUrl: "",
- channel: "",
- name: "",
- },
- resolver: zodResolver(notificationSchema),
- });
- const type = form.watch("type");
+ const { mutateAsync: testCustomConnection, isLoading: isLoadingCustom } =
+ api.notification.testCustomConnection.useMutation();
- const { fields, append, remove } = useFieldArray({
- control: form.control,
- name: "toAddresses" as never,
- });
+ const customMutation = notificationId
+ ? api.notification.updateCustom.useMutation()
+ : api.notification.createCustom.useMutation();
+ const slackMutation = notificationId
+ ? api.notification.updateSlack.useMutation()
+ : api.notification.createSlack.useMutation();
+ const telegramMutation = notificationId
+ ? api.notification.updateTelegram.useMutation()
+ : api.notification.createTelegram.useMutation();
+ const discordMutation = notificationId
+ ? api.notification.updateDiscord.useMutation()
+ : api.notification.createDiscord.useMutation();
+ const emailMutation = notificationId
+ ? api.notification.updateEmail.useMutation()
+ : api.notification.createEmail.useMutation();
+ const gotifyMutation = notificationId
+ ? api.notification.updateGotify.useMutation()
+ : api.notification.createGotify.useMutation();
+ const ntfyMutation = notificationId
+ ? api.notification.updateNtfy.useMutation()
+ : api.notification.createNtfy.useMutation();
+ const larkMutation = notificationId
+ ? api.notification.updateLark.useMutation()
+ : api.notification.createLark.useMutation();
- useEffect(() => {
- if (type === "email" && fields.length === 0) {
- append("");
- }
- }, [type, append, fields.length]);
+ const form = useForm({
+ defaultValues: {
+ type: "slack",
+ webhookUrl: "",
+ channel: "",
+ name: "",
+ },
+ resolver: zodResolver(notificationSchema),
+ });
+ const type = form.watch("type");
- useEffect(() => {
- if (notification) {
- if (notification.notificationType === "slack") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- dockerCleanup: notification.dockerCleanup,
- webhookUrl: notification.slack?.webhookUrl,
- channel: notification.slack?.channel || "",
- name: notification.name,
- type: notification.notificationType,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "telegram") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- botToken: notification.telegram?.botToken,
- messageThreadId: notification.telegram?.messageThreadId || "",
- chatId: notification.telegram?.chatId,
- type: notification.notificationType,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "discord") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- webhookUrl: notification.discord?.webhookUrl,
- decoration: notification.discord?.decoration || undefined,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "email") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- smtpServer: notification.email?.smtpServer,
- smtpPort: notification.email?.smtpPort,
- username: notification.email?.username,
- password: notification.email?.password,
- toAddresses: notification.email?.toAddresses,
- fromAddress: notification.email?.fromAddress,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "gotify") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- appToken: notification.gotify?.appToken,
- decoration: notification.gotify?.decoration || undefined,
- priority: notification.gotify?.priority,
- serverUrl: notification.gotify?.serverUrl,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- });
- } else if (notification.notificationType === "ntfy") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- accessToken: notification.ntfy?.accessToken || "",
- topic: notification.ntfy?.topic,
- priority: notification.ntfy?.priority,
- serverUrl: notification.ntfy?.serverUrl,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "lark") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- webhookUrl: notification.lark?.webhookUrl,
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- } else if (notification.notificationType === "custom") {
- form.reset({
- appBuildError: notification.appBuildError,
- appDeploy: notification.appDeploy,
- dokployRestart: notification.dokployRestart,
- databaseBackup: notification.databaseBackup,
- type: notification.notificationType,
- endpoint: notification.custom?.endpoint || "",
- headers: notification.custom?.headers || "",
- name: notification.name,
- dockerCleanup: notification.dockerCleanup,
- serverThreshold: notification.serverThreshold,
- });
- }
- } else {
- form.reset();
- }
- }, [form, form.reset, form.formState.isSubmitSuccessful, notification]);
+ const { fields, append, remove } = useFieldArray({
+ control: form.control,
+ name: "toAddresses" as never,
+ });
- const activeMutation = {
- slack: slackMutation,
- telegram: telegramMutation,
- discord: discordMutation,
- email: emailMutation,
- gotify: gotifyMutation,
- ntfy: ntfyMutation,
- custom: customMutation,
- lark: larkMutation,
- };
+ useEffect(() => {
+ if (type === "email" && fields.length === 0) {
+ append("");
+ }
+ }, [type, append, fields.length]);
- const onSubmit = async (data: NotificationSchema) => {
- const {
- appBuildError,
- appDeploy,
- dokployRestart,
- databaseBackup,
- dockerCleanup,
- serverThreshold,
- } = data;
- let promise: Promise | null = null;
- if (data.type === "slack") {
- promise = slackMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- webhookUrl: data.webhookUrl,
- channel: data.channel,
- name: data.name,
- dockerCleanup: dockerCleanup,
- slackId: notification?.slackId || "",
- notificationId: notificationId || "",
- serverThreshold: serverThreshold,
- });
- } else if (data.type === "telegram") {
- promise = telegramMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- botToken: data.botToken,
- messageThreadId: data.messageThreadId || "",
- chatId: data.chatId,
- name: data.name,
- dockerCleanup: dockerCleanup,
- notificationId: notificationId || "",
- telegramId: notification?.telegramId || "",
- serverThreshold: serverThreshold,
- });
- } else if (data.type === "discord") {
- promise = discordMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- webhookUrl: data.webhookUrl,
- decoration: data.decoration,
- name: data.name,
- dockerCleanup: dockerCleanup,
- notificationId: notificationId || "",
- discordId: notification?.discordId || "",
- serverThreshold: serverThreshold,
- });
- } else if (data.type === "email") {
- promise = emailMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- smtpServer: data.smtpServer,
- smtpPort: data.smtpPort,
- username: data.username,
- password: data.password,
- fromAddress: data.fromAddress,
- toAddresses: data.toAddresses,
- name: data.name,
- dockerCleanup: dockerCleanup,
- notificationId: notificationId || "",
- emailId: notification?.emailId || "",
- serverThreshold: serverThreshold,
- });
- } else if (data.type === "gotify") {
- promise = gotifyMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- serverUrl: data.serverUrl,
- appToken: data.appToken,
- priority: data.priority,
- name: data.name,
- dockerCleanup: dockerCleanup,
- decoration: data.decoration,
- notificationId: notificationId || "",
- gotifyId: notification?.gotifyId || "",
- });
- } else if (data.type === "ntfy") {
- promise = ntfyMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- serverUrl: data.serverUrl,
- accessToken: data.accessToken || "",
- topic: data.topic,
- priority: data.priority,
- name: data.name,
- dockerCleanup: dockerCleanup,
- notificationId: notificationId || "",
- ntfyId: notification?.ntfyId || "",
- });
- } else if (data.type === "custom") {
- promise = customMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- endpoint: data.endpoint,
- headers: data.headers || "",
- name: data.name,
- dockerCleanup: dockerCleanup,
- serverThreshold: serverThreshold,
- notificationId: notificationId || "",
- customId: notification?.customId || "",
- });
- } else if (data.type === "lark") {
- promise = larkMutation.mutateAsync({
- appBuildError: appBuildError,
- appDeploy: appDeploy,
- dokployRestart: dokployRestart,
- databaseBackup: databaseBackup,
- webhookUrl: data.webhookUrl,
- name: data.name,
- dockerCleanup: dockerCleanup,
- notificationId: notificationId || "",
- larkId: notification?.larkId || "",
- serverThreshold: serverThreshold,
- });
- }
+ useEffect(() => {
+ if (notification) {
+ if (notification.notificationType === "slack") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ dockerCleanup: notification.dockerCleanup,
+ webhookUrl: notification.slack?.webhookUrl,
+ channel: notification.slack?.channel || "",
+ name: notification.name,
+ type: notification.notificationType,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "telegram") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ botToken: notification.telegram?.botToken,
+ messageThreadId: notification.telegram?.messageThreadId || "",
+ chatId: notification.telegram?.chatId,
+ type: notification.notificationType,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "discord") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ type: notification.notificationType,
+ webhookUrl: notification.discord?.webhookUrl,
+ decoration: notification.discord?.decoration || undefined,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "email") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ type: notification.notificationType,
+ smtpServer: notification.email?.smtpServer,
+ smtpPort: notification.email?.smtpPort,
+ username: notification.email?.username,
+ password: notification.email?.password,
+ toAddresses: notification.email?.toAddresses,
+ fromAddress: notification.email?.fromAddress,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "gotify") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ type: notification.notificationType,
+ appToken: notification.gotify?.appToken,
+ decoration: notification.gotify?.decoration || undefined,
+ priority: notification.gotify?.priority,
+ serverUrl: notification.gotify?.serverUrl,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ });
+ } else if (notification.notificationType === "ntfy") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ volumeBackup: notification.volumeBackup,
+ type: notification.notificationType,
+ accessToken: notification.ntfy?.accessToken || "",
+ topic: notification.ntfy?.topic,
+ priority: notification.ntfy?.priority,
+ serverUrl: notification.ntfy?.serverUrl,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "lark") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ type: notification.notificationType,
+ webhookUrl: notification.lark?.webhookUrl,
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ } else if (notification.notificationType === "custom") {
+ form.reset({
+ appBuildError: notification.appBuildError,
+ appDeploy: notification.appDeploy,
+ dokployRestart: notification.dokployRestart,
+ databaseBackup: notification.databaseBackup,
+ type: notification.notificationType,
+ endpoint: notification.custom?.endpoint || "",
+ headers: notification.custom?.headers || "",
+ name: notification.name,
+ dockerCleanup: notification.dockerCleanup,
+ serverThreshold: notification.serverThreshold,
+ });
+ }
+ } else {
+ form.reset();
+ }
+ }, [form, form.reset, form.formState.isSubmitSuccessful, notification]);
- if (promise) {
- await promise
- .then(async () => {
- toast.success(
- notificationId ? "Notification Updated" : "Notification Created"
- );
- form.reset({
- type: "slack",
- webhookUrl: "",
- });
- setVisible(false);
- await utils.notification.all.invalidate();
- })
- .catch(() => {
- toast.error(
- notificationId
- ? "Error updating a notification"
- : "Error creating a notification"
- );
- });
- }
- };
- return (
-
-
- {notificationId ? (
-
-
-
- ) : (
-
-
- Add Notification
-
- )}
-
-
-
-
- {notificationId ? "Update" : "Add"} Notification
-
-
- {notificationId
- ? "Update your notification providers for multiple channels."
- : "Create new notification providers for multiple channels."}
-
-
-
- const data = form.getValues();
+
+ {
+ const isValid = await form.trigger();
+ if (!isValid) return;
- try {
- if (data.type === "slack") {
- await testSlackConnection({
- webhookUrl: data.webhookUrl,
- channel: data.channel,
- });
- } else if (data.type === "telegram") {
- await testTelegramConnection({
- botToken: data.botToken,
- chatId: data.chatId,
- messageThreadId: data.messageThreadId || "",
- });
- } else if (data.type === "discord") {
- await testDiscordConnection({
- webhookUrl: data.webhookUrl,
- decoration: data.decoration,
- });
- } else if (data.type === "email") {
- await testEmailConnection({
- smtpServer: data.smtpServer,
- smtpPort: data.smtpPort,
- username: data.username,
- password: data.password,
- fromAddress: data.fromAddress,
- toAddresses: data.toAddresses,
- });
- } else if (data.type === "gotify") {
- await testGotifyConnection({
- serverUrl: data.serverUrl,
- appToken: data.appToken,
- priority: data.priority,
- decoration: data.decoration,
- });
- } else if (data.type === "ntfy") {
- await testNtfyConnection({
- serverUrl: data.serverUrl,
- topic: data.topic,
- accessToken: data.accessToken || "",
- priority: data.priority,
- });
- } else if (data.type === "custom") {
- await testCustomConnection({
- endpoint: data.endpoint,
- headers: data.headers,
- });
- } else if (data.type === "lark") {
- await testLarkConnection({
- webhookUrl: data.webhookUrl,
- });
- }
- toast.success("Connection Success");
- } catch (error) {
- toast.error(
- `Error testing the provider: ${
- error instanceof Error ? error.message : "Unknown error"
- }`
- );
- }
- }}
- >
- Test Notification
-
-
- {notificationId ? "Update" : "Create"}
-
-
-
-
-
- );
+ const data = form.getValues();
+
+ try {
+ if (data.type === "slack") {
+ await testSlackConnection({
+ webhookUrl: data.webhookUrl,
+ channel: data.channel,
+ });
+ } else if (data.type === "telegram") {
+ await testTelegramConnection({
+ botToken: data.botToken,
+ chatId: data.chatId,
+ messageThreadId: data.messageThreadId || "",
+ });
+ } else if (data.type === "discord") {
+ await testDiscordConnection({
+ webhookUrl: data.webhookUrl,
+ decoration: data.decoration,
+ });
+ } else if (data.type === "email") {
+ await testEmailConnection({
+ smtpServer: data.smtpServer,
+ smtpPort: data.smtpPort,
+ username: data.username,
+ password: data.password,
+ fromAddress: data.fromAddress,
+ toAddresses: data.toAddresses,
+ });
+ } else if (data.type === "gotify") {
+ await testGotifyConnection({
+ serverUrl: data.serverUrl,
+ appToken: data.appToken,
+ priority: data.priority,
+ decoration: data.decoration,
+ });
+ } else if (data.type === "ntfy") {
+ await testNtfyConnection({
+ serverUrl: data.serverUrl,
+ topic: data.topic,
+ accessToken: data.accessToken || "",
+ priority: data.priority,
+ });
+ } else if (data.type === "lark") {
+ await testLarkConnection({
+ webhookUrl: data.webhookUrl,
+ });
+ }
+ toast.success("Connection Success");
+ } catch (error) {
+ toast.error(
+ `Error testing the provider: ${error instanceof Error ? error.message : "Unknown error"}`,
+ );
+ }
+ }}
+ >
+ Test Notification
+
+
+ {notificationId ? "Update" : "Create"}
+
+
+
+
+
+ );
};
diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx
index 583f3fefe..47219620f 100644
--- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx
+++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx
@@ -41,6 +41,7 @@ const profileSchema = z.object({
currentPassword: z.string().nullable(),
image: z.string().optional(),
name: z.string().optional(),
+ lastName: z.string().optional(),
allowImpersonation: z.boolean().optional().default(false),
});
@@ -88,7 +89,8 @@ export const ProfileForm = () => {
image: data?.user?.image || "",
currentPassword: "",
allowImpersonation: data?.user?.allowImpersonation || false,
- name: data?.user?.name || "",
+ name: data?.user?.firstName || "",
+ lastName: data?.user?.lastName || "",
},
resolver: zodResolver(profileSchema),
});
@@ -102,7 +104,8 @@ export const ProfileForm = () => {
image: data?.user?.image || "",
currentPassword: form.getValues("currentPassword") || "",
allowImpersonation: data?.user?.allowImpersonation,
- name: data?.user?.name || "",
+ name: data?.user?.firstName || "",
+ lastName: data?.user?.lastName || "",
},
{
keepValues: true,
@@ -127,6 +130,7 @@ export const ProfileForm = () => {
currentPassword: values.currentPassword || undefined,
allowImpersonation: values.allowImpersonation,
name: values.name || undefined,
+ lastName: values.lastName || undefined,
});
await refetch();
toast.success("Profile Updated");
@@ -136,6 +140,7 @@ export const ProfileForm = () => {
image: values.image,
currentPassword: "",
name: values.name || "",
+ lastName: values.lastName || "",
});
} catch (error) {
toast.error("Error updating the profile");
@@ -180,9 +185,22 @@ export const ProfileForm = () => {
name="name"
render={({ field }) => (
- Name
+ First Name
-
+
+
+
+
+ )}
+ />
+ (
+
+ Last Name
+
+
@@ -280,7 +298,7 @@ export const ProfileForm = () => {
{getFallbackAvatarInitials(
- data?.user?.name,
+ `${data?.user?.firstName} ${data?.user?.lastName}`.trim(),
)}
diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
index d9573ca74..aebba8877 100644
--- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
+++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
@@ -1,5 +1,7 @@
import { useTranslation } from "next-i18next";
import { toast } from "sonner";
+import { AlertBlock } from "@/components/shared/alert-block";
+import { DialogAction } from "@/components/shared/dialog-action";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@@ -85,7 +87,26 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
-
+
+ The Traefik container will be recreated from scratch. This
+ means the container will be deleted and created again, which
+ may cause downtime in your applications.
+
+
+ Are you sure you want to{" "}
+ {haveTraefikDashboardPortEnabled ? "disable" : "enable"} the
+ Traefik dashboard?
+
+
+ }
onClick={async () => {
await toggleDashboard({
enableDashboard: !haveTraefikDashboardPortEnabled,
@@ -97,14 +118,26 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
);
refetchDashboard();
})
- .catch(() => {});
+ .catch((error) => {
+ const errorMessage =
+ error?.message ||
+ "Failed to toggle dashboard. Please check if port 8080 is available.";
+ toast.error(errorMessage);
+ });
}}
- className="w-full cursor-pointer space-x-3"
+ disabled={toggleDashboardIsLoading}
+ type="default"
>
-