mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-08 21:15:17 +00:00
Merge pull request #2699 from ChristoferMendes/feature/add-custom-webhook-notification-provider
feat(notifications): add custom webhook notification provider
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
import type { ApplicationNested } from "@dokploy/server/utils/builders";
|
import type { ApplicationNested } from "@dokploy/server/utils/builders";
|
||||||
import { mechanizeDockerContainer } from "@dokploy/server/utils/builders";
|
import { mechanizeDockerContainer } from "@dokploy/server/utils/builders";
|
||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
type MockCreateServiceOptions = {
|
type MockCreateServiceOptions = {
|
||||||
TaskTemplate?: {
|
TaskTemplate?: {
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
import type { findEnvironmentsByProjectId } from "@dokploy/server";
|
import type { findEnvironmentsByProjectId } from "@dokploy/server";
|
||||||
import {
|
import { ChevronDownIcon, PencilIcon, PlusIcon, TrashIcon } from "lucide-react";
|
||||||
ChevronDownIcon,
|
|
||||||
PencilIcon,
|
|
||||||
PlusIcon,
|
|
||||||
Terminal,
|
|
||||||
TrashIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { EnvironmentVariables } from "@/components/dashboard/project/environment-variables";
|
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -246,20 +239,6 @@ export const AdvancedEnvironmentSelector = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
{/* Action buttons for non-production environments */}
|
|
||||||
{/* <EnvironmentVariables environmentId={environment.environmentId}>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-6 w-6 p-0"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Terminal className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</EnvironmentVariables> */}
|
|
||||||
{environment.name !== "production" && (
|
{environment.name !== "production" && (
|
||||||
<div className="flex items-center gap-1 px-2">
|
<div className="flex items-center gap-1 px-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle, Mail, PenBoxIcon, PlusIcon } from "lucide-react";
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
Mail,
|
||||||
|
PenBoxIcon,
|
||||||
|
PlusIcon,
|
||||||
|
Trash2,
|
||||||
|
} from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useFieldArray, useForm } from "react-hook-form";
|
import { useFieldArray, useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -108,6 +114,21 @@ export const notificationSchema = z.discriminatedUnion("type", [
|
|||||||
priority: z.number().min(1).max(5).default(3),
|
priority: z.number().min(1).max(5).default(3),
|
||||||
})
|
})
|
||||||
.merge(notificationBaseSchema),
|
.merge(notificationBaseSchema),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
type: z.literal("custom"),
|
||||||
|
endpoint: z.string().min(1, { message: "Endpoint URL is required" }),
|
||||||
|
headers: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
|
})
|
||||||
|
.merge(notificationBaseSchema),
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal("lark"),
|
type: z.literal("lark"),
|
||||||
@@ -145,6 +166,10 @@ export const notificationsMap = {
|
|||||||
icon: <NtfyIcon />,
|
icon: <NtfyIcon />,
|
||||||
label: "ntfy",
|
label: "ntfy",
|
||||||
},
|
},
|
||||||
|
custom: {
|
||||||
|
icon: <PenBoxIcon size={29} className="text-muted-foreground" />,
|
||||||
|
label: "Custom",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NotificationSchema = z.infer<typeof notificationSchema>;
|
export type NotificationSchema = z.infer<typeof notificationSchema>;
|
||||||
@@ -180,6 +205,13 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
api.notification.testNtfyConnection.useMutation();
|
api.notification.testNtfyConnection.useMutation();
|
||||||
const { mutateAsync: testLarkConnection, isLoading: isLoadingLark } =
|
const { mutateAsync: testLarkConnection, isLoading: isLoadingLark } =
|
||||||
api.notification.testLarkConnection.useMutation();
|
api.notification.testLarkConnection.useMutation();
|
||||||
|
|
||||||
|
const { mutateAsync: testCustomConnection, isLoading: isLoadingCustom } =
|
||||||
|
api.notification.testCustomConnection.useMutation();
|
||||||
|
|
||||||
|
const customMutation = notificationId
|
||||||
|
? api.notification.updateCustom.useMutation()
|
||||||
|
: api.notification.createCustom.useMutation();
|
||||||
const slackMutation = notificationId
|
const slackMutation = notificationId
|
||||||
? api.notification.updateSlack.useMutation()
|
? api.notification.updateSlack.useMutation()
|
||||||
: api.notification.createSlack.useMutation();
|
: api.notification.createSlack.useMutation();
|
||||||
@@ -218,6 +250,15 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
name: "toAddresses" as never,
|
name: "toAddresses" as never,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
fields: headerFields,
|
||||||
|
append: appendHeader,
|
||||||
|
remove: removeHeader,
|
||||||
|
} = useFieldArray({
|
||||||
|
control: form.control,
|
||||||
|
name: "headers" as never,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (type === "email" && fields.length === 0) {
|
if (type === "email" && fields.length === 0) {
|
||||||
append("");
|
append("");
|
||||||
@@ -330,6 +371,26 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
dockerCleanup: notification.dockerCleanup,
|
dockerCleanup: notification.dockerCleanup,
|
||||||
serverThreshold: notification.serverThreshold,
|
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
|
||||||
|
? Object.entries(notification.custom.headers).map(
|
||||||
|
([key, value]) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
: [],
|
||||||
|
name: notification.name,
|
||||||
|
dockerCleanup: notification.dockerCleanup,
|
||||||
|
serverThreshold: notification.serverThreshold,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
form.reset();
|
form.reset();
|
||||||
@@ -344,6 +405,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
gotify: gotifyMutation,
|
gotify: gotifyMutation,
|
||||||
ntfy: ntfyMutation,
|
ntfy: ntfyMutation,
|
||||||
lark: larkMutation,
|
lark: larkMutation,
|
||||||
|
custom: customMutation,
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: NotificationSchema) => {
|
const onSubmit = async (data: NotificationSchema) => {
|
||||||
@@ -467,6 +529,32 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
larkId: notification?.larkId || "",
|
larkId: notification?.larkId || "",
|
||||||
serverThreshold: serverThreshold,
|
serverThreshold: serverThreshold,
|
||||||
});
|
});
|
||||||
|
} else if (data.type === "custom") {
|
||||||
|
// Convert headers array to object
|
||||||
|
const headersRecord =
|
||||||
|
data.headers && data.headers.length > 0
|
||||||
|
? data.headers.reduce(
|
||||||
|
(acc, { key, value }) => {
|
||||||
|
if (key.trim()) acc[key] = value;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
promise = customMutation.mutateAsync({
|
||||||
|
appBuildError: appBuildError,
|
||||||
|
appDeploy: appDeploy,
|
||||||
|
dokployRestart: dokployRestart,
|
||||||
|
databaseBackup: databaseBackup,
|
||||||
|
endpoint: data.endpoint,
|
||||||
|
headers: headersRecord,
|
||||||
|
name: data.name,
|
||||||
|
dockerCleanup: dockerCleanup,
|
||||||
|
serverThreshold: serverThreshold,
|
||||||
|
notificationId: notificationId || "",
|
||||||
|
customId: notification?.customId || "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promise) {
|
if (promise) {
|
||||||
@@ -1057,7 +1145,92 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{type === "custom" && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="endpoint"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Webhook URL</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="https://api.example.com/webhook"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
The URL where POST requests will be sent with
|
||||||
|
notification data.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div>
|
||||||
|
<FormLabel>Headers</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Optional. Custom headers for your POST request (e.g.,
|
||||||
|
Authorization, Content-Type).
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{headerFields.map((field, index) => (
|
||||||
|
<div
|
||||||
|
key={field.id}
|
||||||
|
className="flex items-center gap-2 p-2 border rounded-md bg-muted/50"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={`headers.${index}.key` as never}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="Key" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={`headers.${index}.value` as never}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-[2]">
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="Value" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => removeHeader(index)}
|
||||||
|
className="text-red-500 hover:text-red-700 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => appendHeader({ key: "", value: "" })}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4 mr-2" />
|
||||||
|
Add header
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{type === "lark" && (
|
{type === "lark" && (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -1250,7 +1423,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
isLoadingEmail ||
|
isLoadingEmail ||
|
||||||
isLoadingGotify ||
|
isLoadingGotify ||
|
||||||
isLoadingNtfy ||
|
isLoadingNtfy ||
|
||||||
isLoadingLark
|
isLoadingLark ||
|
||||||
|
isLoadingCustom
|
||||||
}
|
}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1304,6 +1478,21 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
|||||||
await testLarkConnection({
|
await testLarkConnection({
|
||||||
webhookUrl: data.webhookUrl,
|
webhookUrl: data.webhookUrl,
|
||||||
});
|
});
|
||||||
|
} else if (data.type === "custom") {
|
||||||
|
const headersRecord =
|
||||||
|
data.headers && data.headers.length > 0
|
||||||
|
? data.headers.reduce(
|
||||||
|
(acc, { key, value }) => {
|
||||||
|
if (key.trim()) acc[key] = value;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
await testCustomConnection({
|
||||||
|
endpoint: data.endpoint,
|
||||||
|
headers: headersRecord,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
toast.success("Connection Success");
|
toast.success("Connection Success");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Bell, Loader2, Mail, Trash2 } from "lucide-react";
|
import { Bell, Loader2, Mail, PenBoxIcon, Trash2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
DiscordIcon,
|
DiscordIcon,
|
||||||
@@ -96,6 +96,11 @@ export const ShowNotifications = () => {
|
|||||||
<NtfyIcon className="size-6" />
|
<NtfyIcon className="size-6" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{notification.notificationType === "custom" && (
|
||||||
|
<div className="flex items-center justify-center rounded-lg ">
|
||||||
|
<PenBoxIcon className="size-6 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{notification.notificationType === "lark" && (
|
{notification.notificationType === "lark" && (
|
||||||
<div className="flex items-center justify-center rounded-lg">
|
<div className="flex items-center justify-center rounded-lg">
|
||||||
<LarkIcon className="size-7 text-muted-foreground" />
|
<LarkIcon className="size-7 text-muted-foreground" />
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TYPE "public"."notificationType" ADD VALUE 'custom' BEFORE 'lark';--> statement-breakpoint
|
||||||
|
CREATE TABLE "custom" (
|
||||||
|
"customId" text PRIMARY KEY NOT NULL,
|
||||||
|
"endpoint" text NOT NULL,
|
||||||
|
"headers" jsonb
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "notification" ADD COLUMN "customId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "notification" ADD CONSTRAINT "notification_customId_custom_customId_fk" FOREIGN KEY ("customId") REFERENCES "public"."custom"("customId") ON DELETE cascade ON UPDATE no action;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -904,6 +904,13 @@
|
|||||||
"when": 1765101709413,
|
"when": 1765101709413,
|
||||||
"tag": "0128_hard_falcon",
|
"tag": "0128_hard_falcon",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 129,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1765136384035,
|
||||||
|
"tag": "0129_pale_roughhouse",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
createMount,
|
createMount,
|
||||||
deployMariadb,
|
deployMariadb,
|
||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findMariadbById,
|
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMariadbById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
rebuildDatabase,
|
rebuildDatabase,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
createMount,
|
createMount,
|
||||||
deployMongo,
|
deployMongo,
|
||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findMongoById,
|
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMongoById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
rebuildDatabase,
|
rebuildDatabase,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
createCustomNotification,
|
||||||
createDiscordNotification,
|
createDiscordNotification,
|
||||||
createEmailNotification,
|
createEmailNotification,
|
||||||
createGotifyNotification,
|
createGotifyNotification,
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
findNotificationById,
|
findNotificationById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
removeNotificationById,
|
removeNotificationById,
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -17,6 +19,7 @@ import {
|
|||||||
sendServerThresholdNotifications,
|
sendServerThresholdNotifications,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
sendTelegramNotification,
|
sendTelegramNotification,
|
||||||
|
updateCustomNotification,
|
||||||
updateDiscordNotification,
|
updateDiscordNotification,
|
||||||
updateEmailNotification,
|
updateEmailNotification,
|
||||||
updateGotifyNotification,
|
updateGotifyNotification,
|
||||||
@@ -36,6 +39,7 @@ import {
|
|||||||
} from "@/server/api/trpc";
|
} from "@/server/api/trpc";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import {
|
import {
|
||||||
|
apiCreateCustom,
|
||||||
apiCreateDiscord,
|
apiCreateDiscord,
|
||||||
apiCreateEmail,
|
apiCreateEmail,
|
||||||
apiCreateGotify,
|
apiCreateGotify,
|
||||||
@@ -44,6 +48,7 @@ import {
|
|||||||
apiCreateSlack,
|
apiCreateSlack,
|
||||||
apiCreateTelegram,
|
apiCreateTelegram,
|
||||||
apiFindOneNotification,
|
apiFindOneNotification,
|
||||||
|
apiTestCustomConnection,
|
||||||
apiTestDiscordConnection,
|
apiTestDiscordConnection,
|
||||||
apiTestEmailConnection,
|
apiTestEmailConnection,
|
||||||
apiTestGotifyConnection,
|
apiTestGotifyConnection,
|
||||||
@@ -51,6 +56,7 @@ import {
|
|||||||
apiTestNtfyConnection,
|
apiTestNtfyConnection,
|
||||||
apiTestSlackConnection,
|
apiTestSlackConnection,
|
||||||
apiTestTelegramConnection,
|
apiTestTelegramConnection,
|
||||||
|
apiUpdateCustom,
|
||||||
apiUpdateDiscord,
|
apiUpdateDiscord,
|
||||||
apiUpdateEmail,
|
apiUpdateEmail,
|
||||||
apiUpdateGotify,
|
apiUpdateGotify,
|
||||||
@@ -334,6 +340,7 @@ export const notificationRouter = createTRPCRouter({
|
|||||||
email: true,
|
email: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
orderBy: desc(notifications.createdAt),
|
orderBy: desc(notifications.createdAt),
|
||||||
@@ -518,6 +525,59 @@ export const notificationRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
createCustom: adminProcedure
|
||||||
|
.input(apiCreateCustom)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
return await createCustomNotification(
|
||||||
|
input,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error creating the notification",
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
updateCustom: adminProcedure
|
||||||
|
.input(apiUpdateCustom)
|
||||||
|
.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 updateCustomNotification({
|
||||||
|
...input,
|
||||||
|
organizationId: ctx.session.activeOrganizationId,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
testCustomConnection: adminProcedure
|
||||||
|
.input(apiTestCustomConnection)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
try {
|
||||||
|
await sendCustomNotification(input, {
|
||||||
|
title: "Test Notification",
|
||||||
|
message: "Hi, From Dokploy 👋",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: `${error instanceof Error ? error.message : "Unknown error"}`,
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
createLark: adminProcedure
|
createLark: adminProcedure
|
||||||
.input(apiCreateLark)
|
.input(apiCreateLark)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"switch:prod": "node scripts/switchToDist.js",
|
"switch:prod": "node scripts/switchToDist.js",
|
||||||
"dev": "rm -rf ./dist && pnpm esbuild && tsc --emitDeclarationOnly --outDir dist -p tsconfig.server.json",
|
"dev": "rm -rf ./dist && pnpm esbuild && tsc --emitDeclarationOnly --outDir dist -p tsconfig.server.json",
|
||||||
"esbuild": "tsx ./esbuild.config.ts && tsc --project tsconfig.server.json --emitDeclarationOnly ",
|
"esbuild": "tsx ./esbuild.config.ts && tsc --project tsconfig.server.json --emitDeclarationOnly ",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"dbml:generate": "npx tsx src/db/schema/dbml.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "^2.0.5",
|
"@ai-sdk/anthropic": "^2.0.5",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,12 @@
|
|||||||
import { relations } from "drizzle-orm";
|
import { relations } from "drizzle-orm";
|
||||||
import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
import {
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgEnum,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -12,6 +19,7 @@ export const notificationType = pgEnum("notificationType", [
|
|||||||
"email",
|
"email",
|
||||||
"gotify",
|
"gotify",
|
||||||
"ntfy",
|
"ntfy",
|
||||||
|
"custom",
|
||||||
"lark",
|
"lark",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -50,6 +58,9 @@ export const notifications = pgTable("notification", {
|
|||||||
ntfyId: text("ntfyId").references(() => ntfy.ntfyId, {
|
ntfyId: text("ntfyId").references(() => ntfy.ntfyId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
|
customId: text("customId").references(() => custom.customId, {
|
||||||
|
onDelete: "cascade",
|
||||||
|
}),
|
||||||
larkId: text("larkId").references(() => lark.larkId, {
|
larkId: text("larkId").references(() => lark.larkId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
@@ -121,6 +132,15 @@ export const ntfy = pgTable("ntfy", {
|
|||||||
priority: integer("priority").notNull().default(3),
|
priority: integer("priority").notNull().default(3),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const custom = pgTable("custom", {
|
||||||
|
customId: text("customId")
|
||||||
|
.notNull()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => nanoid()),
|
||||||
|
endpoint: text("endpoint").notNull(),
|
||||||
|
headers: jsonb("headers").$type<Record<string, string>>(),
|
||||||
|
});
|
||||||
|
|
||||||
export const lark = pgTable("lark", {
|
export const lark = pgTable("lark", {
|
||||||
larkId: text("larkId")
|
larkId: text("larkId")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -154,6 +174,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
|
|||||||
fields: [notifications.ntfyId],
|
fields: [notifications.ntfyId],
|
||||||
references: [ntfy.ntfyId],
|
references: [ntfy.ntfyId],
|
||||||
}),
|
}),
|
||||||
|
custom: one(custom, {
|
||||||
|
fields: [notifications.customId],
|
||||||
|
references: [custom.customId],
|
||||||
|
}),
|
||||||
lark: one(lark, {
|
lark: one(lark, {
|
||||||
fields: [notifications.larkId],
|
fields: [notifications.larkId],
|
||||||
references: [lark.larkId],
|
references: [lark.larkId],
|
||||||
@@ -362,6 +386,32 @@ export const apiFindOneNotification = notificationsSchema
|
|||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
|
|
||||||
|
export const apiCreateCustom = notificationsSchema
|
||||||
|
.pick({
|
||||||
|
appBuildError: true,
|
||||||
|
databaseBackup: true,
|
||||||
|
dokployRestart: true,
|
||||||
|
name: true,
|
||||||
|
appDeploy: true,
|
||||||
|
dockerCleanup: true,
|
||||||
|
serverThreshold: true,
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
endpoint: z.string().min(1),
|
||||||
|
headers: z.record(z.string()).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiUpdateCustom = apiCreateCustom.partial().extend({
|
||||||
|
notificationId: z.string().min(1),
|
||||||
|
customId: z.string().min(1),
|
||||||
|
organizationId: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiTestCustomConnection = z.object({
|
||||||
|
endpoint: z.string().min(1),
|
||||||
|
headers: z.record(z.string()).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
export const apiCreateLark = notificationsSchema
|
export const apiCreateLark = notificationsSchema
|
||||||
.pick({
|
.pick({
|
||||||
appBuildError: true,
|
appBuildError: true,
|
||||||
@@ -404,5 +454,7 @@ export const apiSendTest = notificationsSchema
|
|||||||
appToken: z.string(),
|
appToken: z.string(),
|
||||||
accessToken: z.string().optional(),
|
accessToken: z.string().optional(),
|
||||||
priority: z.number(),
|
priority: z.number(),
|
||||||
|
endpoint: z.string(),
|
||||||
|
headers: z.string(),
|
||||||
})
|
})
|
||||||
.partial();
|
.partial();
|
||||||
|
|||||||
@@ -5,17 +5,24 @@ enum applicationStatus {
|
|||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum backupType {
|
||||||
|
database
|
||||||
|
compose
|
||||||
|
}
|
||||||
|
|
||||||
enum buildType {
|
enum buildType {
|
||||||
dockerfile
|
dockerfile
|
||||||
heroku_buildpacks
|
heroku_buildpacks
|
||||||
paketo_buildpacks
|
paketo_buildpacks
|
||||||
nixpacks
|
nixpacks
|
||||||
static
|
static
|
||||||
|
railpack
|
||||||
}
|
}
|
||||||
|
|
||||||
enum certificateType {
|
enum certificateType {
|
||||||
letsencrypt
|
letsencrypt
|
||||||
none
|
none
|
||||||
|
custom
|
||||||
}
|
}
|
||||||
|
|
||||||
enum composeType {
|
enum composeType {
|
||||||
@@ -28,6 +35,7 @@ enum databaseType {
|
|||||||
mariadb
|
mariadb
|
||||||
mysql
|
mysql
|
||||||
mongo
|
mongo
|
||||||
|
"web-server"
|
||||||
}
|
}
|
||||||
|
|
||||||
enum deploymentStatus {
|
enum deploymentStatus {
|
||||||
@@ -61,6 +69,8 @@ enum notificationType {
|
|||||||
discord
|
discord
|
||||||
email
|
email
|
||||||
gotify
|
gotify
|
||||||
|
ntfy
|
||||||
|
custom
|
||||||
}
|
}
|
||||||
|
|
||||||
enum protocolType {
|
enum protocolType {
|
||||||
@@ -68,14 +78,21 @@ enum protocolType {
|
|||||||
udp
|
udp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum publishModeType {
|
||||||
|
ingress
|
||||||
|
host
|
||||||
|
}
|
||||||
|
|
||||||
enum RegistryType {
|
enum RegistryType {
|
||||||
selfHosted
|
selfHosted
|
||||||
cloud
|
cloud
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Roles {
|
enum scheduleType {
|
||||||
admin
|
application
|
||||||
user
|
compose
|
||||||
|
server
|
||||||
|
"dokploy-server"
|
||||||
}
|
}
|
||||||
|
|
||||||
enum serverStatus {
|
enum serverStatus {
|
||||||
@@ -93,6 +110,11 @@ enum serviceType {
|
|||||||
compose
|
compose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum shellType {
|
||||||
|
bash
|
||||||
|
sh
|
||||||
|
}
|
||||||
|
|
||||||
enum sourceType {
|
enum sourceType {
|
||||||
docker
|
docker
|
||||||
git
|
git
|
||||||
@@ -112,6 +134,11 @@ enum sourceTypeCompose {
|
|||||||
raw
|
raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum triggerType {
|
||||||
|
push
|
||||||
|
tag
|
||||||
|
}
|
||||||
|
|
||||||
table account {
|
table account {
|
||||||
id text [pk, not null]
|
id text [pk, not null]
|
||||||
account_id text [not null]
|
account_id text [not null]
|
||||||
@@ -133,7 +160,39 @@ table account {
|
|||||||
confirmationExpiresAt text
|
confirmationExpiresAt text
|
||||||
}
|
}
|
||||||
|
|
||||||
table admin {
|
table ai {
|
||||||
|
aiId text [pk, not null]
|
||||||
|
name text [not null]
|
||||||
|
apiUrl text [not null]
|
||||||
|
apiKey text [not null]
|
||||||
|
model text [not null]
|
||||||
|
isEnabled boolean [not null, default: true]
|
||||||
|
organizationId text [not null]
|
||||||
|
createdAt text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
|
table apikey {
|
||||||
|
id text [pk, not null]
|
||||||
|
name text
|
||||||
|
start text
|
||||||
|
prefix text
|
||||||
|
key text [not null]
|
||||||
|
user_id text [not null]
|
||||||
|
refill_interval integer
|
||||||
|
refill_amount integer
|
||||||
|
last_refill_at timestamp
|
||||||
|
enabled boolean
|
||||||
|
rate_limit_enabled boolean
|
||||||
|
rate_limit_time_window integer
|
||||||
|
rate_limit_max integer
|
||||||
|
request_count integer
|
||||||
|
remaining integer
|
||||||
|
last_request timestamp
|
||||||
|
expires_at timestamp
|
||||||
|
created_at timestamp [not null]
|
||||||
|
updated_at timestamp [not null]
|
||||||
|
permissions text
|
||||||
|
metadata text
|
||||||
}
|
}
|
||||||
|
|
||||||
table application {
|
table application {
|
||||||
@@ -143,14 +202,19 @@ table application {
|
|||||||
description text
|
description text
|
||||||
env text
|
env text
|
||||||
previewEnv text
|
previewEnv text
|
||||||
|
watchPaths text[]
|
||||||
previewBuildArgs text
|
previewBuildArgs text
|
||||||
|
previewLabels text[]
|
||||||
previewWildcard text
|
previewWildcard text
|
||||||
previewPort integer [default: 3000]
|
previewPort integer [default: 3000]
|
||||||
previewHttps boolean [not null, default: false]
|
previewHttps boolean [not null, default: false]
|
||||||
previewPath text [default: '/']
|
previewPath text [default: '/']
|
||||||
certificateType certificateType [not null, default: 'none']
|
certificateType certificateType [not null, default: 'none']
|
||||||
|
previewCustomCertResolver text
|
||||||
previewLimit integer [default: 3]
|
previewLimit integer [default: 3]
|
||||||
isPreviewDeploymentsActive boolean [default: false]
|
isPreviewDeploymentsActive boolean [default: false]
|
||||||
|
previewRequireCollaboratorPermissions boolean [default: true]
|
||||||
|
rollbackActive boolean [default: false]
|
||||||
buildArgs text
|
buildArgs text
|
||||||
memoryReservation text
|
memoryReservation text
|
||||||
memoryLimit text
|
memoryLimit text
|
||||||
@@ -167,6 +231,7 @@ table application {
|
|||||||
owner text
|
owner text
|
||||||
branch text
|
branch text
|
||||||
buildPath text [default: '/']
|
buildPath text [default: '/']
|
||||||
|
triggerType triggerType [default: 'push']
|
||||||
autoDeploy boolean
|
autoDeploy boolean
|
||||||
gitlabProjectId integer
|
gitlabProjectId integer
|
||||||
gitlabRepository text
|
gitlabRepository text
|
||||||
@@ -174,6 +239,10 @@ table application {
|
|||||||
gitlabBranch text
|
gitlabBranch text
|
||||||
gitlabBuildPath text [default: '/']
|
gitlabBuildPath text [default: '/']
|
||||||
gitlabPathNamespace text
|
gitlabPathNamespace text
|
||||||
|
giteaRepository text
|
||||||
|
giteaOwner text
|
||||||
|
giteaBranch text
|
||||||
|
giteaBuildPath text [default: '/']
|
||||||
bitbucketRepository text
|
bitbucketRepository text
|
||||||
bitbucketOwner text
|
bitbucketOwner text
|
||||||
bitbucketBranch text
|
bitbucketBranch text
|
||||||
@@ -186,6 +255,7 @@ table application {
|
|||||||
customGitBranch text
|
customGitBranch text
|
||||||
customGitBuildPath text
|
customGitBuildPath text
|
||||||
customGitSSHKeyId text
|
customGitSSHKeyId text
|
||||||
|
enableSubmodules boolean [not null, default: false]
|
||||||
dockerfile text
|
dockerfile text
|
||||||
dockerContextPath text
|
dockerContextPath text
|
||||||
dockerBuildStage text
|
dockerBuildStage text
|
||||||
@@ -201,52 +271,47 @@ table application {
|
|||||||
replicas integer [not null, default: 1]
|
replicas integer [not null, default: 1]
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
buildType buildType [not null, default: 'nixpacks']
|
buildType buildType [not null, default: 'nixpacks']
|
||||||
|
railpackVersion text [default: '0.2.2']
|
||||||
herokuVersion text [default: '24']
|
herokuVersion text [default: '24']
|
||||||
publishDirectory text
|
publishDirectory text
|
||||||
|
isStaticSpa boolean
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
registryId text
|
registryId text
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
githubId text
|
githubId text
|
||||||
gitlabId text
|
gitlabId text
|
||||||
bitbucketId text
|
|
||||||
giteaId text
|
giteaId text
|
||||||
|
bitbucketId text
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
table auth {
|
|
||||||
id text [pk, not null]
|
|
||||||
email text [not null, unique]
|
|
||||||
password text [not null]
|
|
||||||
rol Roles [not null]
|
|
||||||
image text
|
|
||||||
secret text
|
|
||||||
token text
|
|
||||||
is2FAEnabled boolean [not null, default: false]
|
|
||||||
createdAt text [not null]
|
|
||||||
resetPasswordToken text
|
|
||||||
resetPasswordExpiresAt text
|
|
||||||
confirmationToken text
|
|
||||||
confirmationExpiresAt text
|
|
||||||
}
|
|
||||||
|
|
||||||
table backup {
|
table backup {
|
||||||
backupId text [pk, not null]
|
backupId text [pk, not null]
|
||||||
|
appName text [not null, unique]
|
||||||
schedule text [not null]
|
schedule text [not null]
|
||||||
enabled boolean
|
enabled boolean
|
||||||
database text [not null]
|
database text [not null]
|
||||||
prefix text [not null]
|
prefix text [not null]
|
||||||
|
serviceName text
|
||||||
destinationId text [not null]
|
destinationId text [not null]
|
||||||
|
keepLatestCount integer
|
||||||
|
backupType backupType [not null, default: 'database']
|
||||||
databaseType databaseType [not null]
|
databaseType databaseType [not null]
|
||||||
|
composeId text
|
||||||
postgresId text
|
postgresId text
|
||||||
mariadbId text
|
mariadbId text
|
||||||
mysqlId text
|
mysqlId text
|
||||||
mongoId text
|
mongoId text
|
||||||
|
userId text
|
||||||
|
metadata jsonb
|
||||||
}
|
}
|
||||||
|
|
||||||
table bitbucket {
|
table bitbucket {
|
||||||
bitbucketId text [pk, not null]
|
bitbucketId text [pk, not null]
|
||||||
bitbucketUsername text
|
bitbucketUsername text
|
||||||
|
bitbucketEmail text
|
||||||
appPassword text
|
appPassword text
|
||||||
|
apiToken text
|
||||||
bitbucketWorkspaceName text
|
bitbucketWorkspaceName text
|
||||||
gitProviderId text [not null]
|
gitProviderId text [not null]
|
||||||
}
|
}
|
||||||
@@ -258,7 +323,7 @@ table certificate {
|
|||||||
privateKey text [not null]
|
privateKey text [not null]
|
||||||
certificatePath text [not null, unique]
|
certificatePath text [not null, unique]
|
||||||
autoRenew boolean
|
autoRenew boolean
|
||||||
userId text
|
organizationId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,13 +356,17 @@ table compose {
|
|||||||
customGitBranch text
|
customGitBranch text
|
||||||
customGitSSHKeyId text
|
customGitSSHKeyId text
|
||||||
command text [not null, default: '']
|
command text [not null, default: '']
|
||||||
|
enableSubmodules boolean [not null, default: false]
|
||||||
composePath text [not null, default: './docker-compose.yml']
|
composePath text [not null, default: './docker-compose.yml']
|
||||||
suffix text [not null, default: '']
|
suffix text [not null, default: '']
|
||||||
randomize boolean [not null, default: false]
|
randomize boolean [not null, default: false]
|
||||||
isolatedDeployment boolean [not null, default: false]
|
isolatedDeployment boolean [not null, default: false]
|
||||||
|
isolatedDeploymentsVolume boolean [not null, default: false]
|
||||||
|
triggerType triggerType [default: 'push']
|
||||||
composeStatus applicationStatus [not null, default: 'idle']
|
composeStatus applicationStatus [not null, default: 'idle']
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
|
watchPaths text[]
|
||||||
githubId text
|
githubId text
|
||||||
gitlabId text
|
gitlabId text
|
||||||
bitbucketId text
|
bitbucketId text
|
||||||
@@ -305,19 +374,32 @@ table compose {
|
|||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table custom {
|
||||||
|
customId text [pk, not null]
|
||||||
|
endpoint text [not null]
|
||||||
|
headers text
|
||||||
|
}
|
||||||
|
|
||||||
table deployment {
|
table deployment {
|
||||||
deploymentId text [pk, not null]
|
deploymentId text [pk, not null]
|
||||||
title text [not null]
|
title text [not null]
|
||||||
description text
|
description text
|
||||||
status deploymentStatus [default: 'running']
|
status deploymentStatus [default: 'running']
|
||||||
logPath text [not null]
|
logPath text [not null]
|
||||||
|
pid text
|
||||||
applicationId text
|
applicationId text
|
||||||
composeId text
|
composeId text
|
||||||
serverId text
|
serverId text
|
||||||
isPreviewDeployment boolean [default: false]
|
isPreviewDeployment boolean [default: false]
|
||||||
previewDeploymentId text
|
previewDeploymentId text
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
|
startedAt text
|
||||||
|
finishedAt text
|
||||||
errorMessage text
|
errorMessage text
|
||||||
|
scheduleId text
|
||||||
|
backupId text
|
||||||
|
rollbackId text
|
||||||
|
volumeBackupId text
|
||||||
}
|
}
|
||||||
|
|
||||||
table destination {
|
table destination {
|
||||||
@@ -329,7 +411,8 @@ table destination {
|
|||||||
bucket text [not null]
|
bucket text [not null]
|
||||||
region text [not null]
|
region text [not null]
|
||||||
endpoint text [not null]
|
endpoint text [not null]
|
||||||
userId text [not null]
|
organizationId text [not null]
|
||||||
|
createdAt timestamp [not null, default: `now()`]
|
||||||
}
|
}
|
||||||
|
|
||||||
table discord {
|
table discord {
|
||||||
@@ -349,9 +432,12 @@ table domain {
|
|||||||
uniqueConfigKey serial [not null, increment]
|
uniqueConfigKey serial [not null, increment]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
composeId text
|
composeId text
|
||||||
|
customCertResolver text
|
||||||
applicationId text
|
applicationId text
|
||||||
previewDeploymentId text
|
previewDeploymentId text
|
||||||
certificateType certificateType [not null, default: 'none']
|
certificateType certificateType [not null, default: 'none']
|
||||||
|
internalPath text [default: '/']
|
||||||
|
stripPath boolean [not null, default: false]
|
||||||
}
|
}
|
||||||
|
|
||||||
table email {
|
table email {
|
||||||
@@ -364,12 +450,36 @@ table email {
|
|||||||
toAddress text[] [not null]
|
toAddress text[] [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table environment {
|
||||||
|
environmentId text [pk, not null]
|
||||||
|
name text [not null]
|
||||||
|
description text
|
||||||
|
createdAt text [not null]
|
||||||
|
env text [not null, default: '']
|
||||||
|
projectId text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
table git_provider {
|
table git_provider {
|
||||||
gitProviderId text [pk, not null]
|
gitProviderId text [pk, not null]
|
||||||
name text [not null]
|
name text [not null]
|
||||||
providerType gitProviderType [not null, default: 'github']
|
providerType gitProviderType [not null, default: 'github']
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
userId text
|
organizationId text [not null]
|
||||||
|
userId text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
|
table gitea {
|
||||||
|
giteaId text [pk, not null]
|
||||||
|
giteaUrl text [not null, default: 'https://gitea.com']
|
||||||
|
redirect_uri text
|
||||||
|
client_id text
|
||||||
|
client_secret text
|
||||||
|
gitProviderId text [not null]
|
||||||
|
access_token text
|
||||||
|
refresh_token text
|
||||||
|
expires_at integer
|
||||||
|
scopes text [default: 'repo,repo:status,read:user,read:org']
|
||||||
|
last_authenticated_at integer
|
||||||
}
|
}
|
||||||
|
|
||||||
table github {
|
table github {
|
||||||
@@ -397,20 +507,6 @@ table gitlab {
|
|||||||
gitProviderId text [not null]
|
gitProviderId text [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
table gitea {
|
|
||||||
giteaId text [pk, not null]
|
|
||||||
giteaUrl text [not null, default: 'https://gitea.com']
|
|
||||||
redirect_uri text
|
|
||||||
client_id text [not null]
|
|
||||||
client_secret text [not null]
|
|
||||||
access_token text
|
|
||||||
refresh_token text
|
|
||||||
expires_at integer
|
|
||||||
gitProviderId text [not null]
|
|
||||||
scopes text [default: 'repo,repo:status,read:user,read:org']
|
|
||||||
last_authenticated_at integer
|
|
||||||
}
|
|
||||||
|
|
||||||
table gotify {
|
table gotify {
|
||||||
gotifyId text [pk, not null]
|
gotifyId text [pk, not null]
|
||||||
serverUrl text [not null]
|
serverUrl text [not null]
|
||||||
@@ -427,6 +523,7 @@ table invitation {
|
|||||||
status text [not null]
|
status text [not null]
|
||||||
expires_at timestamp [not null]
|
expires_at timestamp [not null]
|
||||||
inviter_id text [not null]
|
inviter_id text [not null]
|
||||||
|
team_id text
|
||||||
}
|
}
|
||||||
|
|
||||||
table mariadb {
|
table mariadb {
|
||||||
@@ -447,8 +544,17 @@ table mariadb {
|
|||||||
cpuLimit text
|
cpuLimit text
|
||||||
externalPort integer
|
externalPort integer
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
|
healthCheckSwarm json
|
||||||
|
restartPolicySwarm json
|
||||||
|
placementSwarm json
|
||||||
|
updateConfigSwarm json
|
||||||
|
rollbackConfigSwarm json
|
||||||
|
modeSwarm json
|
||||||
|
labelsSwarm json
|
||||||
|
networkSwarm json
|
||||||
|
replicas integer [not null, default: 1]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,6 +564,19 @@ table member {
|
|||||||
user_id text [not null]
|
user_id text [not null]
|
||||||
role text [not null]
|
role text [not null]
|
||||||
created_at timestamp [not null]
|
created_at timestamp [not null]
|
||||||
|
team_id text
|
||||||
|
canCreateProjects boolean [not null, default: false]
|
||||||
|
canAccessToSSHKeys boolean [not null, default: false]
|
||||||
|
canCreateServices boolean [not null, default: false]
|
||||||
|
canDeleteProjects boolean [not null, default: false]
|
||||||
|
canDeleteServices boolean [not null, default: false]
|
||||||
|
canAccessToDocker boolean [not null, default: false]
|
||||||
|
canAccessToAPI boolean [not null, default: false]
|
||||||
|
canAccessToGitProviders boolean [not null, default: false]
|
||||||
|
canAccessToTraefikFiles boolean [not null, default: false]
|
||||||
|
accesedProjects text[] [not null, default: `ARRAY[]::text[]`]
|
||||||
|
accessedEnvironments text[] [not null, default: `ARRAY[]::text[]`]
|
||||||
|
accesedServices text[] [not null, default: `ARRAY[]::text[]`]
|
||||||
}
|
}
|
||||||
|
|
||||||
table mongo {
|
table mongo {
|
||||||
@@ -476,8 +595,17 @@ table mongo {
|
|||||||
cpuLimit text
|
cpuLimit text
|
||||||
externalPort integer
|
externalPort integer
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
|
healthCheckSwarm json
|
||||||
|
restartPolicySwarm json
|
||||||
|
placementSwarm json
|
||||||
|
updateConfigSwarm json
|
||||||
|
rollbackConfigSwarm json
|
||||||
|
modeSwarm json
|
||||||
|
labelsSwarm json
|
||||||
|
networkSwarm json
|
||||||
|
replicas integer [not null, default: 1]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
replicaSets boolean [default: false]
|
replicaSets boolean [default: false]
|
||||||
}
|
}
|
||||||
@@ -518,8 +646,17 @@ table mysql {
|
|||||||
cpuLimit text
|
cpuLimit text
|
||||||
externalPort integer
|
externalPort integer
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
|
healthCheckSwarm json
|
||||||
|
restartPolicySwarm json
|
||||||
|
placementSwarm json
|
||||||
|
updateConfigSwarm json
|
||||||
|
rollbackConfigSwarm json
|
||||||
|
modeSwarm json
|
||||||
|
labelsSwarm json
|
||||||
|
networkSwarm json
|
||||||
|
replicas integer [not null, default: 1]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +676,17 @@ table notification {
|
|||||||
discordId text
|
discordId text
|
||||||
emailId text
|
emailId text
|
||||||
gotifyId text
|
gotifyId text
|
||||||
userId text
|
ntfyId text
|
||||||
|
customId text
|
||||||
|
organizationId text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
|
table ntfy {
|
||||||
|
ntfyId text [pk, not null]
|
||||||
|
serverUrl text [not null]
|
||||||
|
topic text [not null]
|
||||||
|
accessToken text [not null]
|
||||||
|
priority integer [not null, default: 3]
|
||||||
}
|
}
|
||||||
|
|
||||||
table organization {
|
table organization {
|
||||||
@@ -555,6 +702,7 @@ table organization {
|
|||||||
table port {
|
table port {
|
||||||
portId text [pk, not null]
|
portId text [pk, not null]
|
||||||
publishedPort integer [not null]
|
publishedPort integer [not null]
|
||||||
|
publishMode publishModeType [not null, default: 'host']
|
||||||
targetPort integer [not null]
|
targetPort integer [not null]
|
||||||
protocol protocolType [not null]
|
protocol protocolType [not null]
|
||||||
applicationId text [not null]
|
applicationId text [not null]
|
||||||
@@ -577,8 +725,17 @@ table postgres {
|
|||||||
cpuReservation text
|
cpuReservation text
|
||||||
cpuLimit text
|
cpuLimit text
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
|
healthCheckSwarm json
|
||||||
|
restartPolicySwarm json
|
||||||
|
placementSwarm json
|
||||||
|
updateConfigSwarm json
|
||||||
|
rollbackConfigSwarm json
|
||||||
|
modeSwarm json
|
||||||
|
labelsSwarm json
|
||||||
|
networkSwarm json
|
||||||
|
replicas integer [not null, default: 1]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
projectId text [not null]
|
environmentId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,7 +760,7 @@ table project {
|
|||||||
name text [not null]
|
name text [not null]
|
||||||
description text
|
description text
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
userId text [not null]
|
organizationId text [not null]
|
||||||
env text [not null, default: '']
|
env text [not null, default: '']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,7 +790,16 @@ table redis {
|
|||||||
externalPort integer
|
externalPort integer
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
applicationStatus applicationStatus [not null, default: 'idle']
|
applicationStatus applicationStatus [not null, default: 'idle']
|
||||||
projectId text [not null]
|
healthCheckSwarm json
|
||||||
|
restartPolicySwarm json
|
||||||
|
placementSwarm json
|
||||||
|
updateConfigSwarm json
|
||||||
|
rollbackConfigSwarm json
|
||||||
|
modeSwarm json
|
||||||
|
labelsSwarm json
|
||||||
|
networkSwarm json
|
||||||
|
replicas integer [not null, default: 1]
|
||||||
|
environmentId text [not null]
|
||||||
serverId text
|
serverId text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,7 +812,34 @@ table registry {
|
|||||||
registryUrl text [not null, default: '']
|
registryUrl text [not null, default: '']
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
selfHosted RegistryType [not null, default: 'cloud']
|
selfHosted RegistryType [not null, default: 'cloud']
|
||||||
userId text [not null]
|
organizationId text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
|
table rollback {
|
||||||
|
rollbackId text [pk, not null]
|
||||||
|
deploymentId text [not null]
|
||||||
|
version serial [not null, increment]
|
||||||
|
image text
|
||||||
|
createdAt text [not null]
|
||||||
|
fullContext jsonb
|
||||||
|
}
|
||||||
|
|
||||||
|
table schedule {
|
||||||
|
scheduleId text [pk, not null]
|
||||||
|
name text [not null]
|
||||||
|
cronExpression text [not null]
|
||||||
|
appName text [not null]
|
||||||
|
serviceName text
|
||||||
|
shellType shellType [not null, default: 'bash']
|
||||||
|
scheduleType scheduleType [not null, default: 'application']
|
||||||
|
command text [not null]
|
||||||
|
script text
|
||||||
|
applicationId text
|
||||||
|
composeId text
|
||||||
|
serverId text
|
||||||
|
userId text
|
||||||
|
enabled boolean [not null, default: true]
|
||||||
|
createdAt text [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
table security {
|
table security {
|
||||||
@@ -671,14 +864,14 @@ table server {
|
|||||||
appName text [not null]
|
appName text [not null]
|
||||||
enableDockerCleanup boolean [not null, default: false]
|
enableDockerCleanup boolean [not null, default: false]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
userId text [not null]
|
organizationId text [not null]
|
||||||
serverStatus serverStatus [not null, default: 'active']
|
serverStatus serverStatus [not null, default: 'active']
|
||||||
command text [not null, default: '']
|
command text [not null, default: '']
|
||||||
sshKeyId text
|
sshKeyId text
|
||||||
metricsConfig jsonb [not null, default: `{"server":{"type":"Remote","refreshRate":60,"port":4500,"token":"","urlCallback":"","cronJob":"","retentionDays":2,"thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`]
|
metricsConfig jsonb [not null, default: `{"server":{"type":"Remote","refreshRate":60,"port":4500,"token":"","urlCallback":"","cronJob":"","retentionDays":2,"thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`]
|
||||||
}
|
}
|
||||||
|
|
||||||
table session {
|
table session_temp {
|
||||||
id text [pk, not null]
|
id text [pk, not null]
|
||||||
expires_at timestamp [not null]
|
expires_at timestamp [not null]
|
||||||
token text [not null, unique]
|
token text [not null, unique]
|
||||||
@@ -705,49 +898,49 @@ table "ssh-key" {
|
|||||||
description text
|
description text
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
lastUsedAt text
|
lastUsedAt text
|
||||||
userId text
|
organizationId text [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
table telegram {
|
table telegram {
|
||||||
telegramId text [pk, not null]
|
telegramId text [pk, not null]
|
||||||
botToken text [not null]
|
botToken text [not null]
|
||||||
chatId text [not null]
|
chatId text [not null]
|
||||||
|
messageThreadId text
|
||||||
}
|
}
|
||||||
|
|
||||||
table user {
|
table two_factor {
|
||||||
|
id text [pk, not null]
|
||||||
|
secret text [not null]
|
||||||
|
backup_codes text [not null]
|
||||||
|
user_id text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
|
table user_temp {
|
||||||
id text [pk, not null]
|
id text [pk, not null]
|
||||||
name text [not null, default: '']
|
name text [not null, default: '']
|
||||||
token text [not null]
|
|
||||||
isRegistered boolean [not null, default: false]
|
isRegistered boolean [not null, default: false]
|
||||||
expirationDate text [not null]
|
expirationDate text [not null]
|
||||||
createdAt text [not null]
|
createdAt text [not null]
|
||||||
canCreateProjects boolean [not null, default: false]
|
created_at timestamp [default: `now()`]
|
||||||
canAccessToSSHKeys boolean [not null, default: false]
|
two_factor_enabled boolean
|
||||||
canCreateServices boolean [not null, default: false]
|
|
||||||
canDeleteProjects boolean [not null, default: false]
|
|
||||||
canDeleteServices boolean [not null, default: false]
|
|
||||||
canAccessToDocker boolean [not null, default: false]
|
|
||||||
canAccessToAPI boolean [not null, default: false]
|
|
||||||
canAccessToGitProviders boolean [not null, default: false]
|
|
||||||
canAccessToTraefikFiles boolean [not null, default: false]
|
|
||||||
accesedProjects text[] [not null, default: `ARRAY[]::text[]`]
|
|
||||||
accesedServices text[] [not null, default: `ARRAY[]::text[]`]
|
|
||||||
email text [not null, unique]
|
email text [not null, unique]
|
||||||
email_verified boolean [not null]
|
email_verified boolean [not null]
|
||||||
image text
|
image text
|
||||||
role text
|
|
||||||
banned boolean
|
banned boolean
|
||||||
ban_reason text
|
ban_reason text
|
||||||
ban_expires timestamp
|
ban_expires timestamp
|
||||||
updated_at timestamp [not null]
|
updated_at timestamp [not null]
|
||||||
serverIp text
|
serverIp text
|
||||||
certificateType certificateType [not null, default: 'none']
|
certificateType certificateType [not null, default: 'none']
|
||||||
|
https boolean [not null, default: false]
|
||||||
host text
|
host text
|
||||||
letsEncryptEmail text
|
letsEncryptEmail text
|
||||||
sshPrivateKey text
|
sshPrivateKey text
|
||||||
enableDockerCleanup boolean [not null, default: false]
|
enableDockerCleanup boolean [not null, default: false]
|
||||||
enableLogRotation boolean [not null, default: false]
|
logCleanupCron text [default: '0 0 * * *']
|
||||||
|
role text [not null, default: 'user']
|
||||||
enablePaidFeatures boolean [not null, default: false]
|
enablePaidFeatures boolean [not null, default: false]
|
||||||
|
allowImpersonation boolean [not null, default: false]
|
||||||
metricsConfig jsonb [not null, default: `{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`]
|
metricsConfig jsonb [not null, default: `{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`]
|
||||||
cleanupCacheApplications boolean [not null, default: false]
|
cleanupCacheApplications boolean [not null, default: false]
|
||||||
cleanupCacheOnPreviews boolean [not null, default: false]
|
cleanupCacheOnPreviews boolean [not null, default: false]
|
||||||
@@ -766,6 +959,29 @@ table verification {
|
|||||||
updated_at timestamp
|
updated_at timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table volume_backup {
|
||||||
|
volumeBackupId text [pk, not null]
|
||||||
|
name text [not null]
|
||||||
|
volumeName text [not null]
|
||||||
|
prefix text [not null]
|
||||||
|
serviceType serviceType [not null, default: 'application']
|
||||||
|
appName text [not null]
|
||||||
|
serviceName text
|
||||||
|
turnOff boolean [not null, default: false]
|
||||||
|
cronExpression text [not null]
|
||||||
|
keepLatestCount integer
|
||||||
|
enabled boolean
|
||||||
|
applicationId text
|
||||||
|
postgresId text
|
||||||
|
mariadbId text
|
||||||
|
mongoId text
|
||||||
|
mysqlId text
|
||||||
|
redisId text
|
||||||
|
composeId text
|
||||||
|
createdAt text [not null]
|
||||||
|
destinationId text [not null]
|
||||||
|
}
|
||||||
|
|
||||||
ref: mount.applicationId > application.applicationId
|
ref: mount.applicationId > application.applicationId
|
||||||
|
|
||||||
ref: mount.postgresId > postgres.postgresId
|
ref: mount.postgresId > postgres.postgresId
|
||||||
@@ -780,7 +996,13 @@ ref: mount.redisId > redis.redisId
|
|||||||
|
|
||||||
ref: mount.composeId > compose.composeId
|
ref: mount.composeId > compose.composeId
|
||||||
|
|
||||||
ref: application.projectId > project.projectId
|
ref: user_temp.id - account.user_id
|
||||||
|
|
||||||
|
ref: ai.organizationId - organization.id
|
||||||
|
|
||||||
|
ref: apikey.user_id > user_temp.id
|
||||||
|
|
||||||
|
ref: application.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: application.customGitSSHKeyId > "ssh-key".sshKeyId
|
ref: application.customGitSSHKeyId > "ssh-key".sshKeyId
|
||||||
|
|
||||||
@@ -790,6 +1012,8 @@ ref: application.githubId - github.githubId
|
|||||||
|
|
||||||
ref: application.gitlabId - gitlab.gitlabId
|
ref: application.gitlabId - gitlab.gitlabId
|
||||||
|
|
||||||
|
ref: application.giteaId - gitea.giteaId
|
||||||
|
|
||||||
ref: application.bitbucketId - bitbucket.bitbucketId
|
ref: application.bitbucketId - bitbucket.bitbucketId
|
||||||
|
|
||||||
ref: application.serverId > server.serverId
|
ref: application.serverId > server.serverId
|
||||||
@@ -804,13 +1028,17 @@ ref: backup.mysqlId > mysql.mysqlId
|
|||||||
|
|
||||||
ref: backup.mongoId > mongo.mongoId
|
ref: backup.mongoId > mongo.mongoId
|
||||||
|
|
||||||
|
ref: backup.userId > user_temp.id
|
||||||
|
|
||||||
|
ref: backup.composeId > compose.composeId
|
||||||
|
|
||||||
ref: git_provider.gitProviderId - bitbucket.gitProviderId
|
ref: git_provider.gitProviderId - bitbucket.gitProviderId
|
||||||
|
|
||||||
ref: certificate.serverId > server.serverId
|
ref: certificate.serverId > server.serverId
|
||||||
|
|
||||||
ref: certificate.userId - user.id
|
ref: certificate.organizationId - organization.id
|
||||||
|
|
||||||
ref: compose.projectId > project.projectId
|
ref: compose.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: compose.customGitSSHKeyId > "ssh-key".sshKeyId
|
ref: compose.customGitSSHKeyId > "ssh-key".sshKeyId
|
||||||
|
|
||||||
@@ -820,6 +1048,8 @@ ref: compose.gitlabId - gitlab.gitlabId
|
|||||||
|
|
||||||
ref: compose.bitbucketId - bitbucket.bitbucketId
|
ref: compose.bitbucketId - bitbucket.bitbucketId
|
||||||
|
|
||||||
|
ref: compose.giteaId - gitea.giteaId
|
||||||
|
|
||||||
ref: compose.serverId > server.serverId
|
ref: compose.serverId > server.serverId
|
||||||
|
|
||||||
ref: deployment.applicationId > application.applicationId
|
ref: deployment.applicationId > application.applicationId
|
||||||
@@ -830,7 +1060,15 @@ ref: deployment.serverId > server.serverId
|
|||||||
|
|
||||||
ref: deployment.previewDeploymentId > preview_deployments.previewDeploymentId
|
ref: deployment.previewDeploymentId > preview_deployments.previewDeploymentId
|
||||||
|
|
||||||
ref: destination.userId - user.id
|
ref: deployment.scheduleId > schedule.scheduleId
|
||||||
|
|
||||||
|
ref: deployment.backupId > backup.backupId
|
||||||
|
|
||||||
|
ref: rollback.deploymentId - deployment.deploymentId
|
||||||
|
|
||||||
|
ref: deployment.volumeBackupId > volume_backup.volumeBackupId
|
||||||
|
|
||||||
|
ref: destination.organizationId - organization.id
|
||||||
|
|
||||||
ref: domain.applicationId > application.applicationId
|
ref: domain.applicationId > application.applicationId
|
||||||
|
|
||||||
@@ -838,23 +1076,33 @@ ref: domain.composeId > compose.composeId
|
|||||||
|
|
||||||
ref: preview_deployments.domainId - domain.domainId
|
ref: preview_deployments.domainId - domain.domainId
|
||||||
|
|
||||||
|
ref: environment.projectId > project.projectId
|
||||||
|
|
||||||
ref: github.gitProviderId - git_provider.gitProviderId
|
ref: github.gitProviderId - git_provider.gitProviderId
|
||||||
|
|
||||||
ref: gitlab.gitProviderId - git_provider.gitProviderId
|
ref: gitlab.gitProviderId - git_provider.gitProviderId
|
||||||
|
|
||||||
ref: gitea.gitProviderId - git_provider.gitProviderId
|
ref: gitea.gitProviderId - git_provider.gitProviderId
|
||||||
|
|
||||||
ref: git_provider.userId - user.id
|
ref: git_provider.organizationId - organization.id
|
||||||
|
|
||||||
ref: mariadb.projectId > project.projectId
|
ref: git_provider.userId - user_temp.id
|
||||||
|
|
||||||
|
ref: invitation.organization_id - organization.id
|
||||||
|
|
||||||
|
ref: mariadb.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: mariadb.serverId > server.serverId
|
ref: mariadb.serverId > server.serverId
|
||||||
|
|
||||||
ref: mongo.projectId > project.projectId
|
ref: member.organization_id > organization.id
|
||||||
|
|
||||||
|
ref: member.user_id - user_temp.id
|
||||||
|
|
||||||
|
ref: mongo.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: mongo.serverId > server.serverId
|
ref: mongo.serverId > server.serverId
|
||||||
|
|
||||||
ref: mysql.projectId > project.projectId
|
ref: mysql.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: mysql.serverId > server.serverId
|
ref: mysql.serverId > server.serverId
|
||||||
|
|
||||||
@@ -868,30 +1116,58 @@ ref: notification.emailId - email.emailId
|
|||||||
|
|
||||||
ref: notification.gotifyId - gotify.gotifyId
|
ref: notification.gotifyId - gotify.gotifyId
|
||||||
|
|
||||||
ref: notification.userId - user.id
|
ref: notification.ntfyId - ntfy.ntfyId
|
||||||
|
|
||||||
|
ref: notification.customId - custom.customId
|
||||||
|
|
||||||
|
ref: notification.organizationId - organization.id
|
||||||
|
|
||||||
|
ref: organization.owner_id > user_temp.id
|
||||||
|
|
||||||
ref: port.applicationId > application.applicationId
|
ref: port.applicationId > application.applicationId
|
||||||
|
|
||||||
ref: postgres.projectId > project.projectId
|
ref: postgres.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: postgres.serverId > server.serverId
|
ref: postgres.serverId > server.serverId
|
||||||
|
|
||||||
ref: preview_deployments.applicationId > application.applicationId
|
ref: preview_deployments.applicationId > application.applicationId
|
||||||
|
|
||||||
ref: project.userId - user.id
|
ref: project.organizationId > organization.id
|
||||||
|
|
||||||
ref: redirect.applicationId > application.applicationId
|
ref: redirect.applicationId > application.applicationId
|
||||||
|
|
||||||
ref: redis.projectId > project.projectId
|
ref: redis.environmentId > environment.environmentId
|
||||||
|
|
||||||
ref: redis.serverId > server.serverId
|
ref: redis.serverId > server.serverId
|
||||||
|
|
||||||
ref: registry.userId - user.id
|
ref: schedule.applicationId - application.applicationId
|
||||||
|
|
||||||
|
ref: schedule.composeId > compose.composeId
|
||||||
|
|
||||||
|
ref: schedule.serverId > server.serverId
|
||||||
|
|
||||||
|
ref: schedule.userId > user_temp.id
|
||||||
|
|
||||||
ref: security.applicationId > application.applicationId
|
ref: security.applicationId > application.applicationId
|
||||||
|
|
||||||
ref: server.userId - user.id
|
|
||||||
|
|
||||||
ref: server.sshKeyId > "ssh-key".sshKeyId
|
ref: server.sshKeyId > "ssh-key".sshKeyId
|
||||||
|
|
||||||
ref: "ssh-key".userId - user.id
|
ref: server.organizationId > organization.id
|
||||||
|
|
||||||
|
ref: "ssh-key".organizationId - organization.id
|
||||||
|
|
||||||
|
ref: volume_backup.applicationId - application.applicationId
|
||||||
|
|
||||||
|
ref: volume_backup.postgresId - postgres.postgresId
|
||||||
|
|
||||||
|
ref: volume_backup.mariadbId - mariadb.mariadbId
|
||||||
|
|
||||||
|
ref: volume_backup.mongoId - mongo.mongoId
|
||||||
|
|
||||||
|
ref: volume_backup.mysqlId - mysql.mysqlId
|
||||||
|
|
||||||
|
ref: volume_backup.redisId - redis.redisId
|
||||||
|
|
||||||
|
ref: volume_backup.composeId - compose.composeId
|
||||||
|
|
||||||
|
ref: volume_backup.destinationId - destination.destinationId
|
||||||
@@ -1,23 +1,26 @@
|
|||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import {
|
import {
|
||||||
|
type apiCreateCustom,
|
||||||
type apiCreateDiscord,
|
type apiCreateDiscord,
|
||||||
type apiCreateEmail,
|
type apiCreateEmail,
|
||||||
type apiCreateLark,
|
|
||||||
type apiCreateGotify,
|
type apiCreateGotify,
|
||||||
|
type apiCreateLark,
|
||||||
type apiCreateNtfy,
|
type apiCreateNtfy,
|
||||||
type apiCreateSlack,
|
type apiCreateSlack,
|
||||||
type apiCreateTelegram,
|
type apiCreateTelegram,
|
||||||
|
type apiUpdateCustom,
|
||||||
type apiUpdateDiscord,
|
type apiUpdateDiscord,
|
||||||
type apiUpdateEmail,
|
type apiUpdateEmail,
|
||||||
type apiUpdateLark,
|
|
||||||
type apiUpdateGotify,
|
type apiUpdateGotify,
|
||||||
|
type apiUpdateLark,
|
||||||
type apiUpdateNtfy,
|
type apiUpdateNtfy,
|
||||||
type apiUpdateSlack,
|
type apiUpdateSlack,
|
||||||
type apiUpdateTelegram,
|
type apiUpdateTelegram,
|
||||||
|
custom,
|
||||||
discord,
|
discord,
|
||||||
email,
|
email,
|
||||||
lark,
|
|
||||||
gotify,
|
gotify,
|
||||||
|
lark,
|
||||||
notifications,
|
notifications,
|
||||||
ntfy,
|
ntfy,
|
||||||
slack,
|
slack,
|
||||||
@@ -590,6 +593,94 @@ export const updateNtfyNotification = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createCustomNotification = async (
|
||||||
|
input: typeof apiCreateCustom._type,
|
||||||
|
organizationId: string,
|
||||||
|
) => {
|
||||||
|
await db.transaction(async (tx) => {
|
||||||
|
const newCustom = await tx
|
||||||
|
.insert(custom)
|
||||||
|
.values({
|
||||||
|
endpoint: input.endpoint,
|
||||||
|
headers: input.headers,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.then((value) => value[0]);
|
||||||
|
|
||||||
|
if (!newCustom) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error input: Inserting custom",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDestination = await tx
|
||||||
|
.insert(notifications)
|
||||||
|
.values({
|
||||||
|
customId: newCustom.customId,
|
||||||
|
name: input.name,
|
||||||
|
appDeploy: input.appDeploy,
|
||||||
|
appBuildError: input.appBuildError,
|
||||||
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployRestart: input.dokployRestart,
|
||||||
|
dockerCleanup: input.dockerCleanup,
|
||||||
|
notificationType: "custom",
|
||||||
|
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 updateCustomNotification = async (
|
||||||
|
input: typeof apiUpdateCustom._type,
|
||||||
|
) => {
|
||||||
|
await db.transaction(async (tx) => {
|
||||||
|
const newDestination = await tx
|
||||||
|
.update(notifications)
|
||||||
|
.set({
|
||||||
|
name: input.name,
|
||||||
|
appDeploy: input.appDeploy,
|
||||||
|
appBuildError: input.appBuildError,
|
||||||
|
databaseBackup: input.databaseBackup,
|
||||||
|
dokployRestart: input.dokployRestart,
|
||||||
|
dockerCleanup: input.dockerCleanup,
|
||||||
|
organizationId: input.organizationId,
|
||||||
|
serverThreshold: input.serverThreshold,
|
||||||
|
})
|
||||||
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
|
.returning()
|
||||||
|
.then((value) => value[0]);
|
||||||
|
|
||||||
|
if (!newDestination) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error Updating notification",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(custom)
|
||||||
|
.set({
|
||||||
|
endpoint: input.endpoint,
|
||||||
|
headers: input.headers,
|
||||||
|
})
|
||||||
|
.where(eq(custom.customId, input.customId));
|
||||||
|
|
||||||
|
return newDestination;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const findNotificationById = async (notificationId: string) => {
|
export const findNotificationById = async (notificationId: string) => {
|
||||||
const notification = await db.query.notifications.findFirst({
|
const notification = await db.query.notifications.findFirst({
|
||||||
where: eq(notifications.notificationId, notificationId),
|
where: eq(notifications.notificationId, notificationId),
|
||||||
@@ -600,6 +691,7 @@ export const findNotificationById = async (notificationId: string) => {
|
|||||||
email: true,
|
email: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
|
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
import type { Mongo } from "@dokploy/server/services/mongo";
|
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
|
import type { Mongo } from "@dokploy/server/services/mongo";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
import type { MySql } from "@dokploy/server/services/mysql";
|
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
|
import type { MySql } from "@dokploy/server/services/mysql";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
import type { Postgres } from "@dokploy/server/services/postgres";
|
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
|
import type { Postgres } from "@dokploy/server/services/postgres";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { renderAsync } from "@react-email/components";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -45,12 +46,13 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
slack: true,
|
slack: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
|
||||||
notification;
|
notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email) {
|
||||||
@@ -220,6 +222,22 @@ export const sendBuildErrorNotifications = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: "Build Error",
|
||||||
|
message: "Build failed with errors",
|
||||||
|
projectName,
|
||||||
|
applicationName,
|
||||||
|
applicationType,
|
||||||
|
errorMessage,
|
||||||
|
buildLink,
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
status: "error",
|
||||||
|
type: "build",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
const limitCharacter = 800;
|
const limitCharacter = 800;
|
||||||
const truncatedErrorMessage = errorMessage.substring(0, limitCharacter);
|
const truncatedErrorMessage = errorMessage.substring(0, limitCharacter);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { renderAsync } from "@react-email/components";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -48,12 +49,13 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
slack: true,
|
slack: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
|
||||||
notification;
|
notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email) {
|
||||||
@@ -181,7 +183,10 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
|
|
||||||
await sendTelegramNotification(
|
await sendTelegramNotification(
|
||||||
telegram,
|
telegram,
|
||||||
`<b>✅ Build Success</b>\n\n<b>Project:</b> ${projectName}\n<b>Application:</b> ${applicationName}\n<b>Environment:</b> ${environmentName}\n<b>Type:</b> ${applicationType}\n<b>Date:</b> ${format(date, "PP")}\n<b>Time:</b> ${format(date, "pp")}`,
|
`<b>✅ Build Success</b>\n\n<b>Project:</b> ${projectName}\n<b>Application:</b> ${applicationName}\n<b>Environment:</b> ${environmentName}\n<b>Type:</b> ${applicationType}\n<b>Date:</b> ${format(
|
||||||
|
date,
|
||||||
|
"PP",
|
||||||
|
)}\n<b>Time:</b> ${format(date, "pp")}`,
|
||||||
inlineButton,
|
inlineButton,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -233,6 +238,22 @@ export const sendBuildSuccessNotifications = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: "Build Success",
|
||||||
|
message: "Build completed successfully",
|
||||||
|
projectName,
|
||||||
|
applicationName,
|
||||||
|
applicationType,
|
||||||
|
buildLink,
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
domains: domains.map((domain) => domain.host).join(", "),
|
||||||
|
status: "success",
|
||||||
|
type: "build",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
await sendLarkNotification(lark, {
|
await sendLarkNotification(lark, {
|
||||||
msg_type: "interactive",
|
msg_type: "interactive",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { renderAsync } from "@react-email/components";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -45,12 +46,13 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
slack: true,
|
slack: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
|
||||||
notification;
|
notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email) {
|
||||||
@@ -242,6 +244,25 @@ export const sendDatabaseBackupNotifications = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: `Database Backup ${type === "success" ? "Successful" : "Failed"}`,
|
||||||
|
message:
|
||||||
|
type === "success"
|
||||||
|
? "Database backup completed successfully"
|
||||||
|
: "Database backup failed",
|
||||||
|
projectName,
|
||||||
|
applicationName,
|
||||||
|
databaseType,
|
||||||
|
databaseName,
|
||||||
|
type,
|
||||||
|
errorMessage: errorMessage || "",
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
status: type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
const limitCharacter = 800;
|
const limitCharacter = 800;
|
||||||
const truncatedErrorMessage =
|
const truncatedErrorMessage =
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { renderAsync } from "@react-email/components";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -32,12 +33,13 @@ export const sendDockerCleanupNotifications = async (
|
|||||||
slack: true,
|
slack: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
|
||||||
notification;
|
notification;
|
||||||
try {
|
try {
|
||||||
if (email) {
|
if (email) {
|
||||||
@@ -139,6 +141,18 @@ export const sendDockerCleanupNotifications = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: "Docker Cleanup",
|
||||||
|
message: "Docker cleanup completed successfully",
|
||||||
|
cleanupMessage: message,
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
status: "success",
|
||||||
|
type: "docker-cleanup",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
await sendLarkNotification(lark, {
|
await sendLarkNotification(lark, {
|
||||||
msg_type: "interactive",
|
msg_type: "interactive",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { renderAsync } from "@react-email/components";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
sendGotifyNotification,
|
sendGotifyNotification,
|
||||||
@@ -26,12 +27,13 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
slack: true,
|
slack: true,
|
||||||
gotify: true,
|
gotify: true,
|
||||||
ntfy: true,
|
ntfy: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { email, discord, telegram, slack, gotify, ntfy, lark } =
|
const { email, discord, telegram, slack, gotify, ntfy, custom, lark } =
|
||||||
notification;
|
notification;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -101,7 +103,10 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
if (telegram) {
|
if (telegram) {
|
||||||
await sendTelegramNotification(
|
await sendTelegramNotification(
|
||||||
telegram,
|
telegram,
|
||||||
`<b>✅ Dokploy Server Restarted</b>\n\n<b>Date:</b> ${format(date, "PP")}\n<b>Time:</b> ${format(date, "pp")}`,
|
`<b>✅ Dokploy Server Restarted</b>\n\n<b>Date:</b> ${format(
|
||||||
|
date,
|
||||||
|
"PP",
|
||||||
|
)}\n<b>Time:</b> ${format(date, "pp")}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +130,21 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
try {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: "Dokploy Server Restarted",
|
||||||
|
message: "Dokploy server has been restarted successfully",
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
status: "success",
|
||||||
|
type: "dokploy-restart",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
await sendLarkNotification(lark, {
|
await sendLarkNotification(lark, {
|
||||||
msg_type: "interactive",
|
msg_type: "interactive",
|
||||||
@@ -181,7 +201,10 @@ export const sendDokployRestartNotifications = async () => {
|
|||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
tag: "markdown",
|
tag: "markdown",
|
||||||
content: `**Restart Time:**\n${format(date, "PP pp")}`,
|
content: `**Restart Time:**\n${format(
|
||||||
|
date,
|
||||||
|
"PP pp",
|
||||||
|
)}`,
|
||||||
text_align: "left",
|
text_align: "left",
|
||||||
text_size: "normal_v2",
|
text_size: "normal_v2",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { and, eq } from "drizzle-orm";
|
|||||||
import { db } from "../../db";
|
import { db } from "../../db";
|
||||||
import { notifications } from "../../db/schema";
|
import { notifications } from "../../db/schema";
|
||||||
import {
|
import {
|
||||||
|
sendCustomNotification,
|
||||||
sendDiscordNotification,
|
sendDiscordNotification,
|
||||||
sendLarkNotification,
|
sendLarkNotification,
|
||||||
sendSlackNotification,
|
sendSlackNotification,
|
||||||
@@ -35,6 +36,7 @@ export const sendServerThresholdNotifications = async (
|
|||||||
discord: true,
|
discord: true,
|
||||||
telegram: true,
|
telegram: true,
|
||||||
slack: true,
|
slack: true,
|
||||||
|
custom: true,
|
||||||
lark: true,
|
lark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -43,7 +45,7 @@ export const sendServerThresholdNotifications = async (
|
|||||||
const typeColor = 0xff0000; // Rojo para indicar alerta
|
const typeColor = 0xff0000; // Rojo para indicar alerta
|
||||||
|
|
||||||
for (const notification of notificationList) {
|
for (const notification of notificationList) {
|
||||||
const { discord, telegram, slack, lark } = notification;
|
const { discord, telegram, slack, custom, lark } = notification;
|
||||||
|
|
||||||
if (discord) {
|
if (discord) {
|
||||||
const decorate = (decoration: string, text: string) =>
|
const decorate = (decoration: string, text: string) =>
|
||||||
@@ -154,6 +156,21 @@ export const sendServerThresholdNotifications = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
await sendCustomNotification(custom, {
|
||||||
|
title: `Server ${payload.Type} Alert`,
|
||||||
|
message: payload.Message,
|
||||||
|
serverName: payload.ServerName,
|
||||||
|
type: payload.Type,
|
||||||
|
currentValue: payload.Value,
|
||||||
|
threshold: payload.Threshold,
|
||||||
|
timestamp: date.toISOString(),
|
||||||
|
date: date.toLocaleString(),
|
||||||
|
status: "alert",
|
||||||
|
alertType: "server-threshold",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (lark) {
|
if (lark) {
|
||||||
await sendLarkNotification(lark, {
|
await sendLarkNotification(lark, {
|
||||||
msg_type: "interactive",
|
msg_type: "interactive",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type {
|
import type {
|
||||||
|
custom,
|
||||||
discord,
|
discord,
|
||||||
email,
|
email,
|
||||||
gotify,
|
gotify,
|
||||||
@@ -175,6 +176,39 @@ export const sendNtfyNotification = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sendCustomNotification = async (
|
||||||
|
connection: typeof custom.$inferInsert,
|
||||||
|
payload: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Merge default headers with custom headers (now already an object from jsonb)
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...(connection.headers || {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default body with payload
|
||||||
|
const body = JSON.stringify(payload);
|
||||||
|
|
||||||
|
const response = await fetch(connection.endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to send custom notification: ${response.statusText}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error sending custom notification:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const sendLarkNotification = async (
|
export const sendLarkNotification = async (
|
||||||
connection: typeof lark.$inferInsert,
|
connection: typeof lark.$inferInsert,
|
||||||
message: any,
|
message: any,
|
||||||
|
|||||||
+1194
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user