mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-16 04:35:24 +02:00
feat(notifications): add lark webhook
This commit is contained in:
@@ -12,6 +12,7 @@ import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
DiscordIcon,
|
||||
LarkIcon,
|
||||
SlackIcon,
|
||||
TelegramIcon,
|
||||
} from "@/components/icons/notification-icons";
|
||||
@@ -110,6 +111,12 @@ export const notificationSchema = z.discriminatedUnion("type", [
|
||||
priority: z.number().min(1).max(5).default(3),
|
||||
})
|
||||
.merge(notificationBaseSchema),
|
||||
z
|
||||
.object({
|
||||
type: z.literal("lark"),
|
||||
webhookUrl: z.string().min(1, { message: "Webhook URL is required" }),
|
||||
})
|
||||
.merge(notificationBaseSchema),
|
||||
]);
|
||||
|
||||
export const notificationsMap = {
|
||||
@@ -125,6 +132,10 @@ export const notificationsMap = {
|
||||
icon: <DiscordIcon />,
|
||||
label: "Discord",
|
||||
},
|
||||
lark: {
|
||||
icon: <LarkIcon className="text-muted-foreground" />,
|
||||
label: "Lark",
|
||||
},
|
||||
email: {
|
||||
icon: <Mail size={29} className="text-muted-foreground" />,
|
||||
label: "Email",
|
||||
@@ -170,6 +181,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
api.notification.testGotifyConnection.useMutation();
|
||||
const { mutateAsync: testNtfyConnection, isLoading: isLoadingNtfy } =
|
||||
api.notification.testNtfyConnection.useMutation();
|
||||
const { mutateAsync: testLarkConnection, isLoading: isLoadingLark } =
|
||||
api.notification.testLarkConnection.useMutation();
|
||||
const slackMutation = notificationId
|
||||
? api.notification.updateSlack.useMutation()
|
||||
: api.notification.createSlack.useMutation();
|
||||
@@ -188,6 +201,9 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
const ntfyMutation = notificationId
|
||||
? api.notification.updateNtfy.useMutation()
|
||||
: api.notification.createNtfy.useMutation();
|
||||
const larkMutation = notificationId
|
||||
? api.notification.updateLark.useMutation()
|
||||
: api.notification.createLark.useMutation();
|
||||
|
||||
const form = useForm<NotificationSchema>({
|
||||
defaultValues: {
|
||||
@@ -297,6 +313,19 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
serverUrl: notification.ntfy?.serverUrl,
|
||||
name: notification.name,
|
||||
dockerCleanup: notification.dockerCleanup,
|
||||
serverThreshold: notification.serverThreshold,
|
||||
});
|
||||
} else if (notification.notificationType === "lark") {
|
||||
form.reset({
|
||||
appBuildError: notification.appBuildError,
|
||||
appDeploy: notification.appDeploy,
|
||||
dokployRestart: notification.dokployRestart,
|
||||
databaseBackup: notification.databaseBackup,
|
||||
type: notification.notificationType,
|
||||
webhookUrl: notification.lark?.webhookUrl,
|
||||
name: notification.name,
|
||||
dockerCleanup: notification.dockerCleanup,
|
||||
serverThreshold: notification.serverThreshold,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -311,6 +340,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
email: emailMutation,
|
||||
gotify: gotifyMutation,
|
||||
ntfy: ntfyMutation,
|
||||
lark: larkMutation,
|
||||
};
|
||||
|
||||
const onSubmit = async (data: NotificationSchema) => {
|
||||
@@ -413,6 +443,20 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
dockerCleanup: dockerCleanup,
|
||||
notificationId: notificationId || "",
|
||||
ntfyId: notification?.ntfyId || "",
|
||||
serverThreshold: serverThreshold,
|
||||
});
|
||||
} else if (data.type === "lark") {
|
||||
promise = larkMutation.mutateAsync({
|
||||
appBuildError: appBuildError,
|
||||
appDeploy: appDeploy,
|
||||
dokployRestart: dokployRestart,
|
||||
databaseBackup: databaseBackup,
|
||||
webhookUrl: data.webhookUrl,
|
||||
name: data.name,
|
||||
dockerCleanup: dockerCleanup,
|
||||
notificationId: notificationId || "",
|
||||
larkId: notification?.larkId || "",
|
||||
serverThreshold: serverThreshold,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -502,7 +546,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
/>
|
||||
<Label
|
||||
htmlFor={key}
|
||||
className="flex flex-col gap-2 items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary cursor-pointer"
|
||||
className="h-24 flex flex-col gap-2 items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary cursor-pointer"
|
||||
>
|
||||
{value.icon}
|
||||
{value.label}
|
||||
@@ -1000,6 +1044,27 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{type === "lark" && (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="webhookUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Webhook URL</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="https://open.larksuite.com/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -1150,7 +1215,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
isLoadingDiscord ||
|
||||
isLoadingEmail ||
|
||||
isLoadingGotify ||
|
||||
isLoadingNtfy
|
||||
isLoadingNtfy ||
|
||||
isLoadingLark
|
||||
}
|
||||
variant="secondary"
|
||||
onClick={async () => {
|
||||
@@ -1194,6 +1260,10 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
accessToken: form.getValues("accessToken"),
|
||||
priority: form.getValues("priority"),
|
||||
});
|
||||
} else if (type === "lark") {
|
||||
await testLarkConnection({
|
||||
webhookUrl: form.getValues("webhookUrl"),
|
||||
});
|
||||
}
|
||||
toast.success("Connection Success");
|
||||
} catch {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Bell, Loader2, Mail, MessageCircleMore, Trash2 } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
DiscordIcon,
|
||||
LarkIcon,
|
||||
SlackIcon,
|
||||
TelegramIcon,
|
||||
} from "@/components/icons/notification-icons";
|
||||
@@ -33,7 +34,7 @@ export const ShowNotifications = () => {
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Add your providers to receive notifications, like Discord, Slack,
|
||||
Telegram, Email.
|
||||
Telegram, Email, Lark.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 py-8 border-t">
|
||||
@@ -93,6 +94,11 @@ export const ShowNotifications = () => {
|
||||
<MessageCircleMore className="size-6 text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
{notification.notificationType === "lark" && (
|
||||
<div className="flex items-center justify-center rounded-lg">
|
||||
<LarkIcon className="size-7 text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.name}
|
||||
</span>
|
||||
|
||||
@@ -88,3 +88,30 @@ export const DiscordIcon = ({ className }: Props) => {
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const LarkIcon = ({ className }: Props) => {
|
||||
return (
|
||||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-icon="LarkLogoColorful"
|
||||
className={cn("size-9", className)}
|
||||
>
|
||||
<path
|
||||
d="m12.924 12.803.056-.054c.038-.034.076-.072.11-.11l.077-.076.23-.227 1.334-1.319.335-.331c.063-.063.13-.123.195-.183a7.777 7.777 0 0 1 1.823-1.24 7.607 7.607 0 0 1 1.014-.4 13.177 13.177 0 0 0-2.5-5.013 1.203 1.203 0 0 0-.94-.448h-9.65c-.173 0-.246.224-.107.325a28.23 28.23 0 0 1 8 9.098c.007-.006.016-.013.023-.022Z"
|
||||
fill="#00D6B9"
|
||||
/>
|
||||
<path
|
||||
d="M9.097 21.299a13.258 13.258 0 0 0 11.82-7.247 5.576 5.576 0 0 1-.731 1.076 5.315 5.315 0 0 1-.745.7 5.117 5.117 0 0 1-.615.404 4.626 4.626 0 0 1-.726.331 5.312 5.312 0 0 1-1.883.312 5.892 5.892 0 0 1-.524-.031 6.509 6.509 0 0 1-.729-.126c-.06-.016-.12-.029-.18-.044-.166-.044-.33-.092-.494-.14-.082-.024-.164-.046-.246-.072-.123-.038-.247-.072-.366-.11l-.3-.095-.284-.094-.192-.067c-.08-.025-.155-.053-.234-.082a3.49 3.49 0 0 1-.167-.06c-.11-.04-.221-.079-.328-.12-.063-.025-.126-.047-.19-.072l-.252-.098c-.088-.035-.18-.07-.268-.107l-.174-.07c-.072-.028-.141-.06-.214-.088l-.164-.07c-.057-.024-.114-.05-.17-.075l-.149-.066-.135-.06-.14-.063a90.183 90.183 0 0 1-.141-.066 4.808 4.808 0 0 0-.18-.083c-.063-.028-.123-.06-.186-.088a5.697 5.697 0 0 1-.199-.098 27.762 27.762 0 0 1-8.067-5.969.18.18 0 0 0-.312.123l.006 9.21c0 .4.199.779.533 1a13.177 13.177 0 0 0 7.326 2.205Z"
|
||||
fill="#3370FF"
|
||||
/>
|
||||
<path
|
||||
d="M23.732 9.295a7.55 7.55 0 0 0-3.35-.776 7.521 7.521 0 0 0-2.284.35c-.054.016-.107.035-.158.05a8.297 8.297 0 0 0-.855.35 7.14 7.14 0 0 0-.552.297 6.716 6.716 0 0 0-.533.347c-.123.089-.243.18-.363.275-.13.104-.252.211-.375.321-.067.06-.13.123-.196.184l-.334.328-1.338 1.321-.23.228-.076.075c-.038.038-.076.073-.11.11l-.057.054a1.914 1.914 0 0 1-.085.08c-.032.028-.063.06-.095.088a13.286 13.286 0 0 1-2.748 1.946c.06.028.12.057.18.082l.142.066c.044.022.091.041.139.063l.135.06.149.067.17.075.164.07c.073.031.142.06.215.088.056.025.116.047.173.07.088.034.177.072.268.107.085.031.168.066.253.098l.189.072c.11.041.218.082.328.12.057.019.11.041.167.06.08.028.155.053.234.082l.192.066.284.095.3.095c.123.037.243.075.366.11l.246.072c.164.048.331.095.495.14.06.015.12.03.18.043.114.029.227.05.34.07.13.022.26.04.389.057a5.815 5.815 0 0 0 .994.019 5.172 5.172 0 0 0 1.413-.3 5.405 5.405 0 0 0 .726-.334c.06-.035.122-.07.182-.108a7.96 7.96 0 0 0 .432-.297 5.362 5.362 0 0 0 .577-.517 5.285 5.285 0 0 0 .37-.429 5.797 5.797 0 0 0 .527-.827l.13-.258 1.166-2.325-.003.006a7.391 7.391 0 0 1 1.527-2.186Z"
|
||||
fill="#133C9A"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
13
apps/dokploy/drizzle/0113_lark_webhook.sql
Normal file
13
apps/dokploy/drizzle/0113_lark_webhook.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
DO $$ BEGIN
|
||||
ALTER TYPE "public"."notificationType" ADD VALUE 'lark';
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
CREATE TABLE IF NOT EXISTS "lark" (
|
||||
"larkId" text PRIMARY KEY NOT NULL,
|
||||
"webhookUrl" text NOT NULL
|
||||
);
|
||||
ALTER TABLE "notification" ADD COLUMN IF NOT EXISTS "larkId" text;
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "notification" ADD CONSTRAINT "notification_larkId_lark_larkId_fk" FOREIGN KEY ("larkId") REFERENCES "public"."lark"("larkId") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
6616
apps/dokploy/drizzle/meta/0113_snapshot.json
Normal file
6616
apps/dokploy/drizzle/meta/0113_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -792,6 +792,13 @@
|
||||
"when": 1758483520214,
|
||||
"tag": "0112_freezing_skrulls",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 113,
|
||||
"version": "7",
|
||||
"when": 1758547200000,
|
||||
"tag": "0113_lark_webhook",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
createDiscordNotification,
|
||||
createEmailNotification,
|
||||
createLarkNotification,
|
||||
createGotifyNotification,
|
||||
createNtfyNotification,
|
||||
createSlackNotification,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
removeNotificationById,
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendServerThresholdNotifications,
|
||||
@@ -17,6 +19,7 @@ import {
|
||||
sendTelegramNotification,
|
||||
updateDiscordNotification,
|
||||
updateEmailNotification,
|
||||
updateLarkNotification,
|
||||
updateGotifyNotification,
|
||||
updateNtfyNotification,
|
||||
updateSlackNotification,
|
||||
@@ -35,6 +38,7 @@ import { db } from "@/server/db";
|
||||
import {
|
||||
apiCreateDiscord,
|
||||
apiCreateEmail,
|
||||
apiCreateLark,
|
||||
apiCreateGotify,
|
||||
apiCreateNtfy,
|
||||
apiCreateSlack,
|
||||
@@ -42,12 +46,14 @@ import {
|
||||
apiFindOneNotification,
|
||||
apiTestDiscordConnection,
|
||||
apiTestEmailConnection,
|
||||
apiTestLarkConnection,
|
||||
apiTestGotifyConnection,
|
||||
apiTestNtfyConnection,
|
||||
apiTestSlackConnection,
|
||||
apiTestTelegramConnection,
|
||||
apiUpdateDiscord,
|
||||
apiUpdateEmail,
|
||||
apiUpdateLark,
|
||||
apiUpdateGotify,
|
||||
apiUpdateNtfy,
|
||||
apiUpdateSlack,
|
||||
@@ -328,6 +334,7 @@ export const notificationRouter = createTRPCRouter({
|
||||
email: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
orderBy: desc(notifications.createdAt),
|
||||
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
|
||||
@@ -511,6 +518,63 @@ export const notificationRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
createLark: adminProcedure
|
||||
.input(apiCreateLark)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createLarkNotification(
|
||||
input,
|
||||
ctx.session.activeOrganizationId,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error creating the notification",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
updateLark: adminProcedure
|
||||
.input(apiUpdateLark)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const notification = await findNotificationById(input.notificationId);
|
||||
if (
|
||||
IS_CLOUD &&
|
||||
notification.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to update this notification",
|
||||
});
|
||||
}
|
||||
return await updateLarkNotification({
|
||||
...input,
|
||||
organizationId: ctx.session.activeOrganizationId,
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
testLarkConnection: adminProcedure
|
||||
.input(apiTestLarkConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
await sendLarkNotification(input, {
|
||||
msg_type: "text",
|
||||
content: {
|
||||
text: "Hi, From Dokploy 👋",
|
||||
},
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error testing the notification",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
getEmailProviders: adminProcedure.query(async ({ ctx }) => {
|
||||
return await db.query.notifications.findMany({
|
||||
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
|
||||
|
||||
@@ -12,6 +12,7 @@ export const notificationType = pgEnum("notificationType", [
|
||||
"email",
|
||||
"gotify",
|
||||
"ntfy",
|
||||
"lark",
|
||||
]);
|
||||
|
||||
export const notifications = pgTable("notification", {
|
||||
@@ -48,6 +49,9 @@ export const notifications = pgTable("notification", {
|
||||
ntfyId: text("ntfyId").references(() => ntfy.ntfyId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
larkId: text("larkId").references(() => lark.larkId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
@@ -116,6 +120,14 @@ export const ntfy = pgTable("ntfy", {
|
||||
priority: integer("priority").notNull().default(3),
|
||||
});
|
||||
|
||||
export const lark = pgTable("lark", {
|
||||
larkId: text("larkId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
webhookUrl: text("webhookUrl").notNull(),
|
||||
});
|
||||
|
||||
export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
slack: one(slack, {
|
||||
fields: [notifications.slackId],
|
||||
@@ -141,6 +153,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
fields: [notifications.ntfyId],
|
||||
references: [ntfy.ntfyId],
|
||||
}),
|
||||
lark: one(lark, {
|
||||
fields: [notifications.larkId],
|
||||
references: [lark.larkId],
|
||||
}),
|
||||
organization: one(organization, {
|
||||
fields: [notifications.organizationId],
|
||||
references: [organization.id],
|
||||
@@ -339,6 +355,31 @@ export const apiFindOneNotification = notificationsSchema
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiCreateLark = notificationsSchema
|
||||
.pick({
|
||||
appBuildError: true,
|
||||
databaseBackup: true,
|
||||
dokployRestart: true,
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
serverThreshold: true,
|
||||
})
|
||||
.extend({
|
||||
webhookUrl: z.string().min(1),
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiUpdateLark = apiCreateLark.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
larkId: z.string().min(1),
|
||||
organizationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestLarkConnection = apiCreateLark.pick({
|
||||
webhookUrl: true,
|
||||
});
|
||||
|
||||
export const apiSendTest = notificationsSchema
|
||||
.extend({
|
||||
botToken: z.string(),
|
||||
|
||||
@@ -2,18 +2,21 @@ import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
type apiCreateDiscord,
|
||||
type apiCreateEmail,
|
||||
type apiCreateLark,
|
||||
type apiCreateGotify,
|
||||
type apiCreateNtfy,
|
||||
type apiCreateSlack,
|
||||
type apiCreateTelegram,
|
||||
type apiUpdateDiscord,
|
||||
type apiUpdateEmail,
|
||||
type apiUpdateLark,
|
||||
type apiUpdateGotify,
|
||||
type apiUpdateNtfy,
|
||||
type apiUpdateSlack,
|
||||
type apiUpdateTelegram,
|
||||
discord,
|
||||
email,
|
||||
lark,
|
||||
gotify,
|
||||
notifications,
|
||||
ntfy,
|
||||
@@ -585,6 +588,7 @@ export const findNotificationById = async (notificationId: string) => {
|
||||
email: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
if (!notification) {
|
||||
@@ -605,6 +609,94 @@ export const removeNotificationById = async (notificationId: string) => {
|
||||
return result[0];
|
||||
};
|
||||
|
||||
export const createLarkNotification = async (
|
||||
input: typeof apiCreateLark._type,
|
||||
organizationId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newLark = await tx
|
||||
.insert(lark)
|
||||
.values({
|
||||
webhookUrl: input.webhookUrl,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newLark) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting lark",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
larkId: newLark.larkId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "lark",
|
||||
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 updateLarkNotification = async (
|
||||
input: typeof apiUpdateLark._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(lark)
|
||||
.set({
|
||||
webhookUrl: input.webhookUrl,
|
||||
})
|
||||
.where(eq(lark.larkId, input.larkId))
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateNotificationById = async (
|
||||
notificationId: string,
|
||||
notificationData: Partial<Notification>,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendSlackNotification,
|
||||
@@ -44,11 +45,12 @@ export const sendBuildErrorNotifications = async ({
|
||||
slack: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack, gotify, ntfy } = notification;
|
||||
const { email, discord, telegram, slack, gotify, ntfy, lark } = notification;
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
BuildFailedEmail({
|
||||
@@ -126,11 +128,11 @@ export const sendBuildErrorNotifications = async ({
|
||||
gotify,
|
||||
decorate("⚠️", "Build Failed"),
|
||||
`${decorate("🛠️", `Project: ${projectName}`)}` +
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${applicationType}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${decorate("⚠️", `Error:\n${errorMessage}`)}` +
|
||||
`${decorate("🔗", `Build details:\n${buildLink}`)}`,
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${applicationType}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${decorate("⚠️", `Error:\n${errorMessage}`)}` +
|
||||
`${decorate("🔗", `Build details:\n${buildLink}`)}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,10 +143,10 @@ export const sendBuildErrorNotifications = async ({
|
||||
"warning",
|
||||
`view, Build details, ${buildLink}, clear=true;`,
|
||||
`🛠️Project: ${projectName}\n` +
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${applicationType}\n` +
|
||||
`🕒Date: ${date.toLocaleString()}\n` +
|
||||
`⚠️Error:\n${errorMessage}`,
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${applicationType}\n` +
|
||||
`🕒Date: ${date.toLocaleString()}\n` +
|
||||
`⚠️Error:\n${errorMessage}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -211,5 +213,117 @@ export const sendBuildErrorNotifications = async ({
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
const limitCharacter = 800;
|
||||
const truncatedErrorMessage = errorMessage.substring(0, limitCharacter);
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content: "⚠️ Build Failed",
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: "red",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Project:**\n${projectName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Type:**\n${applicationType}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Error Message:**\n\`\`\`\n${truncatedErrorMessage}\n\`\`\``,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Application:**\n${applicationName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Date:**\n${format(date, "PP pp")}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
tag: "button",
|
||||
text: {
|
||||
tag: "plain_text",
|
||||
content: "View Build Details",
|
||||
},
|
||||
type: "danger",
|
||||
width: "default",
|
||||
size: "medium",
|
||||
behaviors: [
|
||||
{
|
||||
type: "open_url",
|
||||
default_url: buildLink,
|
||||
pc_url: "",
|
||||
ios_url: "",
|
||||
android_url: ""
|
||||
}
|
||||
],
|
||||
margin: "0px 0px 0px 0px"
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendSlackNotification,
|
||||
@@ -45,11 +46,12 @@ export const sendBuildSuccessNotifications = async ({
|
||||
slack: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack, gotify, ntfy } = notification;
|
||||
const { email, discord, telegram, slack, gotify, ntfy, lark } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
@@ -121,10 +123,10 @@ export const sendBuildSuccessNotifications = async ({
|
||||
gotify,
|
||||
decorate("✅", "Build Success"),
|
||||
`${decorate("🛠️", `Project: ${projectName}`)}` +
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${applicationType}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${decorate("🔗", `Build details:\n${buildLink}`)}`,
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${applicationType}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${decorate("🔗", `Build details:\n${buildLink}`)}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,9 +137,9 @@ export const sendBuildSuccessNotifications = async ({
|
||||
"white_check_mark",
|
||||
`view, Build details, ${buildLink}, clear=true;`,
|
||||
`🛠Project: ${projectName}\n` +
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${applicationType}\n` +
|
||||
`🕒Date: ${date.toLocaleString()}`,
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${applicationType}\n` +
|
||||
`🕒Date: ${date.toLocaleString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,5 +212,109 @@ export const sendBuildSuccessNotifications = async ({
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content: "✅ Build Success",
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: "green",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Project:**\n${projectName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Type:**\n${applicationType}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Application:**\n${applicationName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Date:**\n${format(date, "PP pp")}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
tag: "button",
|
||||
text: {
|
||||
tag: "plain_text",
|
||||
content: "View Build Details",
|
||||
},
|
||||
type: "primary",
|
||||
width: "default",
|
||||
size: "medium",
|
||||
behaviors: [
|
||||
{
|
||||
type: "open_url",
|
||||
default_url: buildLink,
|
||||
pc_url: "",
|
||||
ios_url: "",
|
||||
android_url: ""
|
||||
}
|
||||
],
|
||||
margin: "0px 0px 0px 0px"
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendSlackNotification,
|
||||
@@ -44,11 +45,12 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
slack: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack, gotify, ntfy } = notification;
|
||||
const { email, discord, telegram, slack, gotify, ntfy, lark } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
@@ -118,11 +120,11 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
},
|
||||
...(type === "error" && errorMessage
|
||||
? [
|
||||
{
|
||||
name: decorate("`⚠️`", "Error Message"),
|
||||
value: `\`\`\`${errorMessage}\`\`\``,
|
||||
},
|
||||
]
|
||||
{
|
||||
name: decorate("`⚠️`", "Error Message"),
|
||||
value: `\`\`\`${errorMessage}\`\`\``,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
@@ -143,11 +145,11 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
`Database Backup ${type === "success" ? "Successful" : "Failed"}`,
|
||||
),
|
||||
`${decorate("🛠️", `Project: ${projectName}`)}` +
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${databaseType}`)}` +
|
||||
`${decorate("📂", `Database Name: ${databaseName}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`,
|
||||
`${decorate("⚙️", `Application: ${applicationName}`)}` +
|
||||
`${decorate("❔", `Type: ${databaseType}`)}` +
|
||||
`${decorate("📂", `Database Name: ${databaseName}`)}` +
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -158,11 +160,11 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
`${type === "success" ? "white_check_mark" : "x"}`,
|
||||
"",
|
||||
`🛠Project: ${projectName}\n` +
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${databaseType}\n` +
|
||||
`📂Database Name: ${databaseName}` +
|
||||
`🕒Date: ${date.toLocaleString()}\n` +
|
||||
`${type === "error" && errorMessage ? `❌Error:\n${errorMessage}` : ""}`,
|
||||
`⚙️Application: ${applicationName}\n` +
|
||||
`❔Type: ${databaseType}\n` +
|
||||
`📂Database Name: ${databaseName}` +
|
||||
`🕒Date: ${date.toLocaleString()}\n` +
|
||||
`${type === "error" && errorMessage ? `❌Error:\n${errorMessage}` : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,12 +196,12 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
fields: [
|
||||
...(type === "error" && errorMessage
|
||||
? [
|
||||
{
|
||||
title: "Error Message",
|
||||
value: errorMessage,
|
||||
short: false,
|
||||
},
|
||||
]
|
||||
{
|
||||
title: "Error Message",
|
||||
value: errorMessage,
|
||||
short: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: "Project",
|
||||
@@ -238,5 +240,119 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
const limitCharacter = 800;
|
||||
const truncatedErrorMessage = errorMessage && errorMessage.length > limitCharacter
|
||||
? errorMessage.substring(0, limitCharacter)
|
||||
: errorMessage;
|
||||
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content:
|
||||
type === "success"
|
||||
? "✅ Database Backup Successful"
|
||||
: "❌ Database Backup Failed",
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: type === "success" ? "green" : "red",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Project:**\n${projectName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Database Type:**\n${databaseType}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Status:**\n${type === "success" ? "Successful" : "Failed"}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Application:**\n${applicationName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Database Name:**\n${databaseName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Date:**\n${format(date, "PP pp")}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
...(type === "error" && truncatedErrorMessage
|
||||
? [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Error Message:**\n\`\`\`\n${truncatedErrorMessage}\n\`\`\``,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
]
|
||||
: [])
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendSlackNotification,
|
||||
@@ -31,11 +32,12 @@ export const sendDockerCleanupNotifications = async (
|
||||
slack: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack, gotify, ntfy } = notification;
|
||||
const { email, discord, telegram, slack, gotify, ntfy, lark } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
@@ -91,7 +93,7 @@ export const sendDockerCleanupNotifications = async (
|
||||
gotify,
|
||||
decorate("✅", "Docker Cleanup"),
|
||||
`${decorate("🕒", `Date: ${date.toLocaleString()}`)}` +
|
||||
`${decorate("📜", `Message:\n${message}`)}`,
|
||||
`${decorate("📜", `Message:\n${message}`)}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,5 +137,83 @@ export const sendDockerCleanupNotifications = async (
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content: "✅ Docker Cleanup",
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: "green",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Status:**\nSuccessful`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Cleanup Details:**\n${message}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Date:**\n${format(date, "PP pp")}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendLarkNotification,
|
||||
sendGotifyNotification,
|
||||
sendNtfyNotification,
|
||||
sendSlackNotification,
|
||||
@@ -25,11 +26,12 @@ export const sendDokployRestartNotifications = async () => {
|
||||
slack: true,
|
||||
gotify: true,
|
||||
ntfy: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack, gotify, ntfy } = notification;
|
||||
const { email, discord, telegram, slack, gotify, ntfy, lark } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
@@ -135,5 +137,81 @@ export const sendDokployRestartNotifications = async () => {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
try {
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content: "✅ Dokploy Server Restarted",
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: "green",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Status:**\nSuccessful`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Restart Time:**\n${format(date, "PP pp")}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { db } from "../../db";
|
||||
import { notifications } from "../../db/schema";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendLarkNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
@@ -34,6 +35,7 @@ export const sendServerThresholdNotifications = async (
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
lark: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -41,7 +43,7 @@ export const sendServerThresholdNotifications = async (
|
||||
const typeColor = 0xff0000; // Rojo para indicar alerta
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { discord, telegram, slack } = notification;
|
||||
const { discord, telegram, slack, lark } = notification;
|
||||
|
||||
if (discord) {
|
||||
const decorate = (decoration: string, text: string) =>
|
||||
@@ -151,5 +153,101 @@ export const sendServerThresholdNotifications = async (
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (lark) {
|
||||
await sendLarkNotification(lark, {
|
||||
msg_type: "interactive",
|
||||
card: {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
update_multi: true,
|
||||
style: {
|
||||
text_size: {
|
||||
normal_v2: {
|
||||
default: "normal",
|
||||
pc: "normal",
|
||||
mobile: "heading"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
tag: "plain_text",
|
||||
content: `⚠️ Server ${payload.Type} Alert`,
|
||||
},
|
||||
subtitle: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
template: "red",
|
||||
padding: "12px 12px 12px 12px"
|
||||
},
|
||||
body: {
|
||||
direction: "vertical",
|
||||
padding: "12px 12px 12px 12px",
|
||||
elements: [
|
||||
{
|
||||
tag: "column_set",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Server Name:**\n${payload.ServerName}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Current Value:**\n${payload.Value.toFixed(2)}%`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Alert Message:**\n${payload.Message}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Type:**\n${payload.Type === "CPU" ? "🔲" : "💾"} ${payload.Type}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Threshold:**\n${payload.Threshold.toFixed(2)}%`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
},
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**Alert Time:**\n${date.toLocaleString()}`,
|
||||
text_align: "left",
|
||||
text_size: "normal_v2"
|
||||
}
|
||||
],
|
||||
vertical_align: "top",
|
||||
weight: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
discord,
|
||||
email,
|
||||
lark,
|
||||
gotify,
|
||||
ntfy,
|
||||
slack,
|
||||
@@ -151,3 +152,18 @@ export const sendNtfyNotification = async (
|
||||
throw new Error(`Failed to send ntfy notification: ${response.statusText}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendLarkNotification = async (
|
||||
connection: typeof lark.$inferInsert,
|
||||
message: any,
|
||||
) => {
|
||||
try {
|
||||
await fetch(connection.webhookUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user