From 4a9d7225c9307024ef9edca865519a7c4f7bb74d Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Wed, 8 Jan 2025 13:44:59 +1100 Subject: [PATCH 01/62] chore: typo "accesed" changed to "accessed" for TS code --- .../settings/users/add-permissions.tsx | 22 +++++----- apps/dokploy/server/api/routers/project.ts | 42 +++++++++---------- packages/server/src/db/schema/user.ts | 12 +++--- packages/server/src/services/user.ts | 20 ++++----- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx index bb82c9829..7c1f50375 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx @@ -30,8 +30,8 @@ import { toast } from "sonner"; import { z } from "zod"; const addPermissions = z.object({ - accesedProjects: z.array(z.string()).optional(), - accesedServices: z.array(z.string()).optional(), + accessedProjects: z.array(z.string()).optional(), + accessedServices: z.array(z.string()).optional(), canCreateProjects: z.boolean().optional().default(false), canCreateServices: z.boolean().optional().default(false), canDeleteProjects: z.boolean().optional().default(false), @@ -66,8 +66,8 @@ export const AddUserPermissions = ({ userId }: Props) => { const form = useForm({ defaultValues: { - accesedProjects: [], - accesedServices: [], + accessedProjects: [], + accessedServices: [], }, resolver: zodResolver(addPermissions), }); @@ -75,8 +75,8 @@ export const AddUserPermissions = ({ userId }: Props) => { useEffect(() => { if (data) { form.reset({ - accesedProjects: data.accesedProjects || [], - accesedServices: data.accesedServices || [], + accessedProjects: data.accessedProjects || [], + accessedServices: data.accessedServices || [], canCreateProjects: data.canCreateProjects, canCreateServices: data.canCreateServices, canDeleteProjects: data.canDeleteProjects, @@ -98,8 +98,8 @@ export const AddUserPermissions = ({ userId }: Props) => { canDeleteServices: data.canDeleteServices, canDeleteProjects: data.canDeleteProjects, canAccessToTraefikFiles: data.canAccessToTraefikFiles, - accesedProjects: data.accesedProjects || [], - accesedServices: data.accesedServices || [], + accessedProjects: data.accessedProjects || [], + accessedServices: data.accessedServices || [], canAccessToDocker: data.canAccessToDocker, canAccessToAPI: data.canAccessToAPI, canAccessToSSHKeys: data.canAccessToSSHKeys, @@ -318,7 +318,7 @@ export const AddUserPermissions = ({ userId }: Props) => { /> (
@@ -339,7 +339,7 @@ export const AddUserPermissions = ({ userId }: Props) => { { return ( { { return ( { if (ctx.user.rol === "user") { - const { accesedServices } = await findUserByAuthId(ctx.user.authId); + const { accessedServices } = await findUserByAuthId(ctx.user.authId); await checkProjectAccess(ctx.user.authId, "access", input.projectId); @@ -79,28 +79,28 @@ export const projectRouter = createTRPCRouter({ ), with: { compose: { - where: buildServiceFilter(compose.composeId, accesedServices), + where: buildServiceFilter(compose.composeId, accessedServices), }, applications: { where: buildServiceFilter( applications.applicationId, - accesedServices, + accessedServices, ), }, mariadb: { - where: buildServiceFilter(mariadb.mariadbId, accesedServices), + where: buildServiceFilter(mariadb.mariadbId, accessedServices), }, mongo: { - where: buildServiceFilter(mongo.mongoId, accesedServices), + where: buildServiceFilter(mongo.mongoId, accessedServices), }, mysql: { - where: buildServiceFilter(mysql.mysqlId, accesedServices), + where: buildServiceFilter(mysql.mysqlId, accessedServices), }, postgres: { - where: buildServiceFilter(postgres.postgresId, accesedServices), + where: buildServiceFilter(postgres.postgresId, accessedServices), }, redis: { - where: buildServiceFilter(redis.redisId, accesedServices), + where: buildServiceFilter(redis.redisId, accessedServices), }, }, }); @@ -125,18 +125,18 @@ export const projectRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { if (ctx.user.rol === "user") { - const { accesedProjects, accesedServices } = await findUserByAuthId( + const { accessedProjects, accessedServices } = await findUserByAuthId( ctx.user.authId, ); - if (accesedProjects.length === 0) { + if (accessedProjects.length === 0) { return []; } const query = await db.query.projects.findMany({ where: and( sql`${projects.projectId} IN (${sql.join( - accesedProjects.map((projectId) => sql`${projectId}`), + accessedProjects.map((projectId) => sql`${projectId}`), sql`, `, )})`, eq(projects.adminId, ctx.user.adminId), @@ -145,27 +145,27 @@ export const projectRouter = createTRPCRouter({ applications: { where: buildServiceFilter( applications.applicationId, - accesedServices, + accessedServices, ), with: { domains: true }, }, mariadb: { - where: buildServiceFilter(mariadb.mariadbId, accesedServices), + where: buildServiceFilter(mariadb.mariadbId, accessedServices), }, mongo: { - where: buildServiceFilter(mongo.mongoId, accesedServices), + where: buildServiceFilter(mongo.mongoId, accessedServices), }, mysql: { - where: buildServiceFilter(mysql.mysqlId, accesedServices), + where: buildServiceFilter(mysql.mysqlId, accessedServices), }, postgres: { - where: buildServiceFilter(postgres.postgresId, accesedServices), + where: buildServiceFilter(postgres.postgresId, accessedServices), }, redis: { - where: buildServiceFilter(redis.redisId, accesedServices), + where: buildServiceFilter(redis.redisId, accessedServices), }, compose: { - where: buildServiceFilter(compose.composeId, accesedServices), + where: buildServiceFilter(compose.composeId, accessedServices), with: { domains: true }, }, }, @@ -239,10 +239,10 @@ export const projectRouter = createTRPCRouter({ } }), }); -function buildServiceFilter(fieldName: AnyPgColumn, accesedServices: string[]) { - return accesedServices.length > 0 +function buildServiceFilter(fieldName: AnyPgColumn, accessedServices: string[]) { + return accessedServices.length > 0 ? sql`${fieldName} IN (${sql.join( - accesedServices.map((serviceId) => sql`${serviceId}`), + accessedServices.map((serviceId) => sql`${serviceId}`), sql`, `, )})` : sql`1 = 0`; diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index fec3d1277..735898f9a 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -40,11 +40,11 @@ export const users = pgTable("user", { canAccessToTraefikFiles: boolean("canAccessToTraefikFiles") .notNull() .default(false), - accesedProjects: text("accesedProjects") + accessedProjects: text("accesedProjects") .array() .notNull() .default(sql`ARRAY[]::text[]`), - accesedServices: text("accesedServices") + accessedServices: text("accesedServices") .array() .notNull() .default(sql`ARRAY[]::text[]`), @@ -73,8 +73,8 @@ const createSchema = createInsertSchema(users, { token: z.string().min(1), isRegistered: z.boolean().optional(), adminId: z.string(), - accesedProjects: z.array(z.string()).optional(), - accesedServices: z.array(z.string()).optional(), + accessedProjects: z.array(z.string()).optional(), + accessedServices: z.array(z.string()).optional(), canCreateProjects: z.boolean().optional(), canCreateServices: z.boolean().optional(), canDeleteProjects: z.boolean().optional(), @@ -106,8 +106,8 @@ export const apiAssignPermissions = createSchema canCreateServices: true, canDeleteProjects: true, canDeleteServices: true, - accesedProjects: true, - accesedServices: true, + accessedProjects: true, + accessedServices: true, canAccessToTraefikFiles: true, canAccessToDocker: true, canAccessToAPI: true, diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 1cfe1260d..c8a9849c7 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -54,7 +54,7 @@ export const addNewProject = async (authId: string, projectId: string) => { await db .update(users) .set({ - accesedProjects: [...user.accesedProjects, projectId], + accessedProjects: [...user.accessedProjects, projectId], }) .where(eq(users.authId, authId)); }; @@ -64,7 +64,7 @@ export const addNewService = async (authId: string, serviceId: string) => { await db .update(users) .set({ - accesedServices: [...user.accesedServices, serviceId], + accessedServices: [...user.accessedServices, serviceId], }) .where(eq(users.authId, authId)); }; @@ -73,8 +73,8 @@ export const canPerformCreationService = async ( userId: string, projectId: string, ) => { - const { accesedProjects, canCreateServices } = await findUserByAuthId(userId); - const haveAccessToProject = accesedProjects.includes(projectId); + const { accessedProjects, canCreateServices } = await findUserByAuthId(userId); + const haveAccessToProject = accessedProjects.includes(projectId); if (canCreateServices && haveAccessToProject) { return true; @@ -87,8 +87,8 @@ export const canPerformAccessService = async ( userId: string, serviceId: string, ) => { - const { accesedServices } = await findUserByAuthId(userId); - const haveAccessToService = accesedServices.includes(serviceId); + const { accessedServices } = await findUserByAuthId(userId); + const haveAccessToService = accessedServices.includes(serviceId); if (haveAccessToService) { return true; @@ -101,8 +101,8 @@ export const canPeformDeleteService = async ( authId: string, serviceId: string, ) => { - const { accesedServices, canDeleteServices } = await findUserByAuthId(authId); - const haveAccessToService = accesedServices.includes(serviceId); + const { accessedServices, canDeleteServices } = await findUserByAuthId(authId); + const haveAccessToService = accessedServices.includes(serviceId); if (canDeleteServices && haveAccessToService) { return true; @@ -135,9 +135,9 @@ export const canPerformAccessProject = async ( authId: string, projectId: string, ) => { - const { accesedProjects } = await findUserByAuthId(authId); + const { accessedProjects } = await findUserByAuthId(authId); - const haveAccessToProject = accesedProjects.includes(projectId); + const haveAccessToProject = accessedProjects.includes(projectId); if (haveAccessToProject) { return true; From dd3618bfd95e5e6eef5c71ec31a24c5b7d0bdcaf Mon Sep 17 00:00:00 2001 From: shiqocred <90097870+shiqocred@users.noreply.github.com> Date: Sun, 12 Jan 2025 00:55:31 +0700 Subject: [PATCH 02/62] Add inline button telegram (#3) * Update utils.ts add type inline button * Update dokploy-restart.ts fixing format massage and adding [] for inline button type * Update docker-cleanup.ts fixing telegram message * Update database-backup.ts fixing telegram message * Update build-error.ts fixing message and adding button logs view * Update build-success.ts fixing message, adding domains props, adding inline button * Update compose.ts adding get domains compose and send to notif * Update application.ts adding get domains and send it to notif --- packages/server/src/services/application.ts | 4 +++ packages/server/src/services/compose.ts | 4 +++ .../src/utils/notifications/build-error.ts | 25 +++++++------ .../src/utils/notifications/build-success.ts | 36 +++++++++++++------ .../utils/notifications/database-backup.ts | 21 +++++------ .../src/utils/notifications/docker-cleanup.ts | 8 ++--- .../utils/notifications/dokploy-restart.ts | 7 ++-- .../server/src/utils/notifications/utils.ts | 7 ++++ 8 files changed, 68 insertions(+), 44 deletions(-) diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index e2ed407fc..fa6909525 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -176,6 +176,7 @@ export const deployApplication = async ({ }) => { const application = await findApplicationById(applicationId); const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`; + const domains = application.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeployment({ applicationId: applicationId, title: titleLog, @@ -213,6 +214,7 @@ export const deployApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, + domains }); } catch (error) { await updateDeploymentStatus(deployment.deploymentId, "error"); @@ -285,6 +287,7 @@ export const deployRemoteApplication = async ({ }) => { const application = await findApplicationById(applicationId); const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`; + const domains = application.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeployment({ applicationId: applicationId, title: titleLog, @@ -332,6 +335,7 @@ export const deployRemoteApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, + domains }); } catch (error) { // @ts-ignore diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 50459c450..2ed3d4620 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -209,6 +209,7 @@ export const deployCompose = async ({ const buildLink = `${await getDokployUrl()}/dashboard/project/${ compose.projectId }/services/compose/${compose.composeId}?tab=deployments`; + const domains = compose.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeploymentCompose({ composeId: composeId, title: titleLog, @@ -243,6 +244,7 @@ export const deployCompose = async ({ applicationType: "compose", buildLink, adminId: compose.project.adminId, + domains }); } catch (error) { await updateDeploymentStatus(deployment.deploymentId, "error"); @@ -313,6 +315,7 @@ export const deployRemoteCompose = async ({ const buildLink = `${await getDokployUrl()}/dashboard/project/${ compose.projectId }/services/compose/${compose.composeId}?tab=deployments`; + const domains = compose.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeploymentCompose({ composeId: composeId, title: titleLog, @@ -366,6 +369,7 @@ export const deployRemoteCompose = async ({ applicationType: "compose", buildLink, adminId: compose.project.adminId, + domains }); } catch (error) { // @ts-ignore diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 695b37863..ef4c16cc0 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -3,6 +3,7 @@ import { notifications } from "@dokploy/server/db/schema"; import BuildFailedEmail from "@dokploy/server/emails/emails/build-failed"; import { renderAsync } from "@react-email/components"; import { and, eq } from "drizzle-orm"; +import { format } from "date-fns"; import { sendDiscordNotification, sendEmailNotification, @@ -113,21 +114,19 @@ export const sendBuildErrorNotifications = async ({ } if (telegram) { + const inlineButton = [ + [ + { + text: "Deployment Logs", + url: buildLink, + }, + ], + ]; + await sendTelegramNotification( telegram, - ` - ⚠️ Build Failed - - Project: ${projectName} - Application: ${applicationName} - Type: ${applicationType} - Time: ${date.toLocaleString()} - - Error: -
${errorMessage}
- - Build Details: ${buildLink} - `, + `⚠️ Build Failed\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}\n\nError:\n
${errorMessage}
`, + inlineButton ); } diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 402b0cd2c..3bfa18a8e 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -16,6 +16,10 @@ interface Props { applicationType: string; buildLink: string; adminId: string; + domains: { + host: string; + https: boolean; + }[]; } export const sendBuildSuccessNotifications = async ({ @@ -24,6 +28,7 @@ export const sendBuildSuccessNotifications = async ({ applicationType, buildLink, adminId, + domains }: Props) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); @@ -107,18 +112,29 @@ export const sendBuildSuccessNotifications = async ({ } if (telegram) { + const chunkArray = (array: T[], chunkSize: number): T[][] => + Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, i) => array.slice(i * chunkSize, i * chunkSize + chunkSize) + ); + + const inlineButton = [ + [ + { + text: "Deployment Logs", + url: buildLink, + }, + ], + ...chunkArray(domains, 2).map((chunk) => + chunk.map((data) => ({ + text: data.host, + url: `${data.https ? "https" : "http"}://${data.host}`, + })) + ), + ]; + await sendTelegramNotification( telegram, - ` - ✅ Build Success - - Project: ${projectName} - Application: ${applicationName} - Type: ${applicationType} - Time: ${date.toLocaleString()} - - Build Details: ${buildLink} - `, + `✅ Build Success\n\nProject: ${projectName}\nApplication: ${applicationName}Type: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + inlineButton ); } diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 3aec6f3d3..6594f87ce 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -3,6 +3,7 @@ import { notifications } from "@dokploy/server/db/schema"; import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup"; import { renderAsync } from "@react-email/components"; import { and, eq } from "drizzle-orm"; +import { format } from "date-fns"; import { sendDiscordNotification, sendEmailNotification, @@ -121,19 +122,15 @@ export const sendDatabaseBackupNotifications = async ({ } if (telegram) { + const isError = type === "error" && errorMessage; + const statusEmoji = type === "success" ? "✅" : "❌"; - const messageText = ` - ${statusEmoji} Database Backup ${type === "success" ? "Successful" : "Failed"} - - Project: ${projectName} - Application: ${applicationName} - Type: ${databaseType} - Time: ${date.toLocaleString()} - - Status: ${type === "success" ? "Successful" : "Failed"} - ${type === "error" && errorMessage ? `Error: ${errorMessage}` : ""} - `; - await sendTelegramNotification(telegram, messageText); + const typeStatus = type === "success" ? "Successful" : "Failed"; + const errorMsg = isError ? `\n\nError:\n
${errorMessage}
` : ""; + + const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; + + await sendTelegramNotification(telegram, messageText, []); } if (slack) { diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index c95c79067..8623867f4 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -3,6 +3,7 @@ import { notifications } from "@dokploy/server/db/schema"; import DockerCleanupEmail from "@dokploy/server/emails/emails/docker-cleanup"; import { renderAsync } from "@react-email/components"; import { and, eq } from "drizzle-orm"; +import { format } from "date-fns"; import { sendDiscordNotification, sendEmailNotification, @@ -82,11 +83,8 @@ export const sendDockerCleanupNotifications = async ( if (telegram) { await sendTelegramNotification( telegram, - ` - ✅ Docker Cleanup - Message: ${message} - Time: ${date.toLocaleString()} - `, + `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + [] ); } diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 16170349b..ffa838b70 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -9,6 +9,7 @@ import { sendSlackNotification, sendTelegramNotification, } from "./utils"; +import { format } from "date-fns"; export const sendDokployRestartNotifications = async () => { const date = new Date(); @@ -67,10 +68,8 @@ export const sendDokployRestartNotifications = async () => { if (telegram) { await sendTelegramNotification( telegram, - ` - ✅ Dokploy Serverd Restarted - Time: ${date.toLocaleString()} - `, + `✅ Dokploy Serverd Restarted\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + [] ); } diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts index 8327c33d2..963011564 100644 --- a/packages/server/src/utils/notifications/utils.ts +++ b/packages/server/src/utils/notifications/utils.ts @@ -55,6 +55,10 @@ export const sendDiscordNotification = async ( export const sendTelegramNotification = async ( connection: typeof telegram.$inferInsert, messageText: string, + inlineButton: { + text: string; + url: string; + }[][] ) => { try { const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`; @@ -66,6 +70,9 @@ export const sendTelegramNotification = async ( text: messageText, parse_mode: "HTML", disable_web_page_preview: true, + reply_markup: { + inline_keyboard: inlineButton, + }, }), }); } catch (err) { From 1d8db07fa1a2841ec899f76eb969d513da908865 Mon Sep 17 00:00:00 2001 From: shiqocred <90097870+shiqocred@users.noreply.github.com> Date: Sun, 12 Jan 2025 01:20:39 +0700 Subject: [PATCH 03/62] Add inline button telegram (#4) * Update utils.ts add type inline button * Update dokploy-restart.ts fixing format massage and adding [] for inline button type * Update docker-cleanup.ts fixing telegram message * Update database-backup.ts fixing telegram message * Update build-error.ts fixing message and adding button logs view * Update build-success.ts fixing message, adding domains props, adding inline button * Update compose.ts adding get domains compose and send to notif * Update application.ts adding get domains and send it to notif * Update build-success.ts fix space * Update dokploy-restart.ts fixing space --- packages/server/src/utils/notifications/build-success.ts | 2 +- packages/server/src/utils/notifications/dokploy-restart.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 3bfa18a8e..7b03d0c19 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -133,7 +133,7 @@ export const sendBuildSuccessNotifications = async ({ await sendTelegramNotification( telegram, - `✅ Build Success\n\nProject: ${projectName}\nApplication: ${applicationName}Type: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + `✅ Build Success\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, inlineButton ); } diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index ffa838b70..7ee50b6a9 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -68,7 +68,7 @@ export const sendDokployRestartNotifications = async () => { if (telegram) { await sendTelegramNotification( telegram, - `✅ Dokploy Serverd Restarted\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + `✅ Dokploy Serverd Restarted\n\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, [] ); } From c0b8a411bd5a75a0cbeba3b7447a8726c2e993ba Mon Sep 17 00:00:00 2001 From: shiqocred <90097870+shiqocred@users.noreply.github.com> Date: Sun, 12 Jan 2025 02:04:10 +0700 Subject: [PATCH 04/62] Update build-success.ts (#5) add format --- packages/server/src/utils/notifications/build-success.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 7b03d0c19..366b4591f 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -3,6 +3,7 @@ import { notifications } from "@dokploy/server/db/schema"; import BuildSuccessEmail from "@dokploy/server/emails/emails/build-success"; import { renderAsync } from "@react-email/components"; import { and, eq } from "drizzle-orm"; +import { format } from "date-fns"; import { sendDiscordNotification, sendEmailNotification, From d2094d6d76821b65c95c91bb3c987e5567574364 Mon Sep 17 00:00:00 2001 From: shiqocred <90097870+shiqocred@users.noreply.github.com> Date: Sun, 12 Jan 2025 02:14:09 +0700 Subject: [PATCH 05/62] Update notification.ts (#6) fix router notification --- apps/dokploy/server/api/routers/notification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index f88695039..61a1310e2 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -134,7 +134,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestTelegramConnection) .mutation(async ({ input }) => { try { - await sendTelegramNotification(input, "Hi, From Dokploy 👋"); + await sendTelegramNotification(input, "Hi, From Dokploy 👋", []); return true; } catch (error) { throw new TRPCError({ From 537950dd9f26aef12b3498845641d259aace3c4f Mon Sep 17 00:00:00 2001 From: shiqocred <90097870+shiqocred@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:13:13 +0700 Subject: [PATCH 06/62] Revision notification (#7) * Update build-success.ts * Update compose.ts * Update application.ts * Update notification.ts * Update utils.ts * Update dokploy-restart.ts * Update docker-cleanup.ts * Update database-backup.ts * Update build-success.ts * Update build-success.ts --- apps/dokploy/server/api/routers/notification.ts | 2 +- packages/server/src/services/application.ts | 6 ++---- packages/server/src/services/compose.ts | 6 ++---- packages/server/src/utils/notifications/build-success.ts | 6 ++---- packages/server/src/utils/notifications/database-backup.ts | 2 +- packages/server/src/utils/notifications/docker-cleanup.ts | 3 +-- packages/server/src/utils/notifications/dokploy-restart.ts | 3 +-- packages/server/src/utils/notifications/utils.ts | 2 +- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 61a1310e2..f88695039 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -134,7 +134,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiTestTelegramConnection) .mutation(async ({ input }) => { try { - await sendTelegramNotification(input, "Hi, From Dokploy 👋", []); + await sendTelegramNotification(input, "Hi, From Dokploy 👋"); return true; } catch (error) { throw new TRPCError({ diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index fa6909525..068e69b36 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -176,7 +176,6 @@ export const deployApplication = async ({ }) => { const application = await findApplicationById(applicationId); const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`; - const domains = application.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeployment({ applicationId: applicationId, title: titleLog, @@ -214,7 +213,7 @@ export const deployApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, - domains + domains: application.domains }); } catch (error) { await updateDeploymentStatus(deployment.deploymentId, "error"); @@ -287,7 +286,6 @@ export const deployRemoteApplication = async ({ }) => { const application = await findApplicationById(applicationId); const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`; - const domains = application.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeployment({ applicationId: applicationId, title: titleLog, @@ -335,7 +333,7 @@ export const deployRemoteApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, - domains + domains: application.domains }); } catch (error) { // @ts-ignore diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 2ed3d4620..8561dd376 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -209,7 +209,6 @@ export const deployCompose = async ({ const buildLink = `${await getDokployUrl()}/dashboard/project/${ compose.projectId }/services/compose/${compose.composeId}?tab=deployments`; - const domains = compose.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeploymentCompose({ composeId: composeId, title: titleLog, @@ -244,7 +243,7 @@ export const deployCompose = async ({ applicationType: "compose", buildLink, adminId: compose.project.adminId, - domains + domains: compose.domains, }); } catch (error) { await updateDeploymentStatus(deployment.deploymentId, "error"); @@ -315,7 +314,6 @@ export const deployRemoteCompose = async ({ const buildLink = `${await getDokployUrl()}/dashboard/project/${ compose.projectId }/services/compose/${compose.composeId}?tab=deployments`; - const domains = compose.domains.map(({ host, https }) => ({ host, https })); const deployment = await createDeploymentCompose({ composeId: composeId, title: titleLog, @@ -369,7 +367,7 @@ export const deployRemoteCompose = async ({ applicationType: "compose", buildLink, adminId: compose.project.adminId, - domains + domains: compose.domains, }); } catch (error) { // @ts-ignore diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 366b4591f..e30ec37b7 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -1,6 +1,7 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import BuildSuccessEmail from "@dokploy/server/emails/emails/build-success"; +import { Domain } from "@dokploy/server/services/domain"; import { renderAsync } from "@react-email/components"; import { and, eq } from "drizzle-orm"; import { format } from "date-fns"; @@ -17,10 +18,7 @@ interface Props { applicationType: string; buildLink: string; adminId: string; - domains: { - host: string; - https: boolean; - }[]; + domains: Domain[]; } export const sendBuildSuccessNotifications = async ({ diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 6594f87ce..74cbf144d 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -130,7 +130,7 @@ export const sendDatabaseBackupNotifications = async ({ const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; - await sendTelegramNotification(telegram, messageText, []); + await sendTelegramNotification(telegram, messageText); } if (slack) { diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index 8623867f4..adcd569e1 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -83,8 +83,7 @@ export const sendDockerCleanupNotifications = async ( if (telegram) { await sendTelegramNotification( telegram, - `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, - [] + `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}` ); } diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 7ee50b6a9..484da16d7 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -68,8 +68,7 @@ export const sendDokployRestartNotifications = async () => { if (telegram) { await sendTelegramNotification( telegram, - `✅ Dokploy Serverd Restarted\n\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, - [] + `✅ Dokploy Serverd Restarted\n\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}` ); } diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts index 963011564..5e16c5178 100644 --- a/packages/server/src/utils/notifications/utils.ts +++ b/packages/server/src/utils/notifications/utils.ts @@ -55,7 +55,7 @@ export const sendDiscordNotification = async ( export const sendTelegramNotification = async ( connection: typeof telegram.$inferInsert, messageText: string, - inlineButton: { + inlineButton?: { text: string; url: string; }[][] From b17ab6d8cbb2b80ff414d7701f81aec5abd63f99 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Mon, 13 Jan 2025 20:02:18 +1100 Subject: [PATCH 07/62] feat(template): added cloudflared --- apps/dokploy/public/templates/cloudflared.svg | 33 +++++++++++++++++++ .../templates/cloudflared/docker-compose.yml | 18 ++++++++++ apps/dokploy/templates/cloudflared/index.ts | 9 +++++ apps/dokploy/templates/templates.ts | 16 +++++++++ 4 files changed, 76 insertions(+) create mode 100644 apps/dokploy/public/templates/cloudflared.svg create mode 100644 apps/dokploy/templates/cloudflared/docker-compose.yml create mode 100644 apps/dokploy/templates/cloudflared/index.ts diff --git a/apps/dokploy/public/templates/cloudflared.svg b/apps/dokploy/public/templates/cloudflared.svg new file mode 100644 index 000000000..5be105f51 --- /dev/null +++ b/apps/dokploy/public/templates/cloudflared.svg @@ -0,0 +1,33 @@ + + + \ No newline at end of file diff --git a/apps/dokploy/templates/cloudflared/docker-compose.yml b/apps/dokploy/templates/cloudflared/docker-compose.yml new file mode 100644 index 000000000..d9fc27419 --- /dev/null +++ b/apps/dokploy/templates/cloudflared/docker-compose.yml @@ -0,0 +1,18 @@ +services: + cloudflared: + image: 'cloudflare/cloudflared:latest' + environment: + # Don't forget to set this in your Dokploy Environment + - 'TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}' + network_mode: host + restart: unless-stopped + command: [ + "tunnel", + + # More tunnel run parameters here: + # https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/tunnel-run-parameters/ + "--no-autoupdate", + #"--protocol", "http2", + + "run" + ] diff --git a/apps/dokploy/templates/cloudflared/index.ts b/apps/dokploy/templates/cloudflared/index.ts new file mode 100644 index 000000000..661fa31d0 --- /dev/null +++ b/apps/dokploy/templates/cloudflared/index.ts @@ -0,0 +1,9 @@ +import type { Schema, Template } from "../utils"; + +export function generate(schema: Schema): Template { + const envs = [`CLOUDFLARE_TUNNEL_TOKEN=""`]; + + return { + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index a0fbdb1be..d58e6fd6c 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1239,4 +1239,20 @@ export const templates: TemplateData[] = [ tags: ["matrix", "communication"], load: () => import("./conduit/index").then((m) => m.generate), }, + { + id: "cloudflared", + name: "Cloudflared", + version: "latest", + description: + "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", + logo: "cloudflared.svg", + links: { + github: "https://github.com/cloudflare/cloudflared", + website: + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", + docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", + }, + tags: ["cloud", "networking", "security", "tunnel"], + load: () => import("./cloudflared/index").then((m) => m.generate), + }, ]; From fb33a5b6a51f8b31b60b8516f76f24d1a21e5972 Mon Sep 17 00:00:00 2001 From: djknaeckebrot Date: Mon, 13 Jan 2025 11:53:05 +0100 Subject: [PATCH 08/62] style: make sure card do use the full width of the div --- .../dashboard/docker/show/show-containers.tsx | 430 +++++++++--------- .../file-system/show-traefik-system.tsx | 186 ++++---- 2 files changed, 308 insertions(+), 308 deletions(-) diff --git a/apps/dokploy/components/dashboard/docker/show/show-containers.tsx b/apps/dokploy/components/dashboard/docker/show/show-containers.tsx index 8c1acfe01..528e4d2c5 100644 --- a/apps/dokploy/components/dashboard/docker/show/show-containers.tsx +++ b/apps/dokploy/components/dashboard/docker/show/show-containers.tsx @@ -1,13 +1,13 @@ import { - type ColumnFiltersState, - type SortingState, - type VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, + type ColumnFiltersState, + type SortingState, + type VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, } from "@tanstack/react-table"; import { ChevronDown, Container } from "lucide-react"; import * as React from "react"; @@ -15,228 +15,228 @@ import * as React from "react"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, } from "@/components/ui/table"; import { type RouterOutputs, api } from "@/utils/api"; import { columns } from "./colums"; export type Container = NonNullable< - RouterOutputs["docker"]["getContainers"] + RouterOutputs["docker"]["getContainers"] >[0]; interface Props { - serverId?: string; + serverId?: string; } export const ShowContainers = ({ serverId }: Props) => { - const { data, isLoading } = api.docker.getContainers.useQuery({ - serverId, - }); + const { data, isLoading } = api.docker.getContainers.useQuery({ + serverId, + }); - const [sorting, setSorting] = React.useState([]); - const [columnFilters, setColumnFilters] = React.useState( - [], - ); - const [columnVisibility, setColumnVisibility] = - React.useState({}); - const [rowSelection, setRowSelection] = React.useState({}); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); - const table = useReactTable({ - data: data ?? [], - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - onRowSelectionChange: setRowSelection, - state: { - sorting, - columnFilters, - columnVisibility, - rowSelection, - }, - }); + const table = useReactTable({ + data: data ?? [], + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); - return ( -
- -
- - - - Docker Containers - - - See all the containers of your dokploy server - - - -
-
-
- - table - .getColumn("name") - ?.setFilterValue(event.target.value) - } - className="md:max-w-sm" - /> - - - - - - {table - .getAllColumns() - .filter((column) => column.getCanHide()) - .map((column) => { - return ( - - column.toggleVisibility(!!value) - } - > - {column.id} - - ); - })} - - -
-
- {isLoading ? ( -
- - Loading... - -
- ) : data?.length === 0 ? ( -
- - No results. - -
- ) : ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table?.getRowModel()?.rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - - {isLoading ? ( -
- - Loading... - -
- ) : ( - <>No results. - )} -
-
- )} -
-
- )} -
- {data && data?.length > 0 && ( -
-
- - -
-
- )} -
-
-
-
-
-
- ); + return ( +
+ +
+ + + + Docker Containers + + + See all the containers of your dokploy server + + + +
+
+
+ + table + .getColumn("name") + ?.setFilterValue(event.target.value) + } + className="md:max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ {isLoading ? ( +
+ + Loading... + +
+ ) : data?.length === 0 ? ( +
+ + No results. + +
+ ) : ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table?.getRowModel()?.rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + {isLoading ? ( +
+ + Loading... + +
+ ) : ( + <>No results. + )} +
+
+ )} +
+
+ )} +
+ {data && data?.length > 0 && ( +
+
+ + +
+
+ )} +
+
+
+
+
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx index d2263f747..c5b2c6676 100644 --- a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx +++ b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx @@ -1,10 +1,10 @@ import { AlertBlock } from "@/components/shared/alert-block"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { Tree } from "@/components/ui/file-tree"; import { cn } from "@/lib/utils"; @@ -14,97 +14,97 @@ import React from "react"; import { ShowTraefikFile } from "./show-traefik-file"; interface Props { - serverId?: string; + serverId?: string; } export const ShowTraefikSystem = ({ serverId }: Props) => { - const [file, setFile] = React.useState(null); + const [file, setFile] = React.useState(null); - const { - data: directories, - isLoading, - error, - isError, - } = api.settings.readDirectories.useQuery( - { - serverId, - }, - { - retry: 2, - }, - ); + const { + data: directories, + isLoading, + error, + isError, + } = api.settings.readDirectories.useQuery( + { + serverId, + }, + { + retry: 2, + } + ); - return ( -
- -
- - - - Traefik File System - - - Manage all the files and directories in {"'/etc/dokploy/traefik'"} - . - + return ( +
+ +
+ + + + Traefik File System + + + Manage all the files and directories in {"'/etc/dokploy/traefik'"} + . + - - Adding invalid configuration to existing files, can break your - Traefik instance, preventing access to your applications. - - - -
-
- {isError && ( - - {error?.message} - - )} - {isLoading && ( -
- - Loading... - - -
- )} - {directories?.length === 0 && ( -
- - No directories or files detected in{" "} - {"'/etc/dokploy/traefik'"} - - -
- )} - {directories && directories?.length > 0 && ( - <> - setFile(item?.id || null)} - folderIcon={Folder} - itemIcon={Workflow} - /> -
- {file ? ( - - ) : ( -
- - No file selected - - -
- )} -
- - )} -
-
-
-
-
-
- ); + + Adding invalid configuration to existing files, can break your + Traefik instance, preventing access to your applications. + +
+ +
+
+ {isError && ( + + {error?.message} + + )} + {isLoading && ( +
+ + Loading... + + +
+ )} + {directories?.length === 0 && ( +
+ + No directories or files detected in{" "} + {"'/etc/dokploy/traefik'"} + + +
+ )} + {directories && directories?.length > 0 && ( + <> + setFile(item?.id || null)} + folderIcon={Folder} + itemIcon={Workflow} + /> +
+ {file ? ( + + ) : ( +
+ + No file selected + + +
+ )} +
+ + )} +
+
+
+
+
+
+ ); }; From 8b203c48b46ed53a546f9a2a8908ee5150b3d074 Mon Sep 17 00:00:00 2001 From: djknaeckebrot Date: Mon, 13 Jan 2025 11:55:58 +0100 Subject: [PATCH 09/62] feat: update sidebar with Traefik File System title and add support link --- apps/dokploy/components/layouts/side.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 1d99552bb..be75e7981 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -8,6 +8,7 @@ import { BlocksIcon, BookIcon, ChevronRight, + CircleHelp, Command, CreditCard, Database, @@ -127,7 +128,7 @@ const data = { isActive: false, }, { - title: "File System", + title: "Traefik File System", url: "/dashboard/traefik", icon: GalleryVerticalEnd, isSingle: true, @@ -317,9 +318,15 @@ const data = { }, { name: "Support", + url: "https://discord.gg/2tBnJ3jDJc", + icon: CircleHelp, + }, + { + name: "Sponsor", url: "https://opencollective.com/dokploy", icon: Heart, }, + // { // name: "Sales & Marketing", // url: "#", From b68273c8cafa2584ed51040f6ae6b808b55aee7f Mon Sep 17 00:00:00 2001 From: djknaeckebrot Date: Mon, 13 Jan 2025 12:02:59 +0100 Subject: [PATCH 10/62] style: adjust grid layout for monitoring card and clean up class names --- apps/dokploy/components/dashboard/swarm/monitoring-card.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx index 3418060dd..98dc0d963 100644 --- a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx +++ b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx @@ -70,7 +70,7 @@ export default function SwarmMonitorCard({ serverId }: Props) { ); return ( - +
@@ -94,7 +94,7 @@ export default function SwarmMonitorCard({ serverId }: Props) { )}
-
+
Total Nodes From b3ff14f7920e29f4220c226b545a60b288fa8269 Mon Sep 17 00:00:00 2001 From: djknaeckebrot Date: Mon, 13 Jan 2025 12:09:06 +0100 Subject: [PATCH 11/62] style: clean up class names for consistency in dashboard components --- .../dokploy/components/dashboard/monitoring/web-server/show.tsx | 2 +- apps/dokploy/components/dashboard/projects/show.tsx | 2 +- apps/dokploy/pages/dashboard/settings/profile.tsx | 2 +- apps/dokploy/pages/dashboard/settings/server.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/monitoring/web-server/show.tsx b/apps/dokploy/components/dashboard/monitoring/web-server/show.tsx index f6bd8cc29..d6f15057e 100644 --- a/apps/dokploy/components/dashboard/monitoring/web-server/show.tsx +++ b/apps/dokploy/components/dashboard/monitoring/web-server/show.tsx @@ -3,7 +3,7 @@ import { DockerMonitoring } from "../docker/show"; export const ShowMonitoring = () => { return ( -
+
); diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 22088d0ac..16f842ed2 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -75,7 +75,7 @@ export const ShowProjects = () => { list={[{ name: "Projects", href: "/dashboard/projects" }]} />
- +
diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 62ab6150c..44e007f11 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -26,7 +26,7 @@ const Page = () => { const { data: isCloud } = api.settings.isCloud.useQuery(); return (
-
+
{(user?.canAccessToAPI || data?.rol === "admin") && } diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index 82b0359f9..d501f1fa6 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -13,7 +13,7 @@ import superjson from "superjson"; const Page = () => { return (
-
+
From 94ee5391fba86b4d4e273aa1fa87ceb1bfc49c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gabriel?= Date: Mon, 13 Jan 2025 09:19:32 -0300 Subject: [PATCH 12/62] refactor: apply ease-out to sidebar toggle animation --- apps/dokploy/components/ui/sidebar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/ui/sidebar.tsx b/apps/dokploy/components/ui/sidebar.tsx index bb3601dcb..a930da6d9 100644 --- a/apps/dokploy/components/ui/sidebar.tsx +++ b/apps/dokploy/components/ui/sidebar.tsx @@ -231,7 +231,7 @@ const Sidebar = React.forwardRef< {/* This is what handles the sidebar gap on desktop */}
@@ -824,7 +945,8 @@ export const HandleNotifications = ({ notificationId }: Props) => { isLoadingSlack || isLoadingTelegram || isLoadingDiscord || - isLoadingEmail + isLoadingEmail || + isLoadingGotify } variant="secondary" onClick={async () => { @@ -853,6 +975,13 @@ export const HandleNotifications = ({ notificationId }: Props) => { toAddresses: form.getValues("toAddresses"), fromAddress: form.getValues("fromAddress"), }); + } else if (type === "gotify") { + await testGotifyConnection({ + serverUrl: form.getValues("serverUrl"), + appToken: form.getValues("appToken"), + priority: form.getValues("priority"), + decoration: form.getValues("decoration"), + }); } toast.success("Connection Success"); } catch (err) { diff --git a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx new file mode 100644 index 000000000..3d8b61e6c --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx @@ -0,0 +1,864 @@ +import { + DiscordIcon, + SlackIcon, + TelegramIcon, +} from "@/components/icons/notification-icons"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Mail, Pen } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + type NotificationSchema, + notificationSchema, +} from "./add-notification"; + +interface Props { + notificationId: string; +} + +export const UpdateNotification = ({ notificationId }: Props) => { + const utils = api.useUtils(); + const [isOpen, setIsOpen] = useState(false); + const { data, refetch } = api.notification.one.useQuery( + { + notificationId, + }, + { + enabled: !!notificationId, + }, + ); + const { mutateAsync: testSlackConnection, isLoading: isLoadingSlack } = + api.notification.testSlackConnection.useMutation(); + + const { mutateAsync: testTelegramConnection, isLoading: isLoadingTelegram } = + api.notification.testTelegramConnection.useMutation(); + const { mutateAsync: testDiscordConnection, isLoading: isLoadingDiscord } = + api.notification.testDiscordConnection.useMutation(); + const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } = + api.notification.testEmailConnection.useMutation(); + const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } = + api.notification.testGotifyConnection.useMutation(); + const slackMutation = api.notification.updateSlack.useMutation(); + const telegramMutation = api.notification.updateTelegram.useMutation(); + const discordMutation = api.notification.updateDiscord.useMutation(); + const emailMutation = api.notification.updateEmail.useMutation(); + const gotifyMutation = api.notification.updateGotify.useMutation(); + const { data: isCloud } = api.settings.isCloud.useQuery(); + const form = useForm({ + defaultValues: { + type: "slack", + webhookUrl: "", + channel: "", + }, + resolver: zodResolver(notificationSchema), + }); + const type = form.watch("type"); + + const { fields, append, remove } = useFieldArray({ + control: form.control, + name: "toAddresses" as never, + }); + + useEffect(() => { + if (data) { + if (data.notificationType === "slack") { + form.reset({ + appBuildError: data.appBuildError, + appDeploy: data.appDeploy, + dokployRestart: data.dokployRestart, + databaseBackup: data.databaseBackup, + dockerCleanup: data.dockerCleanup, + webhookUrl: data.slack?.webhookUrl, + channel: data.slack?.channel || "", + name: data.name, + type: data.notificationType, + }); + } else if (data.notificationType === "telegram") { + form.reset({ + appBuildError: data.appBuildError, + appDeploy: data.appDeploy, + dokployRestart: data.dokployRestart, + databaseBackup: data.databaseBackup, + botToken: data.telegram?.botToken, + chatId: data.telegram?.chatId, + type: data.notificationType, + name: data.name, + dockerCleanup: data.dockerCleanup, + }); + } else if (data.notificationType === "discord") { + form.reset({ + appBuildError: data.appBuildError, + appDeploy: data.appDeploy, + dokployRestart: data.dokployRestart, + databaseBackup: data.databaseBackup, + type: data.notificationType, + webhookUrl: data.discord?.webhookUrl, + decoration: data.discord?.decoration || undefined, + name: data.name, + dockerCleanup: data.dockerCleanup, + }); + } else if (data.notificationType === "email") { + form.reset({ + appBuildError: data.appBuildError, + appDeploy: data.appDeploy, + dokployRestart: data.dokployRestart, + databaseBackup: data.databaseBackup, + type: data.notificationType, + smtpServer: data.email?.smtpServer, + smtpPort: data.email?.smtpPort, + username: data.email?.username, + password: data.email?.password, + toAddresses: data.email?.toAddresses, + fromAddress: data.email?.fromAddress, + name: data.name, + dockerCleanup: data.dockerCleanup, + }); + } else if (data.notificationType === "gotify") { + form.reset({ + appBuildError: data.appBuildError, + appDeploy: data.appDeploy, + dokployRestart: data.dokployRestart, + databaseBackup: data.databaseBackup, + type: data.notificationType, + serverUrl: data.gotify?.serverUrl, + appToken: data.gotify?.appToken, + priority: data.gotify?.priority || 5, + decoration: data.gotify?.decoration || undefined, + name: data.name, + dockerCleanup: data.dockerCleanup, + }); + } + } + }, [form, form.reset, data]); + + const onSubmit = async (formData: NotificationSchema) => { + const { + appBuildError, + appDeploy, + dokployRestart, + databaseBackup, + dockerCleanup, + } = formData; + let promise: Promise | null = null; + if (formData?.type === "slack" && data?.slackId) { + promise = slackMutation.mutateAsync({ + appBuildError: appBuildError, + appDeploy: appDeploy, + dokployRestart: dokployRestart, + databaseBackup: databaseBackup, + webhookUrl: formData.webhookUrl, + channel: formData.channel, + name: formData.name, + notificationId: notificationId, + slackId: data?.slackId, + dockerCleanup: dockerCleanup, + }); + } else if (formData.type === "telegram" && data?.telegramId) { + promise = telegramMutation.mutateAsync({ + appBuildError: appBuildError, + appDeploy: appDeploy, + dokployRestart: dokployRestart, + databaseBackup: databaseBackup, + botToken: formData.botToken, + chatId: formData.chatId, + name: formData.name, + notificationId: notificationId, + telegramId: data?.telegramId, + dockerCleanup: dockerCleanup, + }); + } else if (formData.type === "discord" && data?.discordId) { + promise = discordMutation.mutateAsync({ + appBuildError: appBuildError, + appDeploy: appDeploy, + dokployRestart: dokployRestart, + databaseBackup: databaseBackup, + webhookUrl: formData.webhookUrl, + decoration: formData.decoration, + name: formData.name, + notificationId: notificationId, + discordId: data?.discordId, + dockerCleanup: dockerCleanup, + }); + } else if (formData.type === "email" && data?.emailId) { + promise = emailMutation.mutateAsync({ + appBuildError: appBuildError, + appDeploy: appDeploy, + dokployRestart: dokployRestart, + databaseBackup: databaseBackup, + smtpServer: formData.smtpServer, + smtpPort: formData.smtpPort, + username: formData.username, + password: formData.password, + fromAddress: formData.fromAddress, + toAddresses: formData.toAddresses, + name: formData.name, + notificationId: notificationId, + emailId: data?.emailId, + dockerCleanup: dockerCleanup, + }); + } else if (formData.type === "gotify" && data?.gotifyId) { + promise = gotifyMutation.mutateAsync({ + appBuildError: appBuildError, + appDeploy: appDeploy, + dokployRestart: dokployRestart, + databaseBackup: databaseBackup, + serverUrl: formData.serverUrl, + appToken: formData.appToken, + priority: formData.priority, + decoration: formData.decoration, + name: formData.name, + notificationId: notificationId, + gotifyId: data?.gotifyId, + dockerCleanup: dockerCleanup, + }); + } + + if (promise) { + await promise + .then(async () => { + toast.success("Notification Updated"); + await utils.notification.all.invalidate(); + refetch(); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error updating a notification"); + }); + } + }; + return ( + + + + + + + Update Notification + + Update the current notification config + + +
+ +
+
+
+ + {data?.notificationType === "slack" + ? "Slack" + : data?.notificationType === "telegram" + ? "Telegram" + : data?.notificationType === "discord" + ? "Discord" + : data?.notificationType === "email" + ? "Email" + : "Gotify"} + +
+ {data?.notificationType === "slack" && ( + + )} + {data?.notificationType === "telegram" && ( + + )} + {data?.notificationType === "discord" && ( + + )} + {data?.notificationType === "email" && ( + + )} + {data?.notificationType === "gotify" && ( + + )} +
+ +
+ ( + + Name + + + + + + + )} + /> + + {type === "slack" && ( + <> + ( + + Webhook URL + + + + + + + )} + /> + + ( + + Channel + + + + + + + )} + /> + + )} + + {type === "telegram" && ( + <> + ( + + Bot Token + + + + + + + )} + /> + + ( + + Chat ID + + + + + + + )} + /> + + )} + + {type === "discord" && ( + <> + ( + + Webhook URL + + + + + + + )} + /> + + ( + +
+ Decoration + + Decorate the notification with emojis. + +
+ + + +
+ )} + /> + + )} + {type === "email" && ( + <> +
+ ( + + SMTP Server + + + + + + + )} + /> + ( + + SMTP Port + + { + const value = e.target.value; + if (value) { + const port = Number.parseInt(value); + if (port > 0 && port < 65536) { + field.onChange(port); + } + } + }} + /> + + + + + )} + /> +
+ +
+ ( + + Username + + + + + + + )} + /> + + ( + + Password + + + + + + + )} + /> +
+ + ( + + From Address + + + + + + )} + /> +
+ To Addresses + + {fields.map((field, index) => ( +
+ ( + + + + + + + + )} + /> + +
+ ))} + {type === "email" && + "toAddresses" in form.formState.errors && ( +
+ {form.formState?.errors?.toAddresses?.root?.message} +
+ )} +
+ + + + )} + {type === "gotify" && ( + <> + ( + + Server URL + + + + + + )} + /> + + ( + + App Token + + + + + + )} + /> + + ( + + Priority + + { + const value = e.target.value; + if (value) { + const priority = Number.parseInt(value); + if (priority > 0 && priority < 10) { + field.onChange(priority); + } + } + }} + type="number" + /> + + + + )} + /> + ( + +
+ Decoration + + Decorate the notification with emojis. + +
+ + + +
+ )} + /> + + )} +
+
+
+ + Select the actions. + + +
+ ( + +
+ App Deploy + + Trigger the action when a app is deployed. + +
+ + + +
+ )} + /> + ( + +
+ App Builder Error + + Trigger the action when the build fails. + +
+ + + +
+ )} + /> + + ( + +
+ Database Backup + + Trigger the action when a database backup is created. + +
+ + + +
+ )} + /> + ( + +
+ Docker Cleanup + + Trigger the action when the docker cleanup is + performed. + +
+ + + +
+ )} + /> + {!isCloud && ( + ( + +
+ Dokploy Restart + + Trigger the action when a dokploy is restarted. + +
+ + + +
+ )} + /> + )} +
+
+
+ + + + + + +
+
+ ); +}; diff --git a/apps/dokploy/drizzle/meta/0054_snapshot.json b/apps/dokploy/drizzle/meta/0054_snapshot.json index 88447e32e..cee6723d3 100644 --- a/apps/dokploy/drizzle/meta/0054_snapshot.json +++ b/apps/dokploy/drizzle/meta/0054_snapshot.json @@ -4250,4 +4250,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 572e67c6d..418ac97a4 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -395,4 +395,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index f88695039..77f0287a7 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -2,20 +2,24 @@ import { adminProcedure, createTRPCRouter, protectedProcedure, + publicProcedure, } from "@/server/api/trpc"; import { db } from "@/server/db"; import { apiCreateDiscord, apiCreateEmail, + apiCreateGotify, apiCreateSlack, apiCreateTelegram, apiFindOneNotification, apiTestDiscordConnection, apiTestEmailConnection, + apiTestGotifyConnection, apiTestSlackConnection, apiTestTelegramConnection, apiUpdateDiscord, apiUpdateEmail, + apiUpdateGotify, apiUpdateSlack, apiUpdateTelegram, notifications, @@ -24,16 +28,19 @@ import { IS_CLOUD, createDiscordNotification, createEmailNotification, + createGotifyNotification, createSlackNotification, createTelegramNotification, findNotificationById, removeNotificationById, sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, updateDiscordNotification, updateEmailNotification, + updateGotifyNotification, updateSlackNotification, updateTelegramNotification, } from "@dokploy/server"; @@ -300,10 +307,61 @@ export const notificationRouter = createTRPCRouter({ telegram: true, discord: true, email: true, + gotify: true, }, orderBy: desc(notifications.createdAt), ...(IS_CLOUD && { where: eq(notifications.adminId, ctx.user.adminId) }), // TODO: Remove this line when the cloud version is ready }); }), + createGotify: adminProcedure + .input(apiCreateGotify) + .mutation(async ({ input, ctx }) => { + try { + return await createGotifyNotification(input, ctx.user.adminId); + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the notification", + cause: error, + }); + } + }), + updateGotify: adminProcedure + .input(apiUpdateGotify) + .mutation(async ({ input, ctx }) => { + try { + const notification = await findNotificationById(input.notificationId); + if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to update this notification", + }); + } + return await updateGotifyNotification({ + ...input, + adminId: ctx.user.adminId, + }); + } catch (error) { + throw error; + } + }), + testGotifyConnection: adminProcedure + .input(apiTestGotifyConnection) + .mutation(async ({ input }) => { + try { + await sendGotifyNotification( + input, + "Test Notification", + "Hi, From Dokploy 👋" + ); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error testing the notification", + cause: error, + }); + } + }), }); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 5501621dc..bbbe18d2a 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -10,6 +10,7 @@ export const notificationType = pgEnum("notificationType", [ "telegram", "discord", "email", + "gotify", ]); export const notifications = pgTable("notification", { @@ -39,6 +40,9 @@ export const notifications = pgTable("notification", { emailId: text("emailId").references(() => email.emailId, { onDelete: "cascade", }), + gotifyId: text("gotifyId").references(() => gotify.gotifyId, { + onDelete: "cascade", + }), adminId: text("adminId").references(() => admins.adminId, { onDelete: "cascade", }), @@ -84,6 +88,17 @@ export const email = pgTable("email", { toAddresses: text("toAddress").array().notNull(), }); +export const gotify = pgTable("gotify", { + gotifyId: text("gotifyId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + serverUrl: text("serverUrl").notNull(), + appToken: text("appToken").notNull(), + priority: integer("priority").notNull().default(5), + decoration: boolean("decoration"), +}); + export const notificationsRelations = relations(notifications, ({ one }) => ({ slack: one(slack, { fields: [notifications.slackId], @@ -101,6 +116,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.emailId], references: [email.emailId], }), + gotify: one(gotify, { + fields: [notifications.gotifyId], + references: [gotify.gotifyId], + }), admin: one(admins, { fields: [notifications.adminId], references: [admins.adminId], @@ -224,6 +243,37 @@ export const apiTestEmailConnection = apiCreateEmail.pick({ fromAddress: true, }); +export const apiCreateGotify = notificationsSchema + .pick({ + appBuildError: true, + databaseBackup: true, + dokployRestart: true, + name: true, + appDeploy: true, + dockerCleanup: true, + }) + .extend({ + serverUrl: z.string().min(1), + appToken: z.string().min(1), + priority: z.number().min(1), + decoration: z.boolean(), + }) + .required(); + +export const apiUpdateGotify = apiCreateGotify.partial().extend({ + notificationId: z.string().min(1), + gotifyId: z.string().min(1), + adminId: z.string().optional(), +}); + +export const apiTestGotifyConnection = apiCreateGotify.pick({ + serverUrl: true, + appToken: true, + priority: true, +}).extend({ + decoration: z.boolean().optional(), +}); + export const apiFindOneNotification = notificationsSchema .pick({ notificationId: true, @@ -242,5 +292,8 @@ export const apiSendTest = notificationsSchema username: z.string(), password: z.string(), toAddresses: z.array(z.string()), + serverUrl: z.string(), + appToken: z.string(), + priority: z.number(), }) .partial(); diff --git a/packages/server/src/services/notification.ts b/packages/server/src/services/notification.ts index e75154dfc..2b62b4574 100644 --- a/packages/server/src/services/notification.ts +++ b/packages/server/src/services/notification.ts @@ -2,14 +2,17 @@ import { db } from "@dokploy/server/db"; import { type apiCreateDiscord, type apiCreateEmail, + type apiCreateGotify, type apiCreateSlack, type apiCreateTelegram, type apiUpdateDiscord, type apiUpdateEmail, + type apiUpdateGotify, type apiUpdateSlack, type apiUpdateTelegram, discord, email, + gotify, notifications, slack, telegram, @@ -379,6 +382,96 @@ export const updateEmailNotification = async ( }); }; +export const createGotifyNotification = async ( + input: typeof apiCreateGotify._type, + adminId: string, +) => { + await db.transaction(async (tx) => { + const newGotify = await tx + .insert(gotify) + .values({ + serverUrl: input.serverUrl, + appToken: input.appToken, + priority: input.priority, + decoration: input.decoration, + }) + .returning() + .then((value) => value[0]); + + if (!newGotify) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error input: Inserting gotify", + }); + } + + const newDestination = await tx + .insert(notifications) + .values({ + gotifyId: newGotify.gotifyId, + name: input.name, + appDeploy: input.appDeploy, + appBuildError: input.appBuildError, + databaseBackup: input.databaseBackup, + dokployRestart: input.dokployRestart, + dockerCleanup: input.dockerCleanup, + notificationType: "gotify", + adminId: adminId, + }) + .returning() + .then((value) => value[0]); + + if (!newDestination) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error input: Inserting notification", + }); + } + + return newDestination; + }); +}; + +export const updateGotifyNotification = async ( + input: typeof apiUpdateGotify._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, + adminId: input.adminId, + }) + .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(gotify) + .set({ + serverUrl: input.serverUrl, + appToken: input.appToken, + priority: input.priority, + decoration: input.decoration, + }) + .where(eq(gotify.gotifyId, input.gotifyId)); + + return newDestination; + }); +}; + export const findNotificationById = async (notificationId: string) => { const notification = await db.query.notifications.findFirst({ where: eq(notifications.notificationId, notificationId), @@ -387,6 +480,7 @@ export const findNotificationById = async (notificationId: string) => { telegram: true, discord: true, email: true, + gotify: true, }, }); if (!notification) { diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts index 2f8324bb1..09fdd7bcc 100644 --- a/packages/server/src/utils/notifications/utils.ts +++ b/packages/server/src/utils/notifications/utils.ts @@ -3,6 +3,7 @@ import type { email, slack, telegram, + gotify, } from "@dokploy/server/db/schema"; import nodemailer from "nodemailer"; @@ -87,3 +88,31 @@ export const sendSlackNotification = async ( console.log(err); } }; + +export const sendGotifyNotification = async ( + connection: typeof gotify.$inferInsert, + title: string, + message: string, +) => { + const response = await fetch(`${connection.serverUrl}/message`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Gotify-Key": connection.appToken, + }, + body: JSON.stringify({ + title: title, + message: message, + priority: connection.priority, + extras: { + "client::display": { + "contentType": "text/plain" + } + } + }), + }); + + if (!response.ok) { + throw new Error(`Failed to send Gotify notification: ${response.statusText}`); + } +}; \ No newline at end of file From 1f8a4762640b0a626523929b2e7ccd9c0e3fb00d Mon Sep 17 00:00:00 2001 From: depado Date: Fri, 10 Jan 2025 20:41:51 +0100 Subject: [PATCH 33/62] chore(lint): run biome --- .../notifications/update-notification.tsx | 5 +---- apps/dokploy/server/api/routers/notification.ts | 2 +- packages/server/src/db/schema/notification.ts | 16 +++++++++------- packages/server/src/utils/notifications/utils.ts | 14 ++++++++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx index 3d8b61e6c..100987104 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx @@ -618,10 +618,7 @@ export const UpdateNotification = ({ notificationId }: Props) => { App Token - + diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 77f0287a7..2eafc66d2 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -353,7 +353,7 @@ export const notificationRouter = createTRPCRouter({ await sendGotifyNotification( input, "Test Notification", - "Hi, From Dokploy 👋" + "Hi, From Dokploy 👋", ); return true; } catch (error) { diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index bbbe18d2a..12c7698e2 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -266,13 +266,15 @@ export const apiUpdateGotify = apiCreateGotify.partial().extend({ adminId: z.string().optional(), }); -export const apiTestGotifyConnection = apiCreateGotify.pick({ - serverUrl: true, - appToken: true, - priority: true, -}).extend({ - decoration: z.boolean().optional(), -}); +export const apiTestGotifyConnection = apiCreateGotify + .pick({ + serverUrl: true, + appToken: true, + priority: true, + }) + .extend({ + decoration: z.boolean().optional(), + }); export const apiFindOneNotification = notificationsSchema .pick({ diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts index 09fdd7bcc..c4b46f411 100644 --- a/packages/server/src/utils/notifications/utils.ts +++ b/packages/server/src/utils/notifications/utils.ts @@ -1,9 +1,9 @@ import type { discord, email, + gotify, slack, telegram, - gotify, } from "@dokploy/server/db/schema"; import nodemailer from "nodemailer"; @@ -106,13 +106,15 @@ export const sendGotifyNotification = async ( priority: connection.priority, extras: { "client::display": { - "contentType": "text/plain" - } - } + contentType: "text/plain", + }, + }, }), }); if (!response.ok) { - throw new Error(`Failed to send Gotify notification: ${response.statusText}`); + throw new Error( + `Failed to send Gotify notification: ${response.statusText}`, + ); } -}; \ No newline at end of file +}; From e25d0c0c6809611121dc6cd9ba6eafa3b360d990 Mon Sep 17 00:00:00 2001 From: depado Date: Mon, 13 Jan 2025 18:57:45 +0100 Subject: [PATCH 34/62] feat(notifications): implement notifications for gotify --- .../notifications/handle-notifications.tsx | 22 +- .../notifications/show-notifications.tsx | 7 +- .../notifications/update-notification.tsx | 861 ---- apps/dokploy/drizzle/0056_majestic_skaar.sql | 15 + apps/dokploy/drizzle/meta/0054_snapshot.json | 2 +- apps/dokploy/drizzle/meta/0056_snapshot.json | 4314 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 9 +- .../src/utils/notifications/build-error.ts | 19 +- .../src/utils/notifications/build-success.ts | 18 +- .../utils/notifications/database-backup.ts | 23 +- .../src/utils/notifications/docker-cleanup.ts | 15 +- .../utils/notifications/dokploy-restart.ts | 16 +- 12 files changed, 4450 insertions(+), 871 deletions(-) delete mode 100644 apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx create mode 100644 apps/dokploy/drizzle/0056_majestic_skaar.sql create mode 100644 apps/dokploy/drizzle/meta/0056_snapshot.json diff --git a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx index 6b44e5de8..f4c016a04 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx @@ -28,7 +28,13 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, Mail, PenBoxIcon, PlusIcon, MessageCircleMore } from "lucide-react"; +import { + AlertTriangle, + Mail, + MessageCircleMore, + PenBoxIcon, + PlusIcon, +} from "lucide-react"; import { useEffect, useState } from "react"; import { useFieldArray, useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -239,6 +245,20 @@ export const HandleNotifications = ({ notificationId }: Props) => { name: notification.name, dockerCleanup: notification.dockerCleanup, }); + } else if (notification.notificationType === "gotify") { + form.reset({ + appBuildError: notification.appBuildError, + appDeploy: notification.appDeploy, + dokployRestart: notification.dokployRestart, + databaseBackup: notification.databaseBackup, + type: notification.notificationType, + appToken: notification.gotify?.appToken, + decoration: notification.gotify?.decoration || undefined, + priority: notification.gotify?.priority, + serverUrl: notification.gotify?.serverUrl, + name: notification.name, + dockerCleanup: notification.dockerCleanup, + }); } } else { form.reset(); diff --git a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx index 75b66622c..77fb8858d 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx @@ -13,7 +13,7 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { Bell, Loader2, Mail, Trash2 } from "lucide-react"; +import { Bell, Loader2, Mail, MessageCircleMore, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { HandleNotifications } from "./handle-notifications"; @@ -83,6 +83,11 @@ export const ShowNotifications = () => {
)} + {notification.notificationType === "gotify" && ( +
+ +
+ )} {notification.name} diff --git a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx deleted file mode 100644 index 100987104..000000000 --- a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx +++ /dev/null @@ -1,861 +0,0 @@ -import { - DiscordIcon, - SlackIcon, - TelegramIcon, -} from "@/components/icons/notification-icons"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { Switch } from "@/components/ui/switch"; -import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Mail, Pen } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { - type NotificationSchema, - notificationSchema, -} from "./add-notification"; - -interface Props { - notificationId: string; -} - -export const UpdateNotification = ({ notificationId }: Props) => { - const utils = api.useUtils(); - const [isOpen, setIsOpen] = useState(false); - const { data, refetch } = api.notification.one.useQuery( - { - notificationId, - }, - { - enabled: !!notificationId, - }, - ); - const { mutateAsync: testSlackConnection, isLoading: isLoadingSlack } = - api.notification.testSlackConnection.useMutation(); - - const { mutateAsync: testTelegramConnection, isLoading: isLoadingTelegram } = - api.notification.testTelegramConnection.useMutation(); - const { mutateAsync: testDiscordConnection, isLoading: isLoadingDiscord } = - api.notification.testDiscordConnection.useMutation(); - const { mutateAsync: testEmailConnection, isLoading: isLoadingEmail } = - api.notification.testEmailConnection.useMutation(); - const { mutateAsync: testGotifyConnection, isLoading: isLoadingGotify } = - api.notification.testGotifyConnection.useMutation(); - const slackMutation = api.notification.updateSlack.useMutation(); - const telegramMutation = api.notification.updateTelegram.useMutation(); - const discordMutation = api.notification.updateDiscord.useMutation(); - const emailMutation = api.notification.updateEmail.useMutation(); - const gotifyMutation = api.notification.updateGotify.useMutation(); - const { data: isCloud } = api.settings.isCloud.useQuery(); - const form = useForm({ - defaultValues: { - type: "slack", - webhookUrl: "", - channel: "", - }, - resolver: zodResolver(notificationSchema), - }); - const type = form.watch("type"); - - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: "toAddresses" as never, - }); - - useEffect(() => { - if (data) { - if (data.notificationType === "slack") { - form.reset({ - appBuildError: data.appBuildError, - appDeploy: data.appDeploy, - dokployRestart: data.dokployRestart, - databaseBackup: data.databaseBackup, - dockerCleanup: data.dockerCleanup, - webhookUrl: data.slack?.webhookUrl, - channel: data.slack?.channel || "", - name: data.name, - type: data.notificationType, - }); - } else if (data.notificationType === "telegram") { - form.reset({ - appBuildError: data.appBuildError, - appDeploy: data.appDeploy, - dokployRestart: data.dokployRestart, - databaseBackup: data.databaseBackup, - botToken: data.telegram?.botToken, - chatId: data.telegram?.chatId, - type: data.notificationType, - name: data.name, - dockerCleanup: data.dockerCleanup, - }); - } else if (data.notificationType === "discord") { - form.reset({ - appBuildError: data.appBuildError, - appDeploy: data.appDeploy, - dokployRestart: data.dokployRestart, - databaseBackup: data.databaseBackup, - type: data.notificationType, - webhookUrl: data.discord?.webhookUrl, - decoration: data.discord?.decoration || undefined, - name: data.name, - dockerCleanup: data.dockerCleanup, - }); - } else if (data.notificationType === "email") { - form.reset({ - appBuildError: data.appBuildError, - appDeploy: data.appDeploy, - dokployRestart: data.dokployRestart, - databaseBackup: data.databaseBackup, - type: data.notificationType, - smtpServer: data.email?.smtpServer, - smtpPort: data.email?.smtpPort, - username: data.email?.username, - password: data.email?.password, - toAddresses: data.email?.toAddresses, - fromAddress: data.email?.fromAddress, - name: data.name, - dockerCleanup: data.dockerCleanup, - }); - } else if (data.notificationType === "gotify") { - form.reset({ - appBuildError: data.appBuildError, - appDeploy: data.appDeploy, - dokployRestart: data.dokployRestart, - databaseBackup: data.databaseBackup, - type: data.notificationType, - serverUrl: data.gotify?.serverUrl, - appToken: data.gotify?.appToken, - priority: data.gotify?.priority || 5, - decoration: data.gotify?.decoration || undefined, - name: data.name, - dockerCleanup: data.dockerCleanup, - }); - } - } - }, [form, form.reset, data]); - - const onSubmit = async (formData: NotificationSchema) => { - const { - appBuildError, - appDeploy, - dokployRestart, - databaseBackup, - dockerCleanup, - } = formData; - let promise: Promise | null = null; - if (formData?.type === "slack" && data?.slackId) { - promise = slackMutation.mutateAsync({ - appBuildError: appBuildError, - appDeploy: appDeploy, - dokployRestart: dokployRestart, - databaseBackup: databaseBackup, - webhookUrl: formData.webhookUrl, - channel: formData.channel, - name: formData.name, - notificationId: notificationId, - slackId: data?.slackId, - dockerCleanup: dockerCleanup, - }); - } else if (formData.type === "telegram" && data?.telegramId) { - promise = telegramMutation.mutateAsync({ - appBuildError: appBuildError, - appDeploy: appDeploy, - dokployRestart: dokployRestart, - databaseBackup: databaseBackup, - botToken: formData.botToken, - chatId: formData.chatId, - name: formData.name, - notificationId: notificationId, - telegramId: data?.telegramId, - dockerCleanup: dockerCleanup, - }); - } else if (formData.type === "discord" && data?.discordId) { - promise = discordMutation.mutateAsync({ - appBuildError: appBuildError, - appDeploy: appDeploy, - dokployRestart: dokployRestart, - databaseBackup: databaseBackup, - webhookUrl: formData.webhookUrl, - decoration: formData.decoration, - name: formData.name, - notificationId: notificationId, - discordId: data?.discordId, - dockerCleanup: dockerCleanup, - }); - } else if (formData.type === "email" && data?.emailId) { - promise = emailMutation.mutateAsync({ - appBuildError: appBuildError, - appDeploy: appDeploy, - dokployRestart: dokployRestart, - databaseBackup: databaseBackup, - smtpServer: formData.smtpServer, - smtpPort: formData.smtpPort, - username: formData.username, - password: formData.password, - fromAddress: formData.fromAddress, - toAddresses: formData.toAddresses, - name: formData.name, - notificationId: notificationId, - emailId: data?.emailId, - dockerCleanup: dockerCleanup, - }); - } else if (formData.type === "gotify" && data?.gotifyId) { - promise = gotifyMutation.mutateAsync({ - appBuildError: appBuildError, - appDeploy: appDeploy, - dokployRestart: dokployRestart, - databaseBackup: databaseBackup, - serverUrl: formData.serverUrl, - appToken: formData.appToken, - priority: formData.priority, - decoration: formData.decoration, - name: formData.name, - notificationId: notificationId, - gotifyId: data?.gotifyId, - dockerCleanup: dockerCleanup, - }); - } - - if (promise) { - await promise - .then(async () => { - toast.success("Notification Updated"); - await utils.notification.all.invalidate(); - refetch(); - setIsOpen(false); - }) - .catch(() => { - toast.error("Error updating a notification"); - }); - } - }; - return ( - - - - - - - Update Notification - - Update the current notification config - - -
- -
-
-
- - {data?.notificationType === "slack" - ? "Slack" - : data?.notificationType === "telegram" - ? "Telegram" - : data?.notificationType === "discord" - ? "Discord" - : data?.notificationType === "email" - ? "Email" - : "Gotify"} - -
- {data?.notificationType === "slack" && ( - - )} - {data?.notificationType === "telegram" && ( - - )} - {data?.notificationType === "discord" && ( - - )} - {data?.notificationType === "email" && ( - - )} - {data?.notificationType === "gotify" && ( - - )} -
- -
- ( - - Name - - - - - - - )} - /> - - {type === "slack" && ( - <> - ( - - Webhook URL - - - - - - - )} - /> - - ( - - Channel - - - - - - - )} - /> - - )} - - {type === "telegram" && ( - <> - ( - - Bot Token - - - - - - - )} - /> - - ( - - Chat ID - - - - - - - )} - /> - - )} - - {type === "discord" && ( - <> - ( - - Webhook URL - - - - - - - )} - /> - - ( - -
- Decoration - - Decorate the notification with emojis. - -
- - - -
- )} - /> - - )} - {type === "email" && ( - <> -
- ( - - SMTP Server - - - - - - - )} - /> - ( - - SMTP Port - - { - const value = e.target.value; - if (value) { - const port = Number.parseInt(value); - if (port > 0 && port < 65536) { - field.onChange(port); - } - } - }} - /> - - - - - )} - /> -
- -
- ( - - Username - - - - - - - )} - /> - - ( - - Password - - - - - - - )} - /> -
- - ( - - From Address - - - - - - )} - /> -
- To Addresses - - {fields.map((field, index) => ( -
- ( - - - - - - - - )} - /> - -
- ))} - {type === "email" && - "toAddresses" in form.formState.errors && ( -
- {form.formState?.errors?.toAddresses?.root?.message} -
- )} -
- - - - )} - {type === "gotify" && ( - <> - ( - - Server URL - - - - - - )} - /> - - ( - - App Token - - - - - - )} - /> - - ( - - Priority - - { - const value = e.target.value; - if (value) { - const priority = Number.parseInt(value); - if (priority > 0 && priority < 10) { - field.onChange(priority); - } - } - }} - type="number" - /> - - - - )} - /> - ( - -
- Decoration - - Decorate the notification with emojis. - -
- - - -
- )} - /> - - )} -
-
-
- - Select the actions. - - -
- ( - -
- App Deploy - - Trigger the action when a app is deployed. - -
- - - -
- )} - /> - ( - -
- App Builder Error - - Trigger the action when the build fails. - -
- - - -
- )} - /> - - ( - -
- Database Backup - - Trigger the action when a database backup is created. - -
- - - -
- )} - /> - ( - -
- Docker Cleanup - - Trigger the action when the docker cleanup is - performed. - -
- - - -
- )} - /> - {!isCloud && ( - ( - -
- Dokploy Restart - - Trigger the action when a dokploy is restarted. - -
- - - -
- )} - /> - )} -
-
-
- - - - - - -
-
- ); -}; diff --git a/apps/dokploy/drizzle/0056_majestic_skaar.sql b/apps/dokploy/drizzle/0056_majestic_skaar.sql new file mode 100644 index 000000000..3702a6755 --- /dev/null +++ b/apps/dokploy/drizzle/0056_majestic_skaar.sql @@ -0,0 +1,15 @@ +ALTER TYPE "notificationType" ADD VALUE 'gotify';--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "gotify" ( + "gotifyId" text PRIMARY KEY NOT NULL, + "serverUrl" text NOT NULL, + "appToken" text NOT NULL, + "priority" integer DEFAULT 5 NOT NULL, + "decoration" boolean +); +--> statement-breakpoint +ALTER TABLE "notification" ADD COLUMN "gotifyId" text;--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "notification" ADD CONSTRAINT "notification_gotifyId_gotify_gotifyId_fk" FOREIGN KEY ("gotifyId") REFERENCES "public"."gotify"("gotifyId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/apps/dokploy/drizzle/meta/0054_snapshot.json b/apps/dokploy/drizzle/meta/0054_snapshot.json index cee6723d3..88447e32e 100644 --- a/apps/dokploy/drizzle/meta/0054_snapshot.json +++ b/apps/dokploy/drizzle/meta/0054_snapshot.json @@ -4250,4 +4250,4 @@ "schemas": {}, "tables": {} } -} +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0056_snapshot.json b/apps/dokploy/drizzle/meta/0056_snapshot.json new file mode 100644 index 000000000..be672e8fd --- /dev/null +++ b/apps/dokploy/drizzle/meta/0056_snapshot.json @@ -0,0 +1,4314 @@ +{ + "id": "24787a88-0754-437a-b077-03a3265b8ef5", + "prevId": "21a03d52-4db6-4449-a61c-92b6ceae0bd9", + "version": "6", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_adminId_admin_adminId_fk": { + "name": "project_adminId_admin_adminId_fk", + "tableFrom": "project", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_adminId_admin_adminId_fk": { + "name": "destination_adminId_admin_adminId_fk", + "tableFrom": "destination", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_adminId_admin_adminId_fk": { + "name": "certificate_adminId_admin_adminId_fk", + "tableFrom": "certificate", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + } + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + } + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_adminId_admin_adminId_fk": { + "name": "registry_adminId_admin_adminId_fk", + "tableFrom": "registry", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_adminId_admin_adminId_fk": { + "name": "notification_adminId_admin_adminId_fk", + "tableFrom": "notification", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_adminId_admin_adminId_fk": { + "name": "ssh-key_adminId_admin_adminId_fk", + "tableFrom": "ssh-key", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_adminId_admin_adminId_fk": { + "name": "git_provider_adminId_admin_adminId_fk", + "tableFrom": "git_provider", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "server_adminId_admin_adminId_fk": { + "name": "server_adminId_admin_adminId_fk", + "tableFrom": "server", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + } + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 418ac97a4..88d4d1921 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -393,6 +393,13 @@ "when": 1736669623831, "tag": "0055_next_serpent_society", "breakpoints": true + }, + { + "idx": 56, + "version": "6", + "when": 1736789918294, + "tag": "0056_majestic_skaar", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 695b37863..90bfd77f6 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -6,6 +6,7 @@ import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, } from "./utils"; @@ -39,11 +40,12 @@ export const sendBuildErrorNotifications = async ({ discord: true, telegram: true, slack: true, + gotify: true, }, }); for (const notification of notificationList) { - const { email, discord, telegram, slack } = notification; + const { email, discord, telegram, slack, gotify } = notification; if (email) { const template = await renderAsync( BuildFailedEmail({ @@ -112,6 +114,21 @@ export const sendBuildErrorNotifications = async ({ }); } + if (gotify) { + const decorate = (decoration: string, text: string) => + `${gotify.decoration ? decoration : ""} ${text}`.trim(); + await sendGotifyNotification( + 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}`)}`, + ); + } + if (telegram) { await sendTelegramNotification( telegram, diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 16aa4a588..253762f5d 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -6,6 +6,7 @@ import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, } from "./utils"; @@ -37,11 +38,12 @@ export const sendBuildSuccessNotifications = async ({ discord: true, telegram: true, slack: true, + gotify: true, }, }); for (const notification of notificationList) { - const { email, discord, telegram, slack } = notification; + const { email, discord, telegram, slack, gotify } = notification; if (email) { const template = await renderAsync( @@ -106,6 +108,20 @@ export const sendBuildSuccessNotifications = async ({ }); } + if (gotify) { + const decorate = (decoration: string, text: string) => + `${gotify.decoration ? decoration : ""} ${text}`.trim(); + await sendGotifyNotification( + gotify, + decorate("✅", "Build Success"), + `${decorate("🛠️", `Project: ${projectName}`)} + ${decorate("⚙️", `Application: ${applicationName}`)} + ${decorate("❔", `Type: ${applicationType}`)} + ${decorate("🕒", `Date: ${date.toLocaleString()}`)} + ${decorate("🔗", `Build details:\n${buildLink}`)}`, + ); + } + if (telegram) { await sendTelegramNotification( telegram, diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 3aec6f3d3..21e061955 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -1,3 +1,4 @@ +import { error } from "node:console"; import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup"; @@ -6,6 +7,7 @@ import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, } from "./utils"; @@ -37,11 +39,12 @@ export const sendDatabaseBackupNotifications = async ({ discord: true, telegram: true, slack: true, + gotify: true, }, }); for (const notification of notificationList) { - const { email, discord, telegram, slack } = notification; + const { email, discord, telegram, slack, gotify } = notification; if (email) { const template = await renderAsync( @@ -120,6 +123,24 @@ export const sendDatabaseBackupNotifications = async ({ }); } + if (gotify) { + const decorate = (decoration: string, text: string) => + `${gotify.decoration ? decoration : ""} ${text}`.trim(); + + await sendGotifyNotification( + gotify, + decorate( + type === "success" ? "✅" : "❌", + `Database Backup ${type === "success" ? "Successful" : "Failed"}`, + ), + `${decorate("🛠️", `Project: ${projectName}`)} + ${decorate("⚙️", `Application: ${applicationName}`)} + ${decorate("❔", `Type: ${databaseType}`)} + ${decorate("🕒", `Date: ${date.toLocaleString()}`)} + ${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`, + ); + } + if (telegram) { const statusEmoji = type === "success" ? "✅" : "❌"; const messageText = ` diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index c95c79067..bb167b914 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -6,6 +6,7 @@ import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, } from "./utils"; @@ -26,11 +27,12 @@ export const sendDockerCleanupNotifications = async ( discord: true, telegram: true, slack: true, + gotify: true, }, }); for (const notification of notificationList) { - const { email, discord, telegram, slack } = notification; + const { email, discord, telegram, slack, gotify } = notification; if (email) { const template = await renderAsync( @@ -79,6 +81,17 @@ export const sendDockerCleanupNotifications = async ( }); } + if (gotify) { + const decorate = (decoration: string, text: string) => + `${gotify.decoration ? decoration : ""} ${text}`.trim(); + await sendGotifyNotification( + gotify, + decorate("✅", "Docker Cleanup"), + `${decorate("🕒", `Date: ${date.toLocaleString()}`)} + ${decorate("📜", `Message:\n${message}`)}`, + ); + } + if (telegram) { await sendTelegramNotification( telegram, diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 16170349b..ec4a9407d 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -6,6 +6,7 @@ import { eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, + sendGotifyNotification, sendSlackNotification, sendTelegramNotification, } from "./utils"; @@ -20,11 +21,12 @@ export const sendDokployRestartNotifications = async () => { discord: true, telegram: true, slack: true, + gotify: true, }, }); for (const notification of notificationList) { - const { email, discord, telegram, slack } = notification; + const { email, discord, telegram, slack, gotify } = notification; if (email) { const template = await renderAsync( @@ -64,11 +66,21 @@ export const sendDokployRestartNotifications = async () => { }); } + if (gotify) { + const decorate = (decoration: string, text: string) => + `${gotify.decoration ? decoration : ""} ${text}`.trim(); + await sendGotifyNotification( + gotify, + decorate("✅", "Dokploy Server Restarted"), + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}`, + ); + } + if (telegram) { await sendTelegramNotification( telegram, ` - ✅ Dokploy Serverd Restarted + ✅ Dokploy Server Restarted Time: ${date.toLocaleString()} `, ); From 9806a5d60743241c07b5f90348ea0b0bde2857b9 Mon Sep 17 00:00:00 2001 From: depado Date: Sat, 18 Jan 2025 13:59:39 +0100 Subject: [PATCH 35/62] feat(notifications): fix gotify style --- .../server/src/utils/notifications/build-error.ts | 14 +++++++------- .../src/utils/notifications/build-success.ts | 12 ++++++------ .../src/utils/notifications/database-backup.ts | 12 ++++++------ .../src/utils/notifications/docker-cleanup.ts | 6 +++--- .../src/utils/notifications/dokploy-restart.ts | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 90bfd77f6..086effb09 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -116,16 +116,16 @@ export const sendBuildErrorNotifications = async ({ if (gotify) { const decorate = (decoration: string, text: string) => - `${gotify.decoration ? decoration : ""} ${text}`.trim(); + `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( 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("🛠️", `Project: ${projectName}`)}` + + `${decorate("⚙️", `Application: ${applicationName}`)}` + + `${decorate("❔", `Type: ${applicationType}`)}` + + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}` + + `${decorate("⚠️", `Error:\n${errorMessage}`)}` + + `${decorate("🔗", `Build details:\n${buildLink}`)}`, ); } diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 253762f5d..76d3ee12c 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -110,15 +110,15 @@ export const sendBuildSuccessNotifications = async ({ if (gotify) { const decorate = (decoration: string, text: string) => - `${gotify.decoration ? decoration : ""} ${text}`.trim(); + `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( gotify, decorate("✅", "Build Success"), - `${decorate("🛠️", `Project: ${projectName}`)} - ${decorate("⚙️", `Application: ${applicationName}`)} - ${decorate("❔", `Type: ${applicationType}`)} - ${decorate("🕒", `Date: ${date.toLocaleString()}`)} - ${decorate("🔗", `Build details:\n${buildLink}`)}`, + `${decorate("🛠️", `Project: ${projectName}`)}` + + `${decorate("⚙️", `Application: ${applicationName}`)}` + + `${decorate("❔", `Type: ${applicationType}`)}` + + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}` + + `${decorate("🔗", `Build details:\n${buildLink}`)}`, ); } diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 21e061955..43b967cc2 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -125,7 +125,7 @@ export const sendDatabaseBackupNotifications = async ({ if (gotify) { const decorate = (decoration: string, text: string) => - `${gotify.decoration ? decoration : ""} ${text}`.trim(); + `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( gotify, @@ -133,11 +133,11 @@ export const sendDatabaseBackupNotifications = async ({ type === "success" ? "✅" : "❌", `Database Backup ${type === "success" ? "Successful" : "Failed"}`, ), - `${decorate("🛠️", `Project: ${projectName}`)} - ${decorate("⚙️", `Application: ${applicationName}`)} - ${decorate("❔", `Type: ${databaseType}`)} - ${decorate("🕒", `Date: ${date.toLocaleString()}`)} - ${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`, + `${decorate("🛠️", `Project: ${projectName}`)}` + + `${decorate("⚙️", `Application: ${applicationName}`)}` + + `${decorate("❔", `Type: ${databaseType}`)}` + + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}` + + `${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`, ); } diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index bb167b914..d4aaa5c88 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -83,12 +83,12 @@ export const sendDockerCleanupNotifications = async ( if (gotify) { const decorate = (decoration: string, text: string) => - `${gotify.decoration ? decoration : ""} ${text}`.trim(); + `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( gotify, decorate("✅", "Docker Cleanup"), - `${decorate("🕒", `Date: ${date.toLocaleString()}`)} - ${decorate("📜", `Message:\n${message}`)}`, + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}` + + `${decorate("📜", `Message:\n${message}`)}`, ); } diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index ec4a9407d..30f2ad659 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -68,7 +68,7 @@ export const sendDokployRestartNotifications = async () => { if (gotify) { const decorate = (decoration: string, text: string) => - `${gotify.decoration ? decoration : ""} ${text}`.trim(); + `${gotify.decoration ? decoration : ""} ${text}\n`; await sendGotifyNotification( gotify, decorate("✅", "Dokploy Server Restarted"), From d060eec46509b245718587cd841d501b14ec6551 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 18 Jan 2025 17:38:33 -0600 Subject: [PATCH 36/62] Update apps/dokploy/templates/couchdb/docker-compose.yml --- apps/dokploy/templates/couchdb/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/couchdb/docker-compose.yml b/apps/dokploy/templates/couchdb/docker-compose.yml index 41ed372a5..cb00bf69d 100644 --- a/apps/dokploy/templates/couchdb/docker-compose.yml +++ b/apps/dokploy/templates/couchdb/docker-compose.yml @@ -4,7 +4,7 @@ services: couchdb: image: couchdb:latest ports: - - '5984:5984' + - '5984' volumes: - couchdb-data:/opt/couchdb/data environment: From 25a8df567e4ed4d992e8540560e6150f2e6e047c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 00:57:42 -0600 Subject: [PATCH 37/62] feat: add cleanup cache on deployments --- .../dashboard/project/add-template.tsx | 4 +- apps/dokploy/components/layouts/user-nav.tsx | 18 +- apps/dokploy/drizzle/0057_oval_ken_ellis.sql | 1 + .../dokploy/drizzle/0058_cultured_warpath.sql | 1 + apps/dokploy/drizzle/0059_public_speed.sql | 3 + apps/dokploy/drizzle/meta/0057_snapshot.json | 4321 ++++++++++++++++ apps/dokploy/drizzle/meta/0058_snapshot.json | 4321 ++++++++++++++++ apps/dokploy/drizzle/meta/0059_snapshot.json | 4335 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 21 + .../pages/dashboard/settings/index.tsx | 220 + apps/dokploy/server/api/routers/project.ts | 5 +- apps/dokploy/styles/globals.css | 8 +- apps/dokploy/templates/templates.ts | 3 +- packages/server/src/db/schema/admin.ts | 9 + packages/server/src/services/application.ts | 51 +- packages/server/src/services/compose.ts | 24 +- packages/server/src/services/settings.ts | 33 + packages/server/src/services/user.ts | 6 +- .../src/utils/notifications/build-error.ts | 6 +- .../src/utils/notifications/build-success.ts | 21 +- .../utils/notifications/database-backup.ts | 12 +- .../src/utils/notifications/docker-cleanup.ts | 4 +- .../utils/notifications/dokploy-restart.ts | 4 +- .../server/src/utils/notifications/utils.ts | 2 +- 24 files changed, 13380 insertions(+), 53 deletions(-) create mode 100644 apps/dokploy/drizzle/0057_oval_ken_ellis.sql create mode 100644 apps/dokploy/drizzle/0058_cultured_warpath.sql create mode 100644 apps/dokploy/drizzle/0059_public_speed.sql create mode 100644 apps/dokploy/drizzle/meta/0057_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0058_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0059_snapshot.json create mode 100644 apps/dokploy/pages/dashboard/settings/index.tsx diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index f7a6a5469..90d173640 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -133,7 +133,9 @@ export const AddTemplate = ({ projectId }: Props) => { + + + + +
+ +
+ ); +}; + +export default Page; + +Page.getLayout = (page: ReactElement) => { + return {page}; +}; +export async function getServerSideProps( + ctx: GetServerSidePropsContext<{ serviceId: string }>, +) { + const { req, res } = ctx; + const { user, session } = await validateRequest(ctx.req, ctx.res); + if (!user) { + return { + redirect: { + permanent: true, + destination: "/", + }, + }; + } + if (user.rol === "user") { + return { + redirect: { + permanent: true, + destination: "/dashboard/settings/profile", + }, + }; + } + + const helpers = createServerSideHelpers({ + router: appRouter, + ctx: { + req: req as any, + res: res as any, + db: null as any, + session: session, + user: user, + }, + transformer: superjson, + }); + await helpers.auth.get.prefetch(); + + return { + props: { + trpcState: helpers.dehydrate(), + }, + }; +} diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 19acb9a3e..9c2608cca 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -239,7 +239,10 @@ export const projectRouter = createTRPCRouter({ } }), }); -function buildServiceFilter(fieldName: AnyPgColumn, accessedServices: string[]) { +function buildServiceFilter( + fieldName: AnyPgColumn, + accessedServices: string[], +) { return accessedServices.length > 0 ? sql`${fieldName} IN (${sql.join( accessedServices.map((serviceId) => sql`${serviceId}`), diff --git a/apps/dokploy/styles/globals.css b/apps/dokploy/styles/globals.css index 3910d69a1..7b7977b9c 100644 --- a/apps/dokploy/styles/globals.css +++ b/apps/dokploy/styles/globals.css @@ -101,7 +101,7 @@ * { @apply border-border; } - + body { @apply bg-background text-foreground; } @@ -110,16 +110,16 @@ ::-webkit-scrollbar { width: 0.3125rem; } - + ::-webkit-scrollbar-track { background: transparent; } - + ::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 0.3125rem; } - + * { scrollbar-width: thin; scrollbar-color: hsl(var(--border)) transparent; diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 6238a19c6..9531eb7ae 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1269,7 +1269,8 @@ export const templates: TemplateData[] = [ }, tags: ["cloud", "networking", "security", "tunnel"], load: () => import("./cloudflared/index").then((m) => m.generate), - },{ + }, + { id: "couchdb", name: "CouchDB", version: "latest", diff --git a/packages/server/src/db/schema/admin.ts b/packages/server/src/db/schema/admin.ts index 222fb16c8..e9c73bcc1 100644 --- a/packages/server/src/db/schema/admin.ts +++ b/packages/server/src/db/schema/admin.ts @@ -31,6 +31,15 @@ export const admins = pgTable("admin", { stripeCustomerId: text("stripeCustomerId"), stripeSubscriptionId: text("stripeSubscriptionId"), serversQuantity: integer("serversQuantity").notNull().default(0), + cleanupCacheApplications: boolean("cleanupCacheApplications") + .notNull() + .default(true), + cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") + .notNull() + .default(false), + cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") + .notNull() + .default(false), }); export const adminsRelations = relations(admins, ({ one, many }) => ({ diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index 068e69b36..ccadebf79 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -40,7 +40,7 @@ import { createTraefikConfig } from "@dokploy/server/utils/traefik/application"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { getDokployUrl } from "./admin"; +import { findAdminById, getDokployUrl } from "./admin"; import { createDeployment, createDeploymentPreview, @@ -58,6 +58,7 @@ import { updatePreviewDeployment, } from "./preview-deployment"; import { validUniqueServerAppName } from "./project"; +import { cleanupFullDocker } from "./settings"; export type Application = typeof applications.$inferSelect; export const createApplication = async ( @@ -213,7 +214,7 @@ export const deployApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, - domains: application.domains + domains: application.domains, }); } catch (error) { await updateDeploymentStatus(deployment.deploymentId, "error"); @@ -229,6 +230,12 @@ export const deployApplication = async ({ }); throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheApplications) { + await cleanupFullDocker(application?.serverId); + } } return true; @@ -270,6 +277,12 @@ export const rebuildApplication = async ({ await updateDeploymentStatus(deployment.deploymentId, "error"); await updateApplicationStatus(applicationId, "error"); throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheApplications) { + await cleanupFullDocker(application?.serverId); + } } return true; @@ -333,7 +346,7 @@ export const deployRemoteApplication = async ({ applicationType: "application", buildLink, adminId: application.project.adminId, - domains: application.domains + domains: application.domains, }); } catch (error) { // @ts-ignore @@ -359,15 +372,13 @@ export const deployRemoteApplication = async ({ adminId: application.project.adminId, }); - console.log( - "Error on ", - application.buildType, - "/", - application.sourceType, - error, - ); - throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheApplications) { + await cleanupFullDocker(application?.serverId); + } } return true; @@ -475,6 +486,12 @@ export const deployPreviewApplication = async ({ previewStatus: "error", }); throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheOnPreviews) { + await cleanupFullDocker(application?.serverId); + } } return true; @@ -587,6 +604,12 @@ export const deployRemotePreviewApplication = async ({ previewStatus: "error", }); throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheOnPreviews) { + await cleanupFullDocker(application?.serverId); + } } return true; @@ -634,6 +657,12 @@ export const rebuildRemoteApplication = async ({ await updateDeploymentStatus(deployment.deploymentId, "error"); await updateApplicationStatus(applicationId, "error"); throw error; + } finally { + const admin = await findAdminById(application.project.adminId); + + if (admin.cleanupCacheApplications) { + await cleanupFullDocker(application?.serverId); + } } return true; diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 8561dd376..7f6a59547 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -3,7 +3,6 @@ import { paths } from "@dokploy/server/constants"; import { db } from "@dokploy/server/db"; import { type apiCreateCompose, compose } from "@dokploy/server/db/schema"; import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; -import { generatePassword } from "@dokploy/server/templates/utils"; import { buildCompose, getBuildComposeCommand, @@ -45,9 +44,10 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { getDokployUrl } from "./admin"; +import { findAdminById, getDokployUrl } from "./admin"; import { createDeploymentCompose, updateDeploymentStatus } from "./deployment"; import { validUniqueServerAppName } from "./project"; +import { cleanupFullDocker } from "./settings"; export type Compose = typeof compose.$inferSelect; @@ -260,6 +260,11 @@ export const deployCompose = async ({ adminId: compose.project.adminId, }); throw error; + } finally { + const admin = await findAdminById(compose.project.adminId); + if (admin.cleanupCacheOnCompose) { + await cleanupFullDocker(compose?.serverId); + } } }; @@ -296,6 +301,11 @@ export const rebuildCompose = async ({ composeStatus: "error", }); throw error; + } finally { + const admin = await findAdminById(compose.project.adminId); + if (admin.cleanupCacheOnCompose) { + await cleanupFullDocker(compose?.serverId); + } } return true; @@ -394,6 +404,11 @@ export const deployRemoteCompose = async ({ adminId: compose.project.adminId, }); throw error; + } finally { + const admin = await findAdminById(compose.project.adminId); + if (admin.cleanupCacheOnCompose) { + await cleanupFullDocker(compose?.serverId); + } } }; @@ -438,6 +453,11 @@ export const rebuildRemoteCompose = async ({ composeStatus: "error", }); throw error; + } finally { + const admin = await findAdminById(compose.project.adminId); + if (admin.cleanupCacheOnCompose) { + await cleanupFullDocker(compose?.serverId); + } } return true; diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index 37f7b2ee8..d22780c94 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -5,6 +5,7 @@ import { execAsync, execAsyncRemote, } from "@dokploy/server/utils/process/execAsync"; +import { findAdminById } from "./admin"; // import packageInfo from "../../../package.json"; export interface IUpdateData { @@ -213,3 +214,35 @@ echo "$json_output" } return result; }; + +export const cleanupFullDocker = async (serverId?: string | null) => { + const cleanupImages = "docker image prune --all --force"; + const cleanupVolumes = "docker volume prune --all --force"; + const cleanupContainers = "docker container prune --force"; + const cleanupSystem = "docker system prune --all --force --volumes"; + const cleanupBuilder = "docker builder prune --all --force"; + + try { + if (serverId) { + await execAsyncRemote( + serverId, + ` + ${cleanupImages} + ${cleanupVolumes} + ${cleanupContainers} + ${cleanupSystem} + ${cleanupBuilder} + `, + ); + } + await execAsync(` + ${cleanupImages} + ${cleanupVolumes} + ${cleanupContainers} + ${cleanupSystem} + ${cleanupBuilder} + `); + } catch (error) { + console.log(error); + } +}; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index c8a9849c7..d8d9862c4 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -73,7 +73,8 @@ export const canPerformCreationService = async ( userId: string, projectId: string, ) => { - const { accessedProjects, canCreateServices } = await findUserByAuthId(userId); + const { accessedProjects, canCreateServices } = + await findUserByAuthId(userId); const haveAccessToProject = accessedProjects.includes(projectId); if (canCreateServices && haveAccessToProject) { @@ -101,7 +102,8 @@ export const canPeformDeleteService = async ( authId: string, serviceId: string, ) => { - const { accessedServices, canDeleteServices } = await findUserByAuthId(authId); + const { accessedServices, canDeleteServices } = + await findUserByAuthId(authId); const haveAccessToService = accessedServices.includes(serviceId); if (canDeleteServices && haveAccessToService) { diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 2ab2125c8..95936652c 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -2,8 +2,8 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import BuildFailedEmail from "@dokploy/server/emails/emails/build-failed"; import { renderAsync } from "@react-email/components"; -import { and, eq } from "drizzle-orm"; import { format } from "date-fns"; +import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, @@ -139,11 +139,11 @@ export const sendBuildErrorNotifications = async ({ }, ], ]; - + await sendTelegramNotification( telegram, `⚠️ Build Failed\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}\n\nError:\n
${errorMessage}
`, - inlineButton + inlineButton, ); } diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 19b178115..960f7a6a4 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -1,10 +1,10 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import BuildSuccessEmail from "@dokploy/server/emails/emails/build-success"; -import { Domain } from "@dokploy/server/services/domain"; +import type { Domain } from "@dokploy/server/services/domain"; import { renderAsync } from "@react-email/components"; -import { and, eq } from "drizzle-orm"; import { format } from "date-fns"; +import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, @@ -28,7 +28,7 @@ export const sendBuildSuccessNotifications = async ({ applicationType, buildLink, adminId, - domains + domains, }: Props) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); @@ -128,9 +128,10 @@ export const sendBuildSuccessNotifications = async ({ if (telegram) { const chunkArray = (array: T[], chunkSize: number): T[][] => - Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, i) => array.slice(i * chunkSize, i * chunkSize + chunkSize) - ); - + Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, i) => + array.slice(i * chunkSize, i * chunkSize + chunkSize), + ); + const inlineButton = [ [ { @@ -142,14 +143,14 @@ export const sendBuildSuccessNotifications = async ({ chunk.map((data) => ({ text: data.host, url: `${data.https ? "https" : "http"}://${data.host}`, - })) + })), ), ]; - + await sendTelegramNotification( telegram, - `✅ Build Success\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, - inlineButton + `✅ Build Success\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${applicationType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, + inlineButton, ); } diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 1460964da..0b1d61f7e 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -3,8 +3,8 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup"; import { renderAsync } from "@react-email/components"; -import { and, eq } from "drizzle-orm"; import { format } from "date-fns"; +import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, @@ -144,13 +144,15 @@ export const sendDatabaseBackupNotifications = async ({ if (telegram) { const isError = type === "error" && errorMessage; - + const statusEmoji = type === "success" ? "✅" : "❌"; const typeStatus = type === "success" ? "Successful" : "Failed"; - const errorMsg = isError ? `\n\nError:\n
${errorMessage}
` : ""; - + const errorMsg = isError + ? `\n\nError:\n
${errorMessage}
` + : ""; + const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; - + await sendTelegramNotification(telegram, messageText); } diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index 05624e498..b60e3b0ac 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -2,8 +2,8 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DockerCleanupEmail from "@dokploy/server/emails/emails/docker-cleanup"; import { renderAsync } from "@react-email/components"; -import { and, eq } from "drizzle-orm"; import { format } from "date-fns"; +import { and, eq } from "drizzle-orm"; import { sendDiscordNotification, sendEmailNotification, @@ -96,7 +96,7 @@ export const sendDockerCleanupNotifications = async ( if (telegram) { await sendTelegramNotification( telegram, - `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}` + `✅ Docker Cleanup\n\nMessage: ${message}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, ); } diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 6debb7a77..5a156affe 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -2,6 +2,7 @@ import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DokployRestartEmail from "@dokploy/server/emails/emails/dokploy-restart"; import { renderAsync } from "@react-email/components"; +import { format } from "date-fns"; import { eq } from "drizzle-orm"; import { sendDiscordNotification, @@ -10,7 +11,6 @@ import { sendSlackNotification, sendTelegramNotification, } from "./utils"; -import { format } from "date-fns"; export const sendDokployRestartNotifications = async () => { const date = new Date(); @@ -80,7 +80,7 @@ export const sendDokployRestartNotifications = async () => { if (telegram) { await sendTelegramNotification( telegram, - `✅ Dokploy Server Restarted\n\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}` + `✅ Dokploy Server Restarted\n\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}`, ); } diff --git a/packages/server/src/utils/notifications/utils.ts b/packages/server/src/utils/notifications/utils.ts index ede46034c..4f8bb1a5e 100644 --- a/packages/server/src/utils/notifications/utils.ts +++ b/packages/server/src/utils/notifications/utils.ts @@ -59,7 +59,7 @@ export const sendTelegramNotification = async ( inlineButton?: { text: string; url: string; - }[][] + }[][], ) => { try { const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`; From 52c83fd6fcb1927acbb94a74f9de4bda73338a7e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 01:07:41 -0600 Subject: [PATCH 38/62] refactor: update text --- apps/dokploy/pages/dashboard/settings/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index f5b73ea7e..bf76607b4 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -102,7 +102,7 @@ const Page = () => {
Clean Cache on Applications - Clean the cache after every deployment + Clean the cache after every application deployment
@@ -122,7 +122,7 @@ const Page = () => {
Clean Cache on Previews - Clean the cache after every deployment + Clean the cache after every preview deployment
@@ -142,7 +142,7 @@ const Page = () => {
Clean Cache on Compose - Clean the cache after every deployment + Clean the cache after every compose deployment
From 43b7db00f93db5715a469996589c03a1d3444713 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 01:21:03 -0600 Subject: [PATCH 39/62] refactor: add missing values to test --- .../server/update-server-config.test.ts | 113 +++++++++--------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index c966748a7..9c5beb407 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -1,95 +1,98 @@ import { fs, vol } from "memfs"; vi.mock("node:fs", () => ({ - ...fs, - default: fs, + ...fs, + default: fs, })); import type { Admin, FileConfig } from "@dokploy/server"; import { - createDefaultServerTraefikConfig, - loadOrCreateConfig, - updateServerTraefik, + createDefaultServerTraefikConfig, + loadOrCreateConfig, + updateServerTraefik, } from "@dokploy/server"; import { beforeEach, expect, test, vi } from "vitest"; const baseAdmin: Admin = { - createdAt: "", - authId: "", - adminId: "string", - serverIp: null, - certificateType: "none", - host: null, - letsEncryptEmail: null, - sshPrivateKey: null, - enableDockerCleanup: false, - enableLogRotation: false, - serversQuantity: 0, - stripeCustomerId: "", - stripeSubscriptionId: "", + cleanupCacheApplications: false, + cleanupCacheOnCompose: false, + cleanupCacheOnPreviews: false, + createdAt: "", + authId: "", + adminId: "string", + serverIp: null, + certificateType: "none", + host: null, + letsEncryptEmail: null, + sshPrivateKey: null, + enableDockerCleanup: false, + enableLogRotation: false, + serversQuantity: 0, + stripeCustomerId: "", + stripeSubscriptionId: "", }; beforeEach(() => { - vol.reset(); - createDefaultServerTraefikConfig(); + vol.reset(); + createDefaultServerTraefikConfig(); }); test("Should read the configuration file", () => { - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe( - "dokploy-service-app", - ); + expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe( + "dokploy-service-app" + ); }); test("Should apply redirect-to-https", () => { - updateServerTraefik( - { - ...baseAdmin, - certificateType: "letsencrypt", - }, - "example.com", - ); - - const config: FileConfig = loadOrCreateConfig("dokploy"); - - expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain( - "redirect-to-https", - ); + updateServerTraefik( + { + ...baseAdmin, + certificateType: "letsencrypt", + }, + "example.com" + ); + + const config: FileConfig = loadOrCreateConfig("dokploy"); + + expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain( + "redirect-to-https" + ); }); test("Should change only host when no certificate", () => { - updateServerTraefik(baseAdmin, "example.com"); + updateServerTraefik(baseAdmin, "example.com"); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); + expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); }); test("Should not touch config without host", () => { - const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); + const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik(baseAdmin, null); + updateServerTraefik(baseAdmin, null); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(originalConfig).toEqual(config); + expect(originalConfig).toEqual(config); }); test("Should remove websecure if https rollback to http", () => { - const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); + const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik( - { ...baseAdmin, certificateType: "letsencrypt" }, - "example.com", - ); + updateServerTraefik( + { ...baseAdmin, certificateType: "letsencrypt" }, + "example.com" + ); - updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); + updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); - expect( - config.http?.routers?.["dokploy-router-app"]?.middlewares, - ).not.toContain("redirect-to-https"); + expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); + expect( + config.http?.routers?.["dokploy-router-app"]?.middlewares + ).not.toContain("redirect-to-https"); }); From 5310a559b0487a946e3bd603751e688691a971cf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 01:29:29 -0600 Subject: [PATCH 40/62] refactor: improve sidebar --- .../server/update-server-config.test.ts | 116 +++++++++--------- .../database/backups/show-backups.tsx | 2 +- .../dashboard/monitoring/docker/show.tsx | 2 +- .../components/dashboard/projects/show.tsx | 6 +- .../certificates/show-certificates.tsx | 2 +- .../notifications/show-notifications.tsx | 2 +- .../dashboard/swarm/details/details-card.tsx | 2 +- .../dashboard/swarm/monitoring-card.tsx | 2 +- .../components/layouts/dashboard-layout.tsx | 6 +- .../components/layouts/onboarding-layout.tsx | 23 +--- .../components/layouts/project-layout.tsx | 6 +- apps/dokploy/components/layouts/side.tsx | 4 +- .../components/shared/breadcrumb-sidebar.tsx | 4 +- apps/dokploy/components/ui/sidebar.tsx | 2 +- .../pages/dashboard/project/[projectId].tsx | 6 +- apps/dokploy/pages/dashboard/swarm.tsx | 6 +- 16 files changed, 80 insertions(+), 111 deletions(-) diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 9c5beb407..fac90cc72 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -1,98 +1,98 @@ import { fs, vol } from "memfs"; vi.mock("node:fs", () => ({ - ...fs, - default: fs, + ...fs, + default: fs, })); import type { Admin, FileConfig } from "@dokploy/server"; import { - createDefaultServerTraefikConfig, - loadOrCreateConfig, - updateServerTraefik, + createDefaultServerTraefikConfig, + loadOrCreateConfig, + updateServerTraefik, } from "@dokploy/server"; import { beforeEach, expect, test, vi } from "vitest"; const baseAdmin: Admin = { - cleanupCacheApplications: false, - cleanupCacheOnCompose: false, - cleanupCacheOnPreviews: false, - createdAt: "", - authId: "", - adminId: "string", - serverIp: null, - certificateType: "none", - host: null, - letsEncryptEmail: null, - sshPrivateKey: null, - enableDockerCleanup: false, - enableLogRotation: false, - serversQuantity: 0, - stripeCustomerId: "", - stripeSubscriptionId: "", + cleanupCacheApplications: false, + cleanupCacheOnCompose: false, + cleanupCacheOnPreviews: false, + createdAt: "", + authId: "", + adminId: "string", + serverIp: null, + certificateType: "none", + host: null, + letsEncryptEmail: null, + sshPrivateKey: null, + enableDockerCleanup: false, + enableLogRotation: false, + serversQuantity: 0, + stripeCustomerId: "", + stripeSubscriptionId: "", }; beforeEach(() => { - vol.reset(); - createDefaultServerTraefikConfig(); + vol.reset(); + createDefaultServerTraefikConfig(); }); test("Should read the configuration file", () => { - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe( - "dokploy-service-app" - ); + expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe( + "dokploy-service-app", + ); }); test("Should apply redirect-to-https", () => { - updateServerTraefik( - { - ...baseAdmin, - certificateType: "letsencrypt", - }, - "example.com" - ); - - const config: FileConfig = loadOrCreateConfig("dokploy"); - - expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain( - "redirect-to-https" - ); + updateServerTraefik( + { + ...baseAdmin, + certificateType: "letsencrypt", + }, + "example.com", + ); + + const config: FileConfig = loadOrCreateConfig("dokploy"); + + expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain( + "redirect-to-https", + ); }); test("Should change only host when no certificate", () => { - updateServerTraefik(baseAdmin, "example.com"); + updateServerTraefik(baseAdmin, "example.com"); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); + expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); }); test("Should not touch config without host", () => { - const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); + const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik(baseAdmin, null); + updateServerTraefik(baseAdmin, null); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(originalConfig).toEqual(config); + expect(originalConfig).toEqual(config); }); test("Should remove websecure if https rollback to http", () => { - const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); + const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik( - { ...baseAdmin, certificateType: "letsencrypt" }, - "example.com" - ); + updateServerTraefik( + { ...baseAdmin, certificateType: "letsencrypt" }, + "example.com", + ); - updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); + updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); - const config: FileConfig = loadOrCreateConfig("dokploy"); + const config: FileConfig = loadOrCreateConfig("dokploy"); - expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); - expect( - config.http?.routers?.["dokploy-router-app"]?.middlewares - ).not.toContain("redirect-to-https"); + expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); + expect( + config.http?.routers?.["dokploy-router-app"]?.middlewares, + ).not.toContain("redirect-to-https"); }); diff --git a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx index 9e493529c..21fe28d4a 100644 --- a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx +++ b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx @@ -75,7 +75,7 @@ export const ShowBackups = ({ id, type }: Props) => { {data?.length === 0 ? (
- + To create a backup it is required to set at least 1 provider. Please, go to{" "} -
+
CPU Usage diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 16f842ed2..c4b2f672d 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -77,8 +77,8 @@ export const ShowProjects = () => {
-
- +
+ Projects @@ -87,7 +87,7 @@ export const ShowProjects = () => { Create and manage your projects -
+
diff --git a/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx b/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx index f28a814db..6aaa25630 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx @@ -50,7 +50,7 @@ export const ShowCertificates = () => { {data?.length === 0 ? (
- + You don't have any certificates created diff --git a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx index 77fb8858d..d65069d4b 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx @@ -47,7 +47,7 @@ export const ShowNotifications = () => { {data?.length === 0 ? (
- + To send notifications it is required to set at least 1 provider. diff --git a/apps/dokploy/components/dashboard/swarm/details/details-card.tsx b/apps/dokploy/components/dashboard/swarm/details/details-card.tsx index 8901ba588..52c90c0f1 100644 --- a/apps/dokploy/components/dashboard/swarm/details/details-card.tsx +++ b/apps/dokploy/components/dashboard/swarm/details/details-card.tsx @@ -60,7 +60,7 @@ export function NodeCard({ node, serverId }: Props) {
{node.Hostname}
{node.ManagerStatus || "Worker"}
-
+
TLS Status: {node.TLSStatus} Availability: {node.Availability}
diff --git a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx index 98dc0d963..0c38b509f 100644 --- a/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx +++ b/apps/dokploy/components/dashboard/swarm/monitoring-card.tsx @@ -72,7 +72,7 @@ export default function SwarmMonitorCard({ serverId }: Props) { return (
-
+
diff --git a/apps/dokploy/components/layouts/dashboard-layout.tsx b/apps/dokploy/components/layouts/dashboard-layout.tsx index 13e9061d3..00697e7c4 100644 --- a/apps/dokploy/components/layouts/dashboard-layout.tsx +++ b/apps/dokploy/components/layouts/dashboard-layout.tsx @@ -5,9 +5,5 @@ interface Props { } export const DashboardLayout = ({ children }: Props) => { - return ( - -
{children}
-
- ); + return {children}; }; diff --git a/apps/dokploy/components/layouts/onboarding-layout.tsx b/apps/dokploy/components/layouts/onboarding-layout.tsx index 093c14921..9d4068cf1 100644 --- a/apps/dokploy/components/layouts/onboarding-layout.tsx +++ b/apps/dokploy/components/layouts/onboarding-layout.tsx @@ -11,7 +11,7 @@ interface Props { export const OnboardingLayout = ({ children }: Props) => { return (
-
+
{
-

+

“The Open Source alternative to Netlify, Vercel, Heroku.”

- {/*
Sofia Davis
*/}
{children} - - {/*

- By clicking continue, you agree to our{" "} - - Terms of Service - {" "} - and{" "} - - Privacy Policy - - . -

*/}
diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index f40a0a832..3a8a60b28 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -8,11 +8,7 @@ import type { ReactElement } from "react"; import superjson from "superjson"; const Dashboard = () => { - return ( - <> - - - ); + return ; }; export default Dashboard; From adb204ec1f99bb5be46115d9ade2e27c1ce1c1cf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 01:44:42 -0600 Subject: [PATCH 41/62] refactor: add sidebar persistence --- apps/dokploy/components/layouts/side.tsx | 23 ++++++++++++++++++++++- apps/dokploy/components/ui/sidebar.tsx | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 7a083167a..fba01cfcb 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,5 +1,5 @@ "use client"; - +import { useState, useEffect } from "react"; import { Activity, AudioWaveform, @@ -46,6 +46,7 @@ import { import { Separator } from "@/components/ui/separator"; import { Sidebar, + SIDEBAR_COOKIE_NAME, SidebarContent, SidebarFooter, SidebarGroup, @@ -369,6 +370,19 @@ function SidebarLogo() { } export default function Page({ children }: Props) { + const [defaultOpen, setDefaultOpen] = useState( + undefined, + ); + + useEffect(() => { + const cookieValue = document.cookie + .split("; ") + .find((row) => row.startsWith(`${SIDEBAR_COOKIE_NAME}=`)) + ?.split("=")[1]; + + setDefaultOpen(cookieValue === undefined ? true : cookieValue === "true"); + }, []); + const router = useRouter(); const pathname = usePathname(); const currentPath = router.pathname; @@ -445,6 +459,13 @@ export default function Page({ children }: Props) { return ( { + setDefaultOpen(open); + + document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}`; + }} style={ { "--sidebar-width": "19.5rem", diff --git a/apps/dokploy/components/ui/sidebar.tsx b/apps/dokploy/components/ui/sidebar.tsx index 24c809145..40f848739 100644 --- a/apps/dokploy/components/ui/sidebar.tsx +++ b/apps/dokploy/components/ui/sidebar.tsx @@ -17,7 +17,7 @@ import { import { useIsMobile } from "@/hooks/use-mobile"; import { cn } from "@/lib/utils"; -const SIDEBAR_COOKIE_NAME = "sidebar:state"; +export const SIDEBAR_COOKIE_NAME = "sidebar:state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; const SIDEBAR_WIDTH = "16rem"; const SIDEBAR_WIDTH_MOBILE = "18rem"; From e82db47ec4452256fac3e3bc490b24b71a5c6855 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 02:33:53 -0600 Subject: [PATCH 42/62] refactor: add github docker --- .github/workflows/docker-build.yml | 87 ++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/docker-build.yml diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..7638a2778 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,87 @@ +name: Docker Build + +on: + pull_request: + branches: [main, canary, feat/github-runners] +env: + IMAGE_NAME: dokploy/dokploy + +jobs: + docker-amd: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set tag and version + id: meta + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + VERSION=$(node -p "require('./apps/dokploy/package.json').version") + echo "tags=${IMAGE_NAME}:latest,${IMAGE_NAME}:${VERSION}" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then + echo "tags=${IMAGE_NAME}:canary" >> $GITHUB_OUTPUT + else + echo "tags=${IMAGE_NAME}:feature" >> $GITHUB_OUTPUT + fi + + - name: Prepare env file + run: | + cp apps/dokploy/.env.production.example .env.production + cp apps/dokploy/.env.production.example apps/dokploy/.env.production + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + docker-arm: + runs-on: ubuntu-24.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set tag and version + id: meta + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + VERSION=$(node -p "require('./apps/dokploy/package.json').version") + echo "tags=${IMAGE_NAME}:latest,${IMAGE_NAME}:${VERSION}" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then + echo "tags=${IMAGE_NAME}:canary" >> $GITHUB_OUTPUT + else + echo "tags=${IMAGE_NAME}:feature" >> $GITHUB_OUTPUT + fi + + - name: Prepare env file + run: | + cp apps/dokploy/.env.production.example .env.production + cp apps/dokploy/.env.production.example apps/dokploy/.env.production + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} From b6ae502b925b8203e42084e827296bd84f0a4236 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 02:38:38 -0600 Subject: [PATCH 43/62] refactor: update --- .github/workflows/docker-build.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 7638a2778..c25206fee 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -8,7 +8,7 @@ env: jobs: docker-amd: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 @@ -26,13 +26,14 @@ jobs: id: meta run: | if [ "${{ github.ref }}" = "refs/heads/main" ]; then + TAG="latest" VERSION=$(node -p "require('./apps/dokploy/package.json').version") - echo "tags=${IMAGE_NAME}:latest,${IMAGE_NAME}:${VERSION}" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then - echo "tags=${IMAGE_NAME}:canary" >> $GITHUB_OUTPUT + TAG="canary" else - echo "tags=${IMAGE_NAME}:feature" >> $GITHUB_OUTPUT + TAG="feature" fi + echo "tags=${IMAGE_NAME}:${TAG}-amd64" >> $GITHUB_OUTPUT - name: Prepare env file run: | @@ -65,13 +66,14 @@ jobs: id: meta run: | if [ "${{ github.ref }}" = "refs/heads/main" ]; then + TAG="latest" VERSION=$(node -p "require('./apps/dokploy/package.json').version") - echo "tags=${IMAGE_NAME}:latest,${IMAGE_NAME}:${VERSION}" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then - echo "tags=${IMAGE_NAME}:canary" >> $GITHUB_OUTPUT + TAG="canary" else - echo "tags=${IMAGE_NAME}:feature" >> $GITHUB_OUTPUT + TAG="feature" fi + echo "tags=${IMAGE_NAME}:${TAG}-arm64" >> $GITHUB_OUTPUT - name: Prepare env file run: | From 539aa7a85b515724b50ef5fa4a8f4f5685925469 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 02:47:14 -0600 Subject: [PATCH 44/62] refactor: print version --- .github/workflows/docker-build.yml | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index c25206fee..04e5882ca 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -65,6 +65,8 @@ jobs: - name: Set tag and version id: meta run: | + VERSION=$(node -p "require('./apps/dokploy/package.json').version") + echo $VERSION if [ "${{ github.ref }}" = "refs/heads/main" ]; then TAG="latest" VERSION=$(node -p "require('./apps/dokploy/package.json').version") @@ -87,3 +89,46 @@ jobs: platforms: linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} + + combine-manifests: + needs: [docker-amd, docker-arm] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Create and push manifests + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + VERSION=$(node -p "require('./apps/dokploy/package.json').version") + TAG="latest" + + docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \ + ${IMAGE_NAME}:${TAG}-amd64 \ + ${IMAGE_NAME}:${TAG}-arm64 + + docker buildx imagetools create -t ${IMAGE_NAME}:${VERSION} \ + ${IMAGE_NAME}:${TAG}-amd64 \ + ${IMAGE_NAME}:${TAG}-arm64 + + elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then + TAG="canary" + docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \ + ${IMAGE_NAME}:${TAG}-amd64 \ + ${IMAGE_NAME}:${TAG}-arm64 + + else + TAG="feature" + docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \ + ${IMAGE_NAME}:${TAG}-amd64 \ + ${IMAGE_NAME}:${TAG}-arm64 + fi From 1e4a41a8e3537066558994a8654b166faf0d460d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 02:55:05 -0600 Subject: [PATCH 45/62] chore: replace circle with github actions --- .circleci/config.yml | 119 ----------------------------- .github/workflows/docker-build.yml | 4 +- 2 files changed, 2 insertions(+), 121 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ba405c22e..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,119 +0,0 @@ -version: 2.1 - -jobs: - build-amd64: - machine: - image: ubuntu-2004:current - steps: - - checkout - - run: - name: Prepare .env file - command: | - cp apps/dokploy/.env.production.example .env.production - cp apps/dokploy/.env.production.example apps/dokploy/.env.production - - - run: - name: Build and push AMD64 image - command: | - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN - if [ "${CIRCLE_BRANCH}" == "main" ]; then - TAG="latest" - elif [ "${CIRCLE_BRANCH}" == "canary" ]; then - TAG="canary" - else - TAG="feature" - fi - docker build --platform linux/amd64 -t dokploy/dokploy:${TAG}-amd64 . - docker push dokploy/dokploy:${TAG}-amd64 - - build-arm64: - machine: - image: ubuntu-2004:current - resource_class: arm.large - steps: - - checkout - - run: - name: Prepare .env file - command: | - cp apps/dokploy/.env.production.example .env.production - cp apps/dokploy/.env.production.example apps/dokploy/.env.production - - run: - name: Build and push ARM64 image - command: | - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN - if [ "${CIRCLE_BRANCH}" == "main" ]; then - TAG="latest" - elif [ "${CIRCLE_BRANCH}" == "canary" ]; then - TAG="canary" - else - TAG="feature" - fi - docker build --platform linux/arm64 -t dokploy/dokploy:${TAG}-arm64 . - docker push dokploy/dokploy:${TAG}-arm64 - - combine-manifests: - docker: - - image: cimg/node:20.9.0 - steps: - - checkout - - setup_remote_docker - - run: - name: Create and push multi-arch manifest - command: | - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN - - if [ "${CIRCLE_BRANCH}" == "main" ]; then - VERSION=$(node -p "require('./apps/dokploy/package.json').version") - echo $VERSION - TAG="latest" - - docker manifest create dokploy/dokploy:${TAG} \ - dokploy/dokploy:${TAG}-amd64 \ - dokploy/dokploy:${TAG}-arm64 - docker manifest push dokploy/dokploy:${TAG} - - docker manifest create dokploy/dokploy:${VERSION} \ - dokploy/dokploy:${TAG}-amd64 \ - dokploy/dokploy:${TAG}-arm64 - docker manifest push dokploy/dokploy:${VERSION} - elif [ "${CIRCLE_BRANCH}" == "canary" ]; then - TAG="canary" - docker manifest create dokploy/dokploy:${TAG} \ - dokploy/dokploy:${TAG}-amd64 \ - dokploy/dokploy:${TAG}-arm64 - docker manifest push dokploy/dokploy:${TAG} - else - TAG="feature" - docker manifest create dokploy/dokploy:${TAG} \ - dokploy/dokploy:${TAG}-amd64 \ - dokploy/dokploy:${TAG}-arm64 - docker manifest push dokploy/dokploy:${TAG} - fi - -workflows: - build-all: - jobs: - - build-amd64: - filters: - branches: - only: - - main - - canary - - feat/add-sidebar - - build-arm64: - filters: - branches: - only: - - main - - canary - - feat/add-sidebar - - combine-manifests: - requires: - - build-amd64 - - build-arm64 - filters: - branches: - only: - - main - - canary - - feat/add-sidebar diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 04e5882ca..31b17c3e8 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -1,8 +1,9 @@ name: Docker Build on: - pull_request: + push: branches: [main, canary, feat/github-runners] + env: IMAGE_NAME: dokploy/dokploy @@ -66,7 +67,6 @@ jobs: id: meta run: | VERSION=$(node -p "require('./apps/dokploy/package.json').version") - echo $VERSION if [ "${{ github.ref }}" = "refs/heads/main" ]; then TAG="latest" VERSION=$(node -p "require('./apps/dokploy/package.json').version") From 4b6f9108d4198bfc6f088c3b11a2e13b2efde485 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 02:56:08 -0600 Subject: [PATCH 46/62] refactor: update --- .github/workflows/{docker-build.yml => dokploy.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{docker-build.yml => dokploy.yml} (99%) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/dokploy.yml similarity index 99% rename from .github/workflows/docker-build.yml rename to .github/workflows/dokploy.yml index 31b17c3e8..0f52ae36b 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/dokploy.yml @@ -1,4 +1,4 @@ -name: Docker Build +name: Dokploy Docker Build on: push: From 8aa655af2c6ab12cac08b90cd73e05f2576b4e8e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:00:09 -0600 Subject: [PATCH 47/62] chore: add pr --- .github/workflows/create-pr.yml | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/create-pr.yml diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml new file mode 100644 index 000000000..188ccd6b9 --- /dev/null +++ b/.github/workflows/create-pr.yml @@ -0,0 +1,75 @@ +name: Auto PR to main when version changes + +on: + push: + branches: + - canary + +jobs: + create-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version from package.json + id: package_version + run: echo "VERSION=$(jq -r .version ./apps/dokploy/package.json)" >> $GITHUB_ENV + + - name: Get latest GitHub tag + id: latest_tag + run: | + LATEST_TAG=$(git ls-remote --tags origin | awk -F'/' '{print $3}' | sort -V | tail -n1) + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + echo $LATEST_TAG + - name: Compare versions + id: compare_versions + run: | + if [ "${{ env.VERSION }}" != "${{ env.LATEST_TAG }}" ]; then + VERSION_CHANGED="true" + else + VERSION_CHANGED="false" + fi + echo "VERSION_CHANGED=$VERSION_CHANGED" >> $GITHUB_ENV + echo "Comparing versions:" + echo "Current version: ${{ env.VERSION }}" + echo "Latest tag: ${{ env.LATEST_TAG }}" + echo "Version changed: $VERSION_CHANGED" + - name: Check if a PR already exists + id: check_pr + run: | + PR_EXISTS=$(gh pr list --state open --base main --head canary --json number --jq '. | length') + echo "PR_EXISTS=$PR_EXISTS" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GH_PAT }} + + - name: Create Pull Request + if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GH_PAT }} + title: "🚀 Release v${{ env.VERSION }}" + body: | + ## 🔄 Release v${{ env.VERSION }} + This PR promotes changes from `canary` to `main` for version v${{ env.VERSION }}. + ### 🔍 Changes Include: + - Version bump to v${{ env.VERSION }} + - All changes from canary branch + ### ✅ Pre-merge Checklist: + - [ ] All tests passing + - [ ] Documentation updated + - [ ] Docker images built and tested + > 🤖 This PR was automatically generated from the canary branch + base: main + branch: canary + draft: true + labels: | + release + automated pr + reviewers: | + siumauricio + assignees: | + siumauricio + delete-branch: false From 1e006cb0945c81f8af44d8d6c8f1caafc9d44e75 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:00:54 -0600 Subject: [PATCH 48/62] chore: bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 1aa3fb12a..292c9de6e 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.2", + "version": "v0.17.3", "private": true, "license": "Apache-2.0", "type": "module", From 3ec2e2dd1aaca962130552d86c6e59c516489761 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:06:50 -0600 Subject: [PATCH 49/62] Revert "chore: bump version" This reverts commit 1e006cb0945c81f8af44d8d6c8f1caafc9d44e75. --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 292c9de6e..1aa3fb12a 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.3", + "version": "v0.17.2", "private": true, "license": "Apache-2.0", "type": "module", From f27830daf0ed5dfc56eaeeed878271ffcf5c5b9d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:08:53 -0600 Subject: [PATCH 50/62] refactor: update version --- .github/workflows/create-pr.yml | 2 +- apps/dokploy/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 188ccd6b9..6e15b1a89 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -63,7 +63,7 @@ jobs: - [ ] Docker images built and tested > 🤖 This PR was automatically generated from the canary branch base: main - branch: canary + branch: release/v${{ env.VERSION }} draft: true labels: | release diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 1aa3fb12a..292c9de6e 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.2", + "version": "v0.17.3", "private": true, "license": "Apache-2.0", "type": "module", From 64fc3c7677a539dc412930687a19f604872ba7df Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:14:58 -0600 Subject: [PATCH 51/62] chore: remove pr --- .github/workflows/create-pr.yml | 38 +++++++++++---------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 6e15b1a89..bee087bb7 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -47,29 +47,15 @@ jobs: - name: Create Pull Request if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GH_PAT }} - title: "🚀 Release v${{ env.VERSION }}" - body: | - ## 🔄 Release v${{ env.VERSION }} - This PR promotes changes from `canary` to `main` for version v${{ env.VERSION }}. - ### 🔍 Changes Include: - - Version bump to v${{ env.VERSION }} - - All changes from canary branch - ### ✅ Pre-merge Checklist: - - [ ] All tests passing - - [ ] Documentation updated - - [ ] Docker images built and tested - > 🤖 This PR was automatically generated from the canary branch - base: main - branch: release/v${{ env.VERSION }} - draft: true - labels: | - release - automated pr - reviewers: | - siumauricio - assignees: | - siumauricio - delete-branch: false + run: | + gh pr create \ + --title "🚀 Release v${{ env.VERSION }}" \ + --body "## 🔄 Release v${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated from the canary branch" \ + --base main \ + --head canary \ + --draft \ + --label "release" --label "automated pr" \ + --reviewer siumauricio \ + --assignee siumauricio + env: + GH_TOKEN: ${{ secrets.GH_PAT }} From 94ffa7d5780b08d04c584e7d776188153917ab04 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:15:28 -0600 Subject: [PATCH 52/62] chore: update --- .github/workflows/create-pr.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index bee087bb7..3b8afa183 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -54,7 +54,6 @@ jobs: --base main \ --head canary \ --draft \ - --label "release" --label "automated pr" \ --reviewer siumauricio \ --assignee siumauricio env: From 3d602c232db56200a2c1f2d12962c15396f76a32 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:19:59 -0600 Subject: [PATCH 53/62] refactor: update --- .github/workflows/create-pr.yml | 35 ++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 3b8afa183..fdbc800b0 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -47,14 +47,27 @@ jobs: - name: Create Pull Request if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' - run: | - gh pr create \ - --title "🚀 Release v${{ env.VERSION }}" \ - --body "## 🔄 Release v${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated from the canary branch" \ - --base main \ - --head canary \ - --draft \ - --reviewer siumauricio \ - --assignee siumauricio - env: - GH_TOKEN: ${{ secrets.GH_PAT }} + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GH_PAT }} + title: "🚀 Release ${{ env.VERSION }}" + body: | + ## 🔄 Release ${{ env.VERSION }} + + This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. + + ### 🔍 Changes Include: + - Version bump to ${{ env.VERSION }} + - All changes from canary branch + + ### ✅ Pre-merge Checklist: + - [ ] All tests passing + - [ ] Documentation updated + - [ ] Docker images built and tested + + > 🤖 This PR was automatically generated from the canary branch + base: main + branch: canary + draft: true + assignees: siumauricio + reviewers: siumauricio From 498678c4ae3ac6b5ca567a7d566286f1affd4a2a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:21:30 -0600 Subject: [PATCH 54/62] Revert "refactor: update" This reverts commit 3d602c232db56200a2c1f2d12962c15396f76a32. --- .github/workflows/create-pr.yml | 35 +++++++++++---------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index fdbc800b0..3b8afa183 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -47,27 +47,14 @@ jobs: - name: Create Pull Request if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GH_PAT }} - title: "🚀 Release ${{ env.VERSION }}" - body: | - ## 🔄 Release ${{ env.VERSION }} - - This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. - - ### 🔍 Changes Include: - - Version bump to ${{ env.VERSION }} - - All changes from canary branch - - ### ✅ Pre-merge Checklist: - - [ ] All tests passing - - [ ] Documentation updated - - [ ] Docker images built and tested - - > 🤖 This PR was automatically generated from the canary branch - base: main - branch: canary - draft: true - assignees: siumauricio - reviewers: siumauricio + run: | + gh pr create \ + --title "🚀 Release v${{ env.VERSION }}" \ + --body "## 🔄 Release v${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated from the canary branch" \ + --base main \ + --head canary \ + --draft \ + --reviewer siumauricio \ + --assignee siumauricio + env: + GH_TOKEN: ${{ secrets.GH_PAT }} From 3f8bc47ce568e1f3aeeaffa44b6c78a5a8bb84bd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:35:07 -0600 Subject: [PATCH 55/62] refactor: update --- .github/workflows/create-pr.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 3b8afa183..fcdbdb5fc 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -48,12 +48,20 @@ jobs: - name: Create Pull Request if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + + git fetch origin main + git checkout -b release/${{ env.VERSION }} + git push origin release/${{ env.VERSION }} + gh pr create \ - --title "🚀 Release v${{ env.VERSION }}" \ - --body "## 🔄 Release v${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated from the canary branch" \ + --title "🚀 Release ${{ env.VERSION }}" \ + --body "## 🔄 Release ${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)." \ --base main \ - --head canary \ + --head release/${{ env.VERSION }} \ --draft \ + --label "release" --label "automated pr" \ --reviewer siumauricio \ --assignee siumauricio env: From 95b67ef2e915de50ee47110d0e71f864d5847b2c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:36:47 -0600 Subject: [PATCH 56/62] chore: update --- .github/workflows/create-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index fcdbdb5fc..fbb167f38 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -57,11 +57,11 @@ jobs: gh pr create \ --title "🚀 Release ${{ env.VERSION }}" \ - --body "## 🔄 Release ${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version v${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)." \ + --body "## 🔄 Release ${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version ${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)." \ --base main \ --head release/${{ env.VERSION }} \ --draft \ - --label "release" --label "automated pr" \ + --label "release" --label "automated pr" || true \ --reviewer siumauricio \ --assignee siumauricio env: From 84690c5f755bef09d52deef2721022b10573d8b5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:41:44 -0600 Subject: [PATCH 57/62] chore: update pr --- .github/workflows/create-pr.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index fbb167f38..492e37e6e 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -5,6 +5,10 @@ on: branches: - canary +permissions: + contents: write + pull-requests: write + jobs: create-pr: runs-on: ubuntu-latest @@ -48,8 +52,8 @@ jobs: - name: Create Pull Request if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0' run: | - git config --global user.name "GitHub Actions" - git config --global user.email "actions@github.com" + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git fetch origin main git checkout -b release/${{ env.VERSION }} @@ -65,4 +69,4 @@ jobs: --reviewer siumauricio \ --assignee siumauricio env: - GH_TOKEN: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ github.token }} From 7dca4fe43002efc8ef45a9397dc6b2b97bcf05c0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:43:32 -0600 Subject: [PATCH 58/62] refactor: update --- .github/workflows/create-pr.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 492e37e6e..ddfa171e8 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -61,7 +61,19 @@ jobs: gh pr create \ --title "🚀 Release ${{ env.VERSION }}" \ - --body "## 🔄 Release ${{ env.VERSION }}\n\nThis PR promotes changes from \`canary\` to \`main\` for version ${{ env.VERSION }}.\n\n### 🔍 Changes Include:\n- Version bump to v${{ env.VERSION }}\n- All changes from canary branch\n\n### ✅ Pre-merge Checklist:\n- [ ] All tests passing\n- [ ] Documentation updated\n- [ ] Docker images built and tested\n\n> 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)." \ + --body: | + This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. + + ### 🔍 Changes Include: + - Version bump to ${{ env.VERSION }} + - All changes from canary branch + + ### ✅ Pre-merge Checklist: + - [ ] All tests passing + - [ ] Documentation updated + - [ ] Docker images built and tested + + > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions) \ --base main \ --head release/${{ env.VERSION }} \ --draft \ From 1956836cde43fdd9a3e95c2843456ae7dfe50bda Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:45:12 -0600 Subject: [PATCH 59/62] refactor: update --- .github/workflows/create-pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index ddfa171e8..10b107793 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -56,8 +56,8 @@ jobs: git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git fetch origin main - git checkout -b release/${{ env.VERSION }} - git push origin release/${{ env.VERSION }} + git canary + git push origin canary gh pr create \ --title "🚀 Release ${{ env.VERSION }}" \ @@ -75,7 +75,7 @@ jobs: > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions) \ --base main \ - --head release/${{ env.VERSION }} \ + --head canary \ --draft \ --label "release" --label "automated pr" || true \ --reviewer siumauricio \ From 60748da14401b6c311b0a02fa72848cc22d56bbe Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:45:51 -0600 Subject: [PATCH 60/62] chore: update --- .github/workflows/create-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 10b107793..5072cbf54 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -56,7 +56,7 @@ jobs: git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git fetch origin main - git canary + git checkout canary git push origin canary gh pr create \ From 6d052ad455c8636eff2de1d47a8ccd25c6fd63e8 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:47:04 -0600 Subject: [PATCH 61/62] chore: add quotes --- .github/workflows/create-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index 5072cbf54..dfb6a2e92 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -61,7 +61,7 @@ jobs: gh pr create \ --title "🚀 Release ${{ env.VERSION }}" \ - --body: | + --body ' This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. ### 🔍 Changes Include: @@ -73,7 +73,7 @@ jobs: - [ ] Documentation updated - [ ] Docker images built and tested - > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions) \ + > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)' \ --base main \ --head canary \ --draft \ From 4f11fc254718090ec95cfc0fec61e3e7fe77ec35 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 19 Jan 2025 10:49:08 -0600 Subject: [PATCH 62/62] chore: remove spaces --- .github/workflows/create-pr.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/create-pr.yml b/.github/workflows/create-pr.yml index dfb6a2e92..994231bb5 100644 --- a/.github/workflows/create-pr.yml +++ b/.github/workflows/create-pr.yml @@ -62,18 +62,18 @@ jobs: gh pr create \ --title "🚀 Release ${{ env.VERSION }}" \ --body ' - This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. + This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}. - ### 🔍 Changes Include: - - Version bump to ${{ env.VERSION }} - - All changes from canary branch + ### 🔍 Changes Include: + - Version bump to ${{ env.VERSION }} + - All changes from canary branch - ### ✅ Pre-merge Checklist: - - [ ] All tests passing - - [ ] Documentation updated - - [ ] Docker images built and tested + ### ✅ Pre-merge Checklist: + - [ ] All tests passing + - [ ] Documentation updated + - [ ] Docker images built and tested - > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)' \ + > 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)' \ --base main \ --head canary \ --draft \