mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-31 09:12:15 +00:00
Merge pull request #3512 from mhbdev/resend-provider-for-notifications
feat: add resend notification functionality
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
|||||||
LarkIcon,
|
LarkIcon,
|
||||||
NtfyIcon,
|
NtfyIcon,
|
||||||
PushoverIcon,
|
PushoverIcon,
|
||||||
|
ResendIcon,
|
||||||
SlackIcon,
|
SlackIcon,
|
||||||
TelegramIcon,
|
TelegramIcon,
|
||||||
} from "@/components/icons/notification-icons";
|
} from "@/components/icons/notification-icons";
|
||||||
@@ -97,6 +98,23 @@ export const notificationSchema = z.discriminatedUnion("type", [
|
|||||||
.min(1, { message: "At least one email is required" }),
|
.min(1, { message: "At least one email is required" }),
|
||||||
})
|
})
|
||||||
.merge(notificationBaseSchema),
|
.merge(notificationBaseSchema),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
type: z.literal("resend"),
|
||||||
|
apiKey: z.string().min(1, { message: "API Key is required" }),
|
||||||
|
fromAddress: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: "From Address is required" })
|
||||||
|
.email({ message: "Email is invalid" }),
|
||||||
|
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
|
z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal("gotify"),
|
type: z.literal("gotify"),
|
||||||
@@ -169,6 +187,10 @@ export const notificationsMap = {
|
|||||||
icon: <Mail size={29} className="text-muted-foreground" />,
|
icon: <Mail size={29} className="text-muted-foreground" />,
|
||||||
label: "Email",
|
label: "Email",
|
||||||
},
|
},
|
||||||
|
resend: {
|
||||||
|
icon: <ResendIcon className="text-muted-foreground" />,
|
||||||
|
label: "Resend",
|
||||||
|
},
|
||||||
gotify: {
|
gotify: {
|
||||||
icon: <GotifyIcon />,
|
icon: <GotifyIcon />,
|
||||||
label: "Gotify",
|
label: "Gotify",
|
||||||
@@ -214,6 +236,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
api.notification.testDiscordConnection.useMutation();
|
api.notification.testDiscordConnection.useMutation();
|
||||||
const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } =
|
const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } =
|
||||||
api.notification.testEmailConnection.useMutation();
|
api.notification.testEmailConnection.useMutation();
|
||||||
|
const { mutateAsync: testResendConnection, isLoading: isLoadingResend } =
|
||||||
|
api.notification.testResendConnection.useMutation();
|
||||||
const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } =
|
const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } =
|
||||||
api.notification.testGotifyConnection.useMutation();
|
api.notification.testGotifyConnection.useMutation();
|
||||||
const { mutateAsync: testNtfyConnection, isLoading: isLoadingNtfy } =
|
const { mutateAsync: testNtfyConnection, isLoading: isLoadingNtfy } =
|
||||||
@@ -242,6 +266,9 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
const emailMutation = notificationId
|
const emailMutation = notificationId
|
||||||
? api.notification.updateEmail.useMutation()
|
? api.notification.updateEmail.useMutation()
|
||||||
: api.notification.createEmail.useMutation();
|
: api.notification.createEmail.useMutation();
|
||||||
|
const resendMutation = notificationId
|
||||||
|
? api.notification.updateResend.useMutation()
|
||||||
|
: api.notification.createResend.useMutation();
|
||||||
const gotifyMutation = notificationId
|
const gotifyMutation = notificationId
|
||||||
? api.notification.updateGotify.useMutation()
|
? api.notification.updateGotify.useMutation()
|
||||||
: api.notification.createGotify.useMutation();
|
: api.notification.createGotify.useMutation();
|
||||||
@@ -281,7 +308,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (type === "email" && fields.length === 0) {
|
if ((type === "email" || type === "resend") && fields.length === 0) {
|
||||||
append("");
|
append("");
|
||||||
}
|
}
|
||||||
}, [type, append, fields.length]);
|
}, [type, append, fields.length]);
|
||||||
@@ -349,6 +376,21 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
dockerCleanup: notification.dockerCleanup,
|
dockerCleanup: notification.dockerCleanup,
|
||||||
serverThreshold: notification.serverThreshold,
|
serverThreshold: notification.serverThreshold,
|
||||||
});
|
});
|
||||||
|
} else if (notification.notificationType === "resend") {
|
||||||
|
form.reset({
|
||||||
|
appBuildError: notification.appBuildError,
|
||||||
|
appDeploy: notification.appDeploy,
|
||||||
|
dokployRestart: notification.dokployRestart,
|
||||||
|
databaseBackup: notification.databaseBackup,
|
||||||
|
volumeBackup: notification.volumeBackup,
|
||||||
|
type: notification.notificationType,
|
||||||
|
apiKey: notification.resend?.apiKey,
|
||||||
|
toAddresses: notification.resend?.toAddresses,
|
||||||
|
fromAddress: notification.resend?.fromAddress,
|
||||||
|
name: notification.name,
|
||||||
|
dockerCleanup: notification.dockerCleanup,
|
||||||
|
serverThreshold: notification.serverThreshold,
|
||||||
|
});
|
||||||
} else if (notification.notificationType === "gotify") {
|
} else if (notification.notificationType === "gotify") {
|
||||||
form.reset({
|
form.reset({
|
||||||
appBuildError: notification.appBuildError,
|
appBuildError: notification.appBuildError,
|
||||||
@@ -442,6 +484,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
telegram: telegramMutation,
|
telegram: telegramMutation,
|
||||||
discord: discordMutation,
|
discord: discordMutation,
|
||||||
email: emailMutation,
|
email: emailMutation,
|
||||||
|
resend: resendMutation,
|
||||||
gotify: gotifyMutation,
|
gotify: gotifyMutation,
|
||||||
ntfy: ntfyMutation,
|
ntfy: ntfyMutation,
|
||||||
lark: larkMutation,
|
lark: larkMutation,
|
||||||
@@ -525,6 +568,22 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
emailId: notification?.emailId || "",
|
emailId: notification?.emailId || "",
|
||||||
serverThreshold: serverThreshold,
|
serverThreshold: serverThreshold,
|
||||||
});
|
});
|
||||||
|
} else if (data.type === "resend") {
|
||||||
|
promise = resendMutation.mutateAsync({
|
||||||
|
appBuildError: appBuildError,
|
||||||
|
appDeploy: appDeploy,
|
||||||
|
dokployRestart: dokployRestart,
|
||||||
|
databaseBackup: databaseBackup,
|
||||||
|
volumeBackup: volumeBackup,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
fromAddress: data.fromAddress,
|
||||||
|
toAddresses: data.toAddresses,
|
||||||
|
name: data.name,
|
||||||
|
dockerCleanup: dockerCleanup,
|
||||||
|
notificationId: notificationId || "",
|
||||||
|
resendId: notification?.resendId || "",
|
||||||
|
serverThreshold: serverThreshold,
|
||||||
|
});
|
||||||
} else if (data.type === "gotify") {
|
} else if (data.type === "gotify") {
|
||||||
promise = gotifyMutation.mutateAsync({
|
promise = gotifyMutation.mutateAsync({
|
||||||
appBuildError: appBuildError,
|
appBuildError: appBuildError,
|
||||||
@@ -1042,6 +1101,96 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{type === "resend" && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="apiKey"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>API Key</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
placeholder="re_********"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="fromAddress"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>From Address</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="[email protected]" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2 pt-2">
|
||||||
|
<FormLabel>To Addresses</FormLabel>
|
||||||
|
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<div
|
||||||
|
key={field.id}
|
||||||
|
className="flex flex-row gap-2 w-full"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={`toAddresses.${index}`}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="[email protected]"
|
||||||
|
className="w-full"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
remove(index);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{type === "resend" &&
|
||||||
|
"toAddresses" in form.formState.errors && (
|
||||||
|
<div className="text-sm font-medium text-destructive">
|
||||||
|
{form.formState?.errors?.toAddresses?.root?.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
append("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{type === "gotify" && (
|
{type === "gotify" && (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -1627,6 +1776,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
isLoadingTelegram ||
|
isLoadingTelegram ||
|
||||||
isLoadingDiscord ||
|
isLoadingDiscord ||
|
||||||
isLoadingEmail ||
|
isLoadingEmail ||
|
||||||
|
isLoadingResend ||
|
||||||
isLoadingGotify ||
|
isLoadingGotify ||
|
||||||
isLoadingNtfy ||
|
isLoadingNtfy ||
|
||||||
isLoadingLark ||
|
isLoadingLark ||
|
||||||
@@ -1667,6 +1817,12 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
fromAddress: data.fromAddress,
|
fromAddress: data.fromAddress,
|
||||||
toAddresses: data.toAddresses,
|
toAddresses: data.toAddresses,
|
||||||
});
|
});
|
||||||
|
} else if (data.type === "resend") {
|
||||||
|
await testResendConnection({
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
fromAddress: data.fromAddress,
|
||||||
|
toAddresses: data.toAddresses,
|
||||||
|
});
|
||||||
} else if (data.type === "gotify") {
|
} else if (data.type === "gotify") {
|
||||||
await testGotifyConnection({
|
await testGotifyConnection({
|
||||||
serverUrl: data.serverUrl,
|
serverUrl: data.serverUrl,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
GotifyIcon,
|
GotifyIcon,
|
||||||
LarkIcon,
|
LarkIcon,
|
||||||
NtfyIcon,
|
NtfyIcon,
|
||||||
|
ResendIcon,
|
||||||
SlackIcon,
|
SlackIcon,
|
||||||
TelegramIcon,
|
TelegramIcon,
|
||||||
} from "@/components/icons/notification-icons";
|
} from "@/components/icons/notification-icons";
|
||||||
@@ -36,7 +37,7 @@ export const ShowNotifications = () => {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Add your providers to receive notifications, like Discord, Slack,
|
Add your providers to receive notifications, like Discord, Slack,
|
||||||
Telegram, Email, Lark.
|
Telegram, Email, Resend, Lark.
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2 py-8 border-t">
|
<CardContent className="space-y-2 py-8 border-t">
|
||||||
@@ -86,6 +87,11 @@ export const ShowNotifications = () => {
|
|||||||
<Mail className="size-6 text-muted-foreground" />
|
<Mail className="size-6 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{notification.notificationType === "resend" && (
|
||||||
|
<div className="flex items-center justify-center rounded-lg ">
|
||||||
|
<ResendIcon className="size-6 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{notification.notificationType === "gotify" && (
|
{notification.notificationType === "gotify" && (
|
||||||
<div className="flex items-center justify-center rounded-lg ">
|
<div className="flex items-center justify-center rounded-lg ">
|
||||||
<GotifyIcon className="size-6" />
|
<GotifyIcon className="size-6" />
|
||||||
|
|||||||
@@ -257,3 +257,23 @@ export const PushoverIcon = ({ className }: Props) => {
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ResendIcon = ({ className }: Props) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
className={cn("size-8", className)}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="10" fill="currentColor" opacity="0.12" />
|
||||||
|
<path
|
||||||
|
d="M8 17V7h6a3 3 0 0 1 0 6H8m6 0 2 4"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
ALTER TYPE "public"."notificationType" ADD VALUE 'resend' BEFORE 'gotify';--> statement-breakpoint
|
||||||
|
CREATE TABLE "resend" (
|
||||||
|
"resendId" text PRIMARY KEY NOT NULL,
|
||||||
|
"apiKey" text NOT NULL,
|
||||||
|
"fromAddress" text NOT NULL,
|
||||||
|
"toAddress" text[] NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "notification" ADD COLUMN "resendId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "notification" ADD CONSTRAINT "notification_resendId_resend_resendId_fk" FOREIGN KEY ("resendId") REFERENCES "public"."resend"("resendId") ON DELETE cascade ON UPDATE no action;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -967,6 +967,13 @@
|
|||||||
"when": 1770274109332,
|
"when": 1770274109332,
|
||||||
"tag": "0137_colossal_sally_floyd",
|
"tag": "0137_colossal_sally_floyd",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 138,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1770324882572,
|
||||||
|
"tag": "0138_pretty_ironclad",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
findDomainById,
|
findDomainById,
|
||||||
findDomainsByApplicationId,
|
findDomainsByApplicationId,
|
||||||
findDomainsByComposeId,
|
findDomainsByComposeId,
|
||||||
findOrganizationById,
|
|
||||||
findPreviewDeploymentById,
|
findPreviewDeploymentById,
|
||||||
findServerById,
|
findServerById,
|
||||||
generateTraefikMeDomain,
|
generateTraefikMeDomain,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
createLarkNotification,
|
createLarkNotification,
|
||||||
createNtfyNotification,
|
createNtfyNotification,
|
||||||
createPushoverNotification,
|
createPushoverNotification,
|
||||||
|
createResendNotification,
|
||||||
createSlackNotification,
|
createSlackNotification,
|
||||||
createTelegramNotification,
|
createTelegramNotification,
|
||||||
findNotificationById,
|
findNotificationById,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendServerThresholdNotifications,
|
sendServerThresholdNotifications,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
@@ -29,6 +31,7 @@ import {
|
|||||||
updateLarkNotification,
|
updateLarkNotification,
|
||||||
updateNtfyNotification,
|
updateNtfyNotification,
|
||||||
updatePushoverNotification,
|
updatePushoverNotification,
|
||||||
|
updateResendNotification,
|
||||||
updateSlackNotification,
|
updateSlackNotification,
|
||||||
updateTelegramNotification,
|
updateTelegramNotification,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
@@ -50,6 +53,7 @@ import {
|
|||||||
apiCreateLark,
|
apiCreateLark,
|
||||||
apiCreateNtfy,
|
apiCreateNtfy,
|
||||||
apiCreatePushover,
|
apiCreatePushover,
|
||||||
|
apiCreateResend,
|
||||||
apiCreateSlack,
|
apiCreateSlack,
|
||||||
apiCreateTelegram,
|
apiCreateTelegram,
|
||||||
apiFindOneNotification,
|
apiFindOneNotification,
|
||||||
@@ -60,6 +64,7 @@ import {
|
|||||||
apiTestLarkConnection,
|
apiTestLarkConnection,
|
||||||
apiTestNtfyConnection,
|
apiTestNtfyConnection,
|
||||||
apiTestPushoverConnection,
|
apiTestPushoverConnection,
|
||||||
|
apiTestResendConnection,
|
||||||
apiTestSlackConnection,
|
apiTestSlackConnection,
|
||||||
apiTestTelegramConnection,
|
apiTestTelegramConnection,
|
||||||
apiUpdateCustom,
|
apiUpdateCustom,
|
||||||
@@ -69,6 +74,7 @@ import {
|
|||||||
apiUpdateLark,
|
apiUpdateLark,
|
||||||
apiUpdateNtfy,
|
apiUpdateNtfy,
|
||||||
apiUpdatePushover,
|
apiUpdatePushover,
|
||||||
|
apiUpdateResend,
|
||||||
apiUpdateSlack,
|
apiUpdateSlack,
|
||||||
apiUpdateTelegram,
|
apiUpdateTelegram,
|
||||||
notifications,
|
notifications,
|
||||||
@@ -302,6 +308,63 @@ export const notificationRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
createResend: adminProcedure
|
||||||
|
.input(apiCreateResend)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
return await createResendNotification(
|
||||||
|
input,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error creating the notification",
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
updateResend: adminProcedure
|
||||||
|
.input(apiUpdateResend)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const notification = await findNotificationById(input.notificationId);
|
||||||
|
if (notification.organizationId !== ctx.session.activeOrganizationId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to update this notification",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return await updateResendNotification({
|
||||||
|
...input,
|
||||||
|
organizationId: ctx.session.activeOrganizationId,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error updating the notification",
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
testResendConnection: adminProcedure
|
||||||
|
.input(apiTestResendConnection)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
try {
|
||||||
|
await sendResendNotification(
|
||||||
|
input,
|
||||||
|
"Test Email",
|
||||||
|
"<p>Hi, From Dokploy 👋</p>",
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: `${error instanceof Error ? error.message : "Unknown error"}`,
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
remove: adminProcedure
|
remove: adminProcedure
|
||||||
.input(apiFindOneNotification)
|
.input(apiFindOneNotification)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
@@ -344,6 +407,7 @@ export const notificationRouter = createTRPCRouter({
|
|||||||
telegram: true,
|
telegram: true,
|
||||||
discord: true,
|
discord: true,
|
||||||
email: true,
|
email: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -702,6 +766,7 @@ export const notificationRouter = createTRPCRouter({
|
|||||||
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
|
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
|
||||||
with: {
|
with: {
|
||||||
email: true,
|
email: true,
|
||||||
|
resend: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
removeUserById,
|
removeUserById,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
|
sendResendNotification,
|
||||||
updateUser,
|
updateUser,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
@@ -509,15 +510,16 @@ export const userRouter = createTRPCRouter({
|
|||||||
const notification = await findNotificationById(input.notificationId);
|
const notification = await findNotificationById(input.notificationId);
|
||||||
|
|
||||||
const email = notification.email;
|
const email = notification.email;
|
||||||
|
const resend = notification.resend;
|
||||||
|
|
||||||
const currentInvitation = await db.query.invitation.findFirst({
|
const currentInvitation = await db.query.invitation.findFirst({
|
||||||
where: eq(invitation.id, input.invitationId),
|
where: eq(invitation.id, input.invitationId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!email) {
|
if (!email && !resend) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "Email notification not found",
|
message: "Email provider not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,16 +534,29 @@ export const userRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendEmailNotification(
|
const htmlContent = `
|
||||||
{
|
\t\t\t\t<p>You are invited to join ${organization?.name || "organization"} on Dokploy. Click the link to accept the invitation: <a href="${inviteLink}">Accept Invitation</a></p>
|
||||||
...email,
|
\t\t\t\t`;
|
||||||
toAddresses: [currentInvitation?.email || ""],
|
|
||||||
},
|
if (email) {
|
||||||
"Invitation to join organization",
|
await sendEmailNotification(
|
||||||
`
|
{
|
||||||
<p>You are invited to join ${organization?.name || "organization"} on Dokploy. Click the link to accept the invitation: <a href="${inviteLink}">Accept Invitation</a></p>
|
...email,
|
||||||
`,
|
toAddresses: [currentInvitation?.email || ""],
|
||||||
);
|
},
|
||||||
|
"Invitation to join organization",
|
||||||
|
htmlContent,
|
||||||
|
);
|
||||||
|
} else if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
{
|
||||||
|
...resend,
|
||||||
|
toAddresses: [currentInvitation?.email || ""],
|
||||||
|
},
|
||||||
|
"Invitation to join organization",
|
||||||
|
htmlContent,
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"resend": "^6.0.2",
|
||||||
"shell-quote": "^1.8.1",
|
"shell-quote": "^1.8.1",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"ssh2": "1.15.0",
|
"ssh2": "1.15.0",
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ enum notificationType {
|
|||||||
telegram
|
telegram
|
||||||
discord
|
discord
|
||||||
email
|
email
|
||||||
|
resend
|
||||||
gotify
|
gotify
|
||||||
ntfy
|
ntfy
|
||||||
custom
|
custom
|
||||||
@@ -456,6 +457,13 @@ table email {
|
|||||||
toAddress text[] [not null]
|
toAddress text[] [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table resend {
|
||||||
|
resendId text [pk, not null]
|
||||||
|
apiKey text [not null]
|
||||||
|
fromAddress text [not null]
|
||||||
|
toAddress text[] [not null]
|
||||||
|
}
|
||||||
|
|
||||||
table environment {
|
table environment {
|
||||||
environmentId text [pk, not null]
|
environmentId text [pk, not null]
|
||||||
name text [not null]
|
name text [not null]
|
||||||
@@ -695,6 +703,7 @@ table notification {
|
|||||||
telegramId text
|
telegramId text
|
||||||
discordId text
|
discordId text
|
||||||
emailId text
|
emailId text
|
||||||
|
resendId text
|
||||||
gotifyId text
|
gotifyId text
|
||||||
ntfyId text
|
ntfyId text
|
||||||
customId text
|
customId text
|
||||||
@@ -1139,6 +1148,8 @@ ref: notification.discordId - discord.discordId
|
|||||||
|
|
||||||
ref: notification.emailId - email.emailId
|
ref: notification.emailId - email.emailId
|
||||||
|
|
||||||
|
ref: notification.resendId - resend.resendId
|
||||||
|
|
||||||
ref: notification.gotifyId - gotify.gotifyId
|
ref: notification.gotifyId - gotify.gotifyId
|
||||||
|
|
||||||
ref: notification.ntfyId - ntfy.ntfyId
|
ref: notification.ntfyId - ntfy.ntfyId
|
||||||
@@ -1197,4 +1208,4 @@ ref: volume_backup.redisId - redis.redisId
|
|||||||
|
|
||||||
ref: volume_backup.composeId - compose.composeId
|
ref: volume_backup.composeId - compose.composeId
|
||||||
|
|
||||||
ref: volume_backup.destinationId - destination.destinationId
|
ref: volume_backup.destinationId - destination.destinationId
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export const notificationType = pgEnum("notificationType", [
|
|||||||
"telegram",
|
"telegram",
|
||||||
"discord",
|
"discord",
|
||||||
"email",
|
"email",
|
||||||
|
"resend",
|
||||||
"gotify",
|
"gotify",
|
||||||
"ntfy",
|
"ntfy",
|
||||||
"pushover",
|
"pushover",
|
||||||
@@ -53,6 +54,9 @@ export const notifications = pgTable("notification", {
|
|||||||
emailId: text("emailId").references(() => email.emailId, {
|
emailId: text("emailId").references(() => email.emailId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
|
resendId: text("resendId").references(() => resend.resendId, {
|
||||||
|
onDelete: "cascade",
|
||||||
|
}),
|
||||||
gotifyId: text("gotifyId").references(() => gotify.gotifyId, {
|
gotifyId: text("gotifyId").references(() => gotify.gotifyId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
@@ -114,6 +118,16 @@ export const email = pgTable("email", {
|
|||||||
toAddresses: text("toAddress").array().notNull(),
|
toAddresses: text("toAddress").array().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const resend = pgTable("resend", {
|
||||||
|
resendId: text("resendId")
|
||||||
|
.notNull()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => nanoid()),
|
||||||
|
apiKey: text("apiKey").notNull(),
|
||||||
|
fromAddress: text("fromAddress").notNull(),
|
||||||
|
toAddresses: text("toAddress").array().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
export const gotify = pgTable("gotify", {
|
export const gotify = pgTable("gotify", {
|
||||||
gotifyId: text("gotifyId")
|
gotifyId: text("gotifyId")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -182,6 +196,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
|
|||||||
fields: [notifications.emailId],
|
fields: [notifications.emailId],
|
||||||
references: [email.emailId],
|
references: [email.emailId],
|
||||||
}),
|
}),
|
||||||
|
resend: one(resend, {
|
||||||
|
fields: [notifications.resendId],
|
||||||
|
references: [resend.resendId],
|
||||||
|
}),
|
||||||
gotify: one(gotify, {
|
gotify: one(gotify, {
|
||||||
fields: [notifications.gotifyId],
|
fields: [notifications.gotifyId],
|
||||||
references: [gotify.gotifyId],
|
references: [gotify.gotifyId],
|
||||||
@@ -335,6 +353,36 @@ export const apiTestEmailConnection = apiCreateEmail.pick({
|
|||||||
fromAddress: true,
|
fromAddress: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const apiCreateResend = notificationsSchema
|
||||||
|
.pick({
|
||||||
|
appBuildError: true,
|
||||||
|
databaseBackup: true,
|
||||||
|
volumeBackup: true,
|
||||||
|
dokployRestart: true,
|
||||||
|
name: true,
|
||||||
|
appDeploy: true,
|
||||||
|
dockerCleanup: true,
|
||||||
|
serverThreshold: true,
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
apiKey: z.string().min(1),
|
||||||
|
fromAddress: z.string().min(1),
|
||||||
|
toAddresses: z.array(z.string()).min(1),
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export const apiUpdateResend = apiCreateResend.partial().extend({
|
||||||
|
notificationId: z.string().min(1),
|
||||||
|
resendId: z.string().min(1),
|
||||||
|
organizationId: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiTestResendConnection = apiCreateResend.pick({
|
||||||
|
apiKey: true,
|
||||||
|
fromAddress: true,
|
||||||
|
toAddresses: true,
|
||||||
|
});
|
||||||
|
|
||||||
export const apiCreateGotify = notificationsSchema
|
export const apiCreateGotify = notificationsSchema
|
||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
@@ -534,6 +582,7 @@ export const apiSendTest = notificationsSchema
|
|||||||
username: z.string(),
|
username: z.string(),
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
toAddresses: z.array(z.string()),
|
toAddresses: z.array(z.string()),
|
||||||
|
apiKey: z.string(),
|
||||||
serverUrl: z.string(),
|
serverUrl: z.string(),
|
||||||
topic: z.string(),
|
topic: z.string(),
|
||||||
appToken: z.string(),
|
appToken: z.string(),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
type apiCreateLark,
|
type apiCreateLark,
|
||||||
type apiCreateNtfy,
|
type apiCreateNtfy,
|
||||||
type apiCreatePushover,
|
type apiCreatePushover,
|
||||||
|
type apiCreateResend,
|
||||||
type apiCreateSlack,
|
type apiCreateSlack,
|
||||||
type apiCreateTelegram,
|
type apiCreateTelegram,
|
||||||
type apiUpdateCustom,
|
type apiUpdateCustom,
|
||||||
@@ -16,6 +17,7 @@ import {
|
|||||||
type apiUpdateLark,
|
type apiUpdateLark,
|
||||||
type apiUpdateNtfy,
|
type apiUpdateNtfy,
|
||||||
type apiUpdatePushover,
|
type apiUpdatePushover,
|
||||||
|
type apiUpdateResend,
|
||||||
type apiUpdateSlack,
|
type apiUpdateSlack,
|
||||||
type apiUpdateTelegram,
|
type apiUpdateTelegram,
|
||||||
custom,
|
custom,
|
||||||
@@ -26,6 +28,7 @@ import {
|
|||||||
notifications,
|
notifications,
|
||||||
ntfy,
|
ntfy,
|
||||||
pushover,
|
pushover,
|
||||||
|
resend,
|
||||||
slack,
|
slack,
|
||||||
telegram,
|
telegram,
|
||||||
} from "@dokploy/server/db/schema";
|
} from "@dokploy/server/db/schema";
|
||||||
@@ -412,6 +415,100 @@ export const updateEmailNotification = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createResendNotification = async (
|
||||||
|
input: typeof apiCreateResend._type,
|
||||||
|
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: typeof apiUpdateResend._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,
|
||||||
|
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 (
|
export const createGotifyNotification = async (
|
||||||
input: typeof apiCreateGotify._type,
|
input: typeof apiCreateGotify._type,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
@@ -693,6 +790,7 @@ export const findNotificationById = async (notificationId: string) => {
|
|||||||
telegram: true,
|
telegram: true,
|
||||||
discord: true,
|
discord: true,
|
||||||
email: true,
|
email: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -45,6 +46,7 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -56,6 +58,7 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
|
resend,
|
||||||
discord,
|
discord,
|
||||||
telegram,
|
telegram,
|
||||||
slack,
|
slack,
|
||||||
@@ -66,7 +69,7 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
pushover,
|
pushover,
|
||||||
} = notification;
|
} = notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const template = await renderAsync(
|
const template = await renderAsync(
|
||||||
BuildFailedEmail({
|
BuildFailedEmail({
|
||||||
projectName,
|
projectName,
|
||||||
@@ -77,11 +80,22 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
date: date.toLocaleString(),
|
date: date.toLocaleString(),
|
||||||
}),
|
}),
|
||||||
).catch();
|
).catch();
|
||||||
await sendEmailNotification(
|
|
||||||
email,
|
if (email) {
|
||||||
"Build failed for dokploy",
|
await sendEmailNotification(
|
||||||
template,
|
email,
|
||||||
);
|
"Build failed for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
resend,
|
||||||
|
"Build failed for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -48,6 +49,7 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -59,6 +61,7 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
|
resend,
|
||||||
discord,
|
discord,
|
||||||
telegram,
|
telegram,
|
||||||
slack,
|
slack,
|
||||||
@@ -69,7 +72,7 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
pushover,
|
pushover,
|
||||||
} = notification;
|
} = notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const template = await renderAsync(
|
const template = await renderAsync(
|
||||||
BuildSuccessEmail({
|
BuildSuccessEmail({
|
||||||
projectName,
|
projectName,
|
||||||
@@ -80,11 +83,22 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
environmentName,
|
environmentName,
|
||||||
}),
|
}),
|
||||||
).catch();
|
).catch();
|
||||||
await sendEmailNotification(
|
|
||||||
email,
|
if (email) {
|
||||||
"Build success for dokploy",
|
await sendEmailNotification(
|
||||||
template,
|
email,
|
||||||
);
|
"Build success for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
resend,
|
||||||
|
"Build success for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -45,6 +46,7 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -56,6 +58,7 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
|
resend,
|
||||||
discord,
|
discord,
|
||||||
telegram,
|
telegram,
|
||||||
slack,
|
slack,
|
||||||
@@ -66,7 +69,7 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
pushover,
|
pushover,
|
||||||
} = notification;
|
} = notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const template = await renderAsync(
|
const template = await renderAsync(
|
||||||
DatabaseBackupEmail({
|
DatabaseBackupEmail({
|
||||||
projectName,
|
projectName,
|
||||||
@@ -77,11 +80,22 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
date: date.toLocaleString(),
|
date: date.toLocaleString(),
|
||||||
}),
|
}),
|
||||||
).catch();
|
).catch();
|
||||||
await sendEmailNotification(
|
|
||||||
email,
|
if (email) {
|
||||||
"Database backup for dokploy",
|
await sendEmailNotification(
|
||||||
template,
|
email,
|
||||||
);
|
"Database backup for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
resend,
|
||||||
|
"Database backup for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -32,6 +33,7 @@ export const sendDockerCleanupNotifications = async (
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -43,6 +45,7 @@ export const sendDockerCleanupNotifications = async (
|
|||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
|
resend,
|
||||||
discord,
|
discord,
|
||||||
telegram,
|
telegram,
|
||||||
slack,
|
slack,
|
||||||
@@ -53,16 +56,26 @@ export const sendDockerCleanupNotifications = async (
|
|||||||
pushover,
|
pushover,
|
||||||
} = notification;
|
} = notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const template = await renderAsync(
|
const template = await renderAsync(
|
||||||
DockerCleanupEmail({ message, date: date.toLocaleString() }),
|
DockerCleanupEmail({ message, date: date.toLocaleString() }),
|
||||||
).catch();
|
).catch();
|
||||||
|
|
||||||
await sendEmailNotification(
|
if (email) {
|
||||||
email,
|
await sendEmailNotification(
|
||||||
"Docker cleanup for dokploy",
|
email,
|
||||||
template,
|
"Docker cleanup for dokploy",
|
||||||
);
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
resend,
|
||||||
|
"Docker cleanup for dokploy",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -26,6 +27,7 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
custom: true,
|
custom: true,
|
||||||
@@ -37,6 +39,7 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
|
resend,
|
||||||
discord,
|
discord,
|
||||||
telegram,
|
telegram,
|
||||||
slack,
|
slack,
|
||||||
@@ -48,16 +51,26 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
} = notification;
|
} = notification;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const template = await renderAsync(
|
const template = await renderAsync(
|
||||||
DokployRestartEmail({ date: date.toLocaleString() }),
|
DokployRestartEmail({ date: date.toLocaleString() }),
|
||||||
).catch();
|
).catch();
|
||||||
|
|
||||||
await sendEmailNotification(
|
if (email) {
|
||||||
email,
|
await sendEmailNotification(
|
||||||
"Dokploy Server Restarted",
|
email,
|
||||||
template,
|
"Dokploy Server Restarted",
|
||||||
);
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(
|
||||||
|
resend,
|
||||||
|
"Dokploy Server Restarted",
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import type {
|
|||||||
lark,
|
lark,
|
||||||
ntfy,
|
ntfy,
|
||||||
pushover,
|
pushover,
|
||||||
|
resend,
|
||||||
slack,
|
slack,
|
||||||
telegram,
|
telegram,
|
||||||
} from "@dokploy/server/db/schema";
|
} from "@dokploy/server/db/schema";
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
|
import { Resend } from "resend";
|
||||||
|
|
||||||
export const sendEmailNotification = async (
|
export const sendEmailNotification = async (
|
||||||
connection: typeof email.$inferInsert,
|
connection: typeof email.$inferInsert,
|
||||||
@@ -46,6 +48,32 @@ export const sendEmailNotification = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sendResendNotification = async (
|
||||||
|
connection: typeof resend.$inferInsert,
|
||||||
|
subject: string,
|
||||||
|
htmlContent: string,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const client = new Resend(connection.apiKey);
|
||||||
|
|
||||||
|
const result = await client.emails.send({
|
||||||
|
from: connection.fromAddress,
|
||||||
|
to: connection.toAddresses,
|
||||||
|
subject,
|
||||||
|
html: htmlContent,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error.message);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to send Resend notification ${err instanceof Error ? err.message : "Unknown error"}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const sendDiscordNotification = async (
|
export const sendDiscordNotification = async (
|
||||||
connection: typeof discord.$inferInsert,
|
connection: typeof discord.$inferInsert,
|
||||||
embed: any,
|
embed: any,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
sendNtfyNotification,
|
sendNtfyNotification,
|
||||||
sendPushoverNotification,
|
sendPushoverNotification,
|
||||||
|
sendResendNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
@@ -52,6 +53,7 @@ export const sendVolumeBackupNotifications = async ({
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
resend: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
pushover: true,
|
pushover: true,
|
||||||
@@ -59,10 +61,10 @@ export const sendVolumeBackupNotifications = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, pushover } =
|
const { email, resend, discord, telegram, slack, gotify, ntfy, pushover } =
|
||||||
notification;
|
notification;
|
||||||
|
|
||||||
if (email) {
|
if (email || resend) {
|
||||||
const subject = `Volume Backup ${type === "success" ? "Successful" : "Failed"} - ${applicationName}`;
|
const subject = `Volume Backup ${type === "success" ? "Successful" : "Failed"} - ${applicationName}`;
|
||||||
const htmlContent = await renderAsync(
|
const htmlContent = await renderAsync(
|
||||||
VolumeBackupEmail({
|
VolumeBackupEmail({
|
||||||
@@ -76,7 +78,12 @@ export const sendVolumeBackupNotifications = async ({
|
|||||||
date: date.toISOString(),
|
date: date.toISOString(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
await sendEmailNotification(email, subject, htmlContent);
|
if (email) {
|
||||||
|
await sendEmailNotification(email, subject, htmlContent);
|
||||||
|
}
|
||||||
|
if (resend) {
|
||||||
|
await sendResendNotification(resend, subject, htmlContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
|
|||||||
Generated
+50
@@ -717,6 +717,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0([email protected])
|
version: 18.2.0([email protected])
|
||||||
|
resend:
|
||||||
|
specifier: ^6.0.2
|
||||||
|
version: 6.8.0(@react-email/[email protected]([email protected]([email protected]))([email protected]))
|
||||||
semver:
|
semver:
|
||||||
specifier: 7.7.3
|
specifier: 7.7.3
|
||||||
version: 7.7.3
|
version: 7.7.3
|
||||||
@@ -4030,6 +4033,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
'@stablelib/[email protected]':
|
||||||
|
resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==}
|
||||||
|
|
||||||
'@standard-schema/[email protected]':
|
'@standard-schema/[email protected]':
|
||||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||||
|
|
||||||
@@ -5574,6 +5580,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==}
|
resolution: {integrity: sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -7378,6 +7387,15 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-fDOXGqafQfQXl8nXe93wr93pus8tW7YPpowenE3SmG7dJJf0hH3xUWm3xqacnPvhqjCQTJH9xETg07rmUeSuqQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@react-email/render': '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@react-email/render':
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||||
|
|
||||||
@@ -7606,6 +7624,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -7705,6 +7726,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-K8DPPSZaW/XqXiz1kEyzSHYgmGLnhB43nQCMeKjWGCUpLIpAMMM8kx3rVVOSm6Bo6EHyK1RQLPT4R06skM/MlQ==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-4bO+dhBbasP485Ak67o46cWNVUnV0/92ypb2997bhvxTO2M+IuQZM1ilkN/7nSaiGuxDKJhkuL54I35PVI3AAw==}
|
resolution: {integrity: sha512-4bO+dhBbasP485Ak67o46cWNVUnV0/92ypb2997bhvxTO2M+IuQZM1ilkN/7nSaiGuxDKJhkuL54I35PVI3AAw==}
|
||||||
|
|
||||||
@@ -7982,6 +8006,10 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -11495,6 +11523,8 @@ snapshots:
|
|||||||
|
|
||||||
'@sindresorhus/[email protected]': {}
|
'@sindresorhus/[email protected]': {}
|
||||||
|
|
||||||
|
'@stablelib/[email protected]': {}
|
||||||
|
|
||||||
'@standard-schema/[email protected]': {}
|
'@standard-schema/[email protected]': {}
|
||||||
|
|
||||||
'@stepperize/[email protected]([email protected]([email protected]))([email protected])':
|
'@stepperize/[email protected]([email protected]([email protected]))([email protected])':
|
||||||
@@ -13320,6 +13350,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
strnum: 2.1.2
|
strnum: 2.1.2
|
||||||
@@ -15348,6 +15380,12 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected](@react-email/[email protected]([email protected]([email protected]))([email protected])):
|
||||||
|
dependencies:
|
||||||
|
svix: 1.84.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@react-email/render': 0.0.16([email protected]([email protected]))([email protected])
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -15613,6 +15651,11 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@stablelib/base64': 1.0.1
|
||||||
|
fast-sha256: 1.3.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -15708,6 +15751,11 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
standardwebhooks: 1.0.0
|
||||||
|
uuid: 10.0.0
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime-corejs3': 7.27.3
|
'@babel/runtime-corejs3': 7.27.3
|
||||||
@@ -16053,6 +16101,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|||||||
+12
-1
@@ -69,6 +69,7 @@ enum notificationType {
|
|||||||
telegram
|
telegram
|
||||||
discord
|
discord
|
||||||
email
|
email
|
||||||
|
resend
|
||||||
gotify
|
gotify
|
||||||
ntfy
|
ntfy
|
||||||
custom
|
custom
|
||||||
@@ -455,6 +456,13 @@ table email {
|
|||||||
toAddress text[] [not null]
|
toAddress text[] [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table resend {
|
||||||
|
resendId text [pk, not null]
|
||||||
|
apiKey text [not null]
|
||||||
|
fromAddress text [not null]
|
||||||
|
toAddress text[] [not null]
|
||||||
|
}
|
||||||
|
|
||||||
table environment {
|
table environment {
|
||||||
environmentId text [pk, not null]
|
environmentId text [pk, not null]
|
||||||
name text [not null]
|
name text [not null]
|
||||||
@@ -691,6 +699,7 @@ table notification {
|
|||||||
telegramId text
|
telegramId text
|
||||||
discordId text
|
discordId text
|
||||||
emailId text
|
emailId text
|
||||||
|
resendId text
|
||||||
gotifyId text
|
gotifyId text
|
||||||
ntfyId text
|
ntfyId text
|
||||||
customId text
|
customId text
|
||||||
@@ -1133,6 +1142,8 @@ ref: notification.discordId - discord.discordId
|
|||||||
|
|
||||||
ref: notification.emailId - email.emailId
|
ref: notification.emailId - email.emailId
|
||||||
|
|
||||||
|
ref: notification.resendId - resend.resendId
|
||||||
|
|
||||||
ref: notification.gotifyId - gotify.gotifyId
|
ref: notification.gotifyId - gotify.gotifyId
|
||||||
|
|
||||||
ref: notification.ntfyId - ntfy.ntfyId
|
ref: notification.ntfyId - ntfy.ntfyId
|
||||||
@@ -1191,4 +1202,4 @@ ref: volume_backup.redisId - redis.redisId
|
|||||||
|
|
||||||
ref: volume_backup.composeId - compose.composeId
|
ref: volume_backup.composeId - compose.composeId
|
||||||
|
|
||||||
ref: volume_backup.destinationId - destination.destinationId
|
ref: volume_backup.destinationId - destination.destinationId
|
||||||
|
|||||||
Reference in New Issue
Block a user