mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(notifications): WIP add schema and modal
This commit is contained in:
@@ -23,6 +23,7 @@ import { dockerRouter } from "./routers/docker";
|
||||
import { composeRouter } from "./routers/compose";
|
||||
import { registryRouter } from "./routers/registry";
|
||||
import { clusterRouter } from "./routers/cluster";
|
||||
import { notificationRouter } from "./routers/notification";
|
||||
|
||||
/**
|
||||
* This is the primary router for your server.
|
||||
@@ -54,6 +55,7 @@ export const appRouter = createTRPCRouter({
|
||||
port: portRouter,
|
||||
registry: registryRouter,
|
||||
cluster: clusterRouter,
|
||||
notification: notificationRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
150
server/api/routers/notification.ts
Normal file
150
server/api/routers/notification.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import {
|
||||
adminProcedure,
|
||||
createTRPCRouter,
|
||||
protectedProcedure,
|
||||
} from "@/server/api/trpc";
|
||||
import { db } from "@/server/db";
|
||||
import {
|
||||
apiCreateDestination,
|
||||
apiCreateDiscord,
|
||||
apiCreateEmail,
|
||||
apiCreateSlack,
|
||||
apiCreateTelegram,
|
||||
apiFindOneNotification,
|
||||
apiUpdateDestination,
|
||||
} from "@/server/db/schema";
|
||||
import { HeadBucketCommand, S3Client } from "@aws-sdk/client-s3";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { updateDestinationById } from "../services/destination";
|
||||
import {
|
||||
createDiscordNotification,
|
||||
createEmailNotification,
|
||||
createSlackNotification,
|
||||
createTelegramNotification,
|
||||
findNotificationById,
|
||||
removeNotificationById,
|
||||
} from "../services/notification";
|
||||
|
||||
export const notificationRouter = createTRPCRouter({
|
||||
createSlack: adminProcedure
|
||||
.input(apiCreateSlack)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await createSlackNotification(input);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the destination",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
createTelegram: adminProcedure
|
||||
.input(apiCreateTelegram)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await createTelegramNotification(input);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the destination",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
createDiscord: adminProcedure
|
||||
.input(apiCreateDiscord)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await createDiscordNotification(input);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the destination",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
createEmail: adminProcedure
|
||||
.input(apiCreateEmail)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await createEmailNotification(input);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the destination",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
removeNotification: adminProcedure
|
||||
.input(apiFindOneNotification)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await removeNotificationById(input.notificationId);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to delete this notification",
|
||||
});
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.input(apiFindOneNotification)
|
||||
.query(async ({ input }) => {
|
||||
const notification = await findNotificationById(input.notificationId);
|
||||
return notification;
|
||||
}),
|
||||
testConnection: adminProcedure
|
||||
.input(apiCreateDestination)
|
||||
.mutation(async ({ input }) => {
|
||||
const { secretAccessKey, bucket, region, endpoint, accessKey } = input;
|
||||
const s3Client = new S3Client({
|
||||
region: region,
|
||||
...(endpoint && {
|
||||
endpoint: endpoint,
|
||||
}),
|
||||
credentials: {
|
||||
accessKeyId: accessKey,
|
||||
secretAccessKey: secretAccessKey,
|
||||
},
|
||||
forcePathStyle: true,
|
||||
});
|
||||
const headBucketCommand = new HeadBucketCommand({ Bucket: bucket });
|
||||
|
||||
try {
|
||||
await s3Client.send(headBucketCommand);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to connect to bucket",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
all: adminProcedure.query(async () => {
|
||||
return await db.query.notifications.findMany({
|
||||
with: {
|
||||
slack: true,
|
||||
telegram: true,
|
||||
discord: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
}),
|
||||
update: adminProcedure
|
||||
.input(apiUpdateDestination)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return await updateDestinationById(input.destinationId, input);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to update this destination",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
241
server/api/services/notification.ts
Normal file
241
server/api/services/notification.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
import { db } from "@/server/db";
|
||||
import {
|
||||
type apiCreateDiscord,
|
||||
type apiCreateEmail,
|
||||
type apiCreateSlack,
|
||||
type apiCreateTelegram,
|
||||
discord,
|
||||
email,
|
||||
notifications,
|
||||
slack,
|
||||
telegram,
|
||||
} from "@/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export type Notification = typeof notifications.$inferSelect;
|
||||
|
||||
export const createSlackNotification = async (
|
||||
input: typeof apiCreateSlack._type,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newSlack = await tx
|
||||
.insert(slack)
|
||||
.values({
|
||||
channel: input.channel,
|
||||
webhookUrl: input.webhookUrl,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newSlack) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting slack",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
slackId: newSlack.slackId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
userJoin: input.userJoin,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDestination) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting notification",
|
||||
});
|
||||
}
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const createTelegramNotification = async (
|
||||
input: typeof apiCreateTelegram._type,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newTelegram = await tx
|
||||
.insert(telegram)
|
||||
.values({
|
||||
botToken: input.botToken,
|
||||
chatId: input.chatId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newTelegram) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting telegram",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
telegramId: newTelegram.telegramId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
userJoin: input.userJoin,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDestination) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting notification",
|
||||
});
|
||||
}
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const createDiscordNotification = async (
|
||||
input: typeof apiCreateDiscord._type,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newDiscord = await tx
|
||||
.insert(discord)
|
||||
.values({
|
||||
webhookUrl: input.webhookUrl,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDiscord) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting discord",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
discordId: newDiscord.discordId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
userJoin: input.userJoin,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDestination) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting notification",
|
||||
});
|
||||
}
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const createEmailNotification = async (
|
||||
input: typeof apiCreateEmail._type,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newEmail = await tx
|
||||
.insert(email)
|
||||
.values({
|
||||
smtpServer: input.smtpServer,
|
||||
smtpPort: input.smtpPort,
|
||||
username: input.username,
|
||||
password: input.password,
|
||||
toAddresses: input.toAddresses,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newEmail) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting email",
|
||||
});
|
||||
}
|
||||
|
||||
const newDestination = await tx
|
||||
.insert(notifications)
|
||||
.values({
|
||||
emailId: newEmail.emailId,
|
||||
name: input.name,
|
||||
appDeploy: input.appDeploy,
|
||||
userJoin: input.userJoin,
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newDestination) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Inserting notification",
|
||||
});
|
||||
}
|
||||
|
||||
return newDestination;
|
||||
});
|
||||
};
|
||||
|
||||
export const findNotificationById = async (notificationId: string) => {
|
||||
const notification = await db.query.notifications.findFirst({
|
||||
where: eq(notifications.notificationId, notificationId),
|
||||
with: {
|
||||
slack: true,
|
||||
telegram: true,
|
||||
discord: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
if (!notification) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Notification not found",
|
||||
});
|
||||
}
|
||||
return notification;
|
||||
};
|
||||
|
||||
export const removeNotificationById = async (notificationId: string) => {
|
||||
const result = await db
|
||||
.delete(notifications)
|
||||
.where(eq(notifications.notificationId, notificationId))
|
||||
.returning();
|
||||
|
||||
return result[0];
|
||||
};
|
||||
|
||||
export const updateDestinationById = async (
|
||||
notificationId: string,
|
||||
notificationData: Partial<Notification>,
|
||||
) => {
|
||||
const result = await db
|
||||
.update(notifications)
|
||||
.set({
|
||||
...notificationData,
|
||||
})
|
||||
.where(eq(notifications.notificationId, notificationId))
|
||||
.returning();
|
||||
|
||||
return result[0];
|
||||
};
|
||||
Reference in New Issue
Block a user