From 57594ecb0c1d35bc9a39f4e7ffa9f3209b32f86c Mon Sep 17 00:00:00 2001 From: Orkhan Karimov <136202877+kerimovok@users.noreply.github.com> Date: Sat, 30 Nov 2024 18:01:13 +0400 Subject: [PATCH 01/52] feat: Updated PocketBase version to 0.23.3 --- apps/dokploy/templates/pocketbase/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/pocketbase/docker-compose.yml b/apps/dokploy/templates/pocketbase/docker-compose.yml index fa6674afe..cf5e94f1b 100644 --- a/apps/dokploy/templates/pocketbase/docker-compose.yml +++ b/apps/dokploy/templates/pocketbase/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: pocketbase: - image: spectado/pocketbase:0.22.12 + image: spectado/pocketbase:0.23.3 restart: unless-stopped volumes: - /etc/dokploy/templates/${HASH}/data:/pb_data From a235815a13c494b07ca63b9f4571340a98f0d684 Mon Sep 17 00:00:00 2001 From: 190km Date: Sun, 1 Dec 2024 04:09:19 +0100 Subject: [PATCH 02/52] style: improved discord webhooks notifications --- .../server/api/routers/notification.ts | 5 +-- .../src/utils/notifications/build-error.ts | 22 ++++++------ .../src/utils/notifications/build-success.ts | 18 +++++----- .../utils/notifications/database-backup.ts | 35 ++++++++++--------- .../src/utils/notifications/docker-cleanup.ts | 8 ++--- .../utils/notifications/dokploy-restart.ts | 8 ++--- 6 files changed, 49 insertions(+), 47 deletions(-) diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 170b7bf20..504e33209 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -188,8 +188,9 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input }) => { try { await sendDiscordNotification(input, { - title: "Test Notification", - description: "Hi, From Dokploy 👋", + title: "> `🤚` - Test Notification", + description: "> Hi, From Dokploy 👋", + color: 0xf3f7f4 }); return true; } catch (error) { diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 9fcb06412..a8a7bf257 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -59,30 +59,30 @@ export const sendBuildErrorNotifications = async ({ if (discord) { await sendDiscordNotification(discord, { - title: "⚠️ Build Failed", - color: 0xff0000, + title: "> `⚠️` - Build Failed", + color: 0xed4245, fields: [ { - name: "Project", - value: projectName, + name: "`🛠️`・Project", + value: `\`\`\`${projectName}\`\`\``, inline: true, }, { - name: "Application", - value: applicationName, + name: "`⚙️`・Application", + value: `\`\`\`${applicationName}\`\`\``, inline: true, }, { - name: "Type", - value: applicationType, + name: "`❔`・Type", + value: `\`\`\`${applicationType}\`\`\``, inline: true, }, { - name: "Error", - value: errorMessage, + name: "`⚠️`・Error Message", + value: `\`\`\`${errorMessage}\`\`\``, }, { - name: "Build Link", + name: "`🧷`・Build Link", value: buildLink, }, ], diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 9210eca2a..4475bd44f 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -57,26 +57,26 @@ export const sendBuildSuccessNotifications = async ({ if (discord) { await sendDiscordNotification(discord, { - title: "✅ Build Success", - color: 0x00ff00, + title: "> `✅` - Build Success", + color: 0x57f287, fields: [ { - name: "Project", - value: projectName, + name: "`🛠️`・Project", + value: `\`\`\`${projectName}\`\`\``, inline: true, }, { - name: "Application", - value: applicationName, + name: "`⚙️`・Application", + value: `\`\`\`${applicationName}\`\`\``, inline: true, }, { - name: "Type", - value: applicationType, + name: "`❔`・Type", + value: `\`\`\`${applicationType}\`\`\``, inline: true, }, { - name: "Build Link", + name: "`🧷`・Build Link", value: buildLink, }, ], diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index c97afa031..63a842ab4 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -64,39 +64,40 @@ export const sendDatabaseBackupNotifications = async ({ await sendDiscordNotification(discord, { title: type === "success" - ? "✅ Database Backup Successful" - : "❌ Database Backup Failed", - color: type === "success" ? 0x00ff00 : 0xff0000, + ? "> `✅` - Database Backup Successful" + : "> `❌` - Database Backup Failed", + color: type === "success" ? 0x57f287 : 0xed4245, fields: [ { - name: "Project", - value: projectName, - inline: true, + name: "`🛠️`・Project", + value: `\`\`\`${projectName}\`\`\``, + inline: false, }, { - name: "Application", - value: applicationName, + name: "`⚙️`・Application", + value: `\`\`\`${applicationName}\`\`\``, inline: true, }, { - name: "Type", - value: databaseType, + name: "`❔`・Type", + value: `\`\`\`${databaseType}\`\`\``, inline: true, }, { - name: "Time", - value: date.toLocaleString(), - inline: true, + name: "`📅`・Time", + value: `\`\`\`${date.toLocaleString()}\`\`\``, + inline: false, }, { - name: "Type", - value: type, + name: "`❔`・Type", + value: `\`\`\`${type}\`\`\``, + inline: false, }, ...(type === "error" && errorMessage ? [ { - name: "Error Message", - value: errorMessage, + name: "`⚠️`・Error Message", + value: `\`\`\`${errorMessage}\`\`\``, }, ] : []), diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index a2e091393..68c3f119e 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -45,12 +45,12 @@ export const sendDockerCleanupNotifications = async ( if (discord) { await sendDiscordNotification(discord, { - title: "✅ Docker Cleanup", - color: 0x00ff00, + title: "> `✅` - Docker Cleanup", + color: 0x57f287, fields: [ { - name: "Message", - value: message, + name: "`📜`・Message", + value: `\`\`\`${message}\`\`\``, }, ], timestamp: date.toISOString(), diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 33bc32f04..451044f4c 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -34,12 +34,12 @@ export const sendDokployRestartNotifications = async () => { if (discord) { await sendDiscordNotification(discord, { - title: "✅ Dokploy Server Restarted", - color: 0x00ff00, + title: "> `✅` - Dokploy Server Restarted", + color: 0x57f287, fields: [ { - name: "Time", - value: date.toLocaleString(), + name: "`📅`・Time", + value: `\`\`\`${date.toLocaleString()}\`\`\``, inline: true, }, ], From bcf1ba242e039a645ee05ade093f8eaf35c3304b Mon Sep 17 00:00:00 2001 From: Jongho Hong Date: Sun, 1 Dec 2024 16:48:16 +0900 Subject: [PATCH 03/52] feat(i18n): add korean language support --- .../dashboard/settings/appearance-form.tsx | 8 ++-- apps/dokploy/next-i18next.config.cjs | 13 +++++- apps/dokploy/pages/_app.tsx | 1 + apps/dokploy/public/locales/ko/common.json | 1 + apps/dokploy/public/locales/ko/settings.json | 44 +++++++++++++++++++ apps/dokploy/utils/hooks/use-locale.ts | 1 + 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 apps/dokploy/public/locales/ko/common.json create mode 100644 apps/dokploy/public/locales/ko/settings.json diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index f43119232..2a3f5132b 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -38,7 +38,7 @@ const appearanceFormSchema = z.object({ required_error: "Please select a theme.", }), language: z.enum( - ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa"], + ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa", "ko"], { required_error: "Please select a language.", }, @@ -184,10 +184,8 @@ export function AppearanceForm() { { label: "繁體中文", value: "zh-Hant" }, { label: "简体中文", value: "zh-Hans" }, { label: "Türkçe", value: "tr" }, - { - label: "Persian", - value: "fa", - }, + { label: "Persian", value: "fa" }, + { label: "한국어", value: "ko" }, ].map((preset) => ( {preset.label} diff --git a/apps/dokploy/next-i18next.config.cjs b/apps/dokploy/next-i18next.config.cjs index 5457859f3..adf741e91 100644 --- a/apps/dokploy/next-i18next.config.cjs +++ b/apps/dokploy/next-i18next.config.cjs @@ -2,7 +2,18 @@ module.exports = { i18n: { defaultLocale: "en", - locales: ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa"], + locales: [ + "en", + "pl", + "ru", + "fr", + "de", + "tr", + "zh-Hant", + "zh-Hans", + "fa", + "ko", + ], localeDetection: false, }, fallbackLng: "en", diff --git a/apps/dokploy/pages/_app.tsx b/apps/dokploy/pages/_app.tsx index 9b21243ab..17b986c57 100644 --- a/apps/dokploy/pages/_app.tsx +++ b/apps/dokploy/pages/_app.tsx @@ -81,6 +81,7 @@ export default api.withTRPC( "zh-Hant", "zh-Hans", "fa", + "ko", ], localeDetection: false, }, diff --git a/apps/dokploy/public/locales/ko/common.json b/apps/dokploy/public/locales/ko/common.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/apps/dokploy/public/locales/ko/common.json @@ -0,0 +1 @@ +{} diff --git a/apps/dokploy/public/locales/ko/settings.json b/apps/dokploy/public/locales/ko/settings.json new file mode 100644 index 000000000..f0a99e4e7 --- /dev/null +++ b/apps/dokploy/public/locales/ko/settings.json @@ -0,0 +1,44 @@ +{ + "settings.common.save": "저장", + "settings.server.domain.title": "서버 도메인", + "settings.server.domain.description": "서버 애플리케이션에 도메인을 추가합니다.", + "settings.server.domain.form.domain": "도메인", + "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 이메일", + "settings.server.domain.form.certificate.label": "인증서", + "settings.server.domain.form.certificate.placeholder": "인증서 선택", + "settings.server.domain.form.certificateOptions.none": "없음", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (기본)", + + "settings.server.webServer.title": "웹 서버", + "settings.server.webServer.description": "웹 서버를 재시작하거나 정리합니다.", + "settings.server.webServer.actions": "작업", + "settings.server.webServer.reload": "재시작", + "settings.server.webServer.watchLogs": "로그 보기", + "settings.server.webServer.updateServerIp": "서버 IP 갱신", + "settings.server.webServer.server.label": "서버", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "환경 변수 수정", + "settings.server.webServer.storage.label": "저장 공간", + "settings.server.webServer.storage.cleanUnusedImages": "사용하지 않는 이미지 정리", + "settings.server.webServer.storage.cleanUnusedVolumes": "사용하지 않는 볼륨 정리", + "settings.server.webServer.storage.cleanStoppedContainers": "정지된 컨테이너 정리", + "settings.server.webServer.storage.cleanDockerBuilder": "도커 빌더 & 시스템 정리", + "settings.server.webServer.storage.cleanMonitoring": "모니터링 데이터 정리", + "settings.server.webServer.storage.cleanAll": "전체 정리", + + "settings.profile.title": "계정", + "settings.profile.description": "여기에서 프로필 세부 정보를 변경하세요.", + "settings.profile.email": "이메일", + "settings.profile.password": "비밀번호", + "settings.profile.avatar": "아바타", + + "settings.appearance.title": "외관", + "settings.appearance.description": "대시보드의 테마를 사용자 설정합니다.", + "settings.appearance.theme": "테마", + "settings.appearance.themeDescription": "대시보드 테마 선택", + "settings.appearance.themes.light": "라이트", + "settings.appearance.themes.dark": "다크", + "settings.appearance.themes.system": "시스템", + "settings.appearance.language": "언어", + "settings.appearance.languageDescription": "대시보드에서 사용할 언어 선택" +} diff --git a/apps/dokploy/utils/hooks/use-locale.ts b/apps/dokploy/utils/hooks/use-locale.ts index 1cf3104b1..eaeb1612f 100644 --- a/apps/dokploy/utils/hooks/use-locale.ts +++ b/apps/dokploy/utils/hooks/use-locale.ts @@ -10,6 +10,7 @@ const SUPPORTED_LOCALES = [ "zh-Hant", "zh-Hans", "fa", + "ko", ] as const; type Locale = (typeof SUPPORTED_LOCALES)[number]; From 9a8a40b0f84d329b30ffacc470112197275f5871 Mon Sep 17 00:00:00 2001 From: 190km Date: Sun, 1 Dec 2024 16:37:45 +0100 Subject: [PATCH 04/52] style: removed useless codeblock fields --- .../src/utils/notifications/build-error.ts | 23 +++++++++++++--- .../src/utils/notifications/build-success.ts | 25 +++++++++++++---- .../utils/notifications/database-backup.ts | 27 +++++++++++-------- .../src/utils/notifications/docker-cleanup.ts | 16 +++++++++++ .../utils/notifications/dokploy-restart.ts | 14 ++++++++-- 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index a8a7bf257..3c4fd73d5 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -64,17 +64,32 @@ export const sendBuildErrorNotifications = async ({ fields: [ { name: "`🛠️`・Project", - value: `\`\`\`${projectName}\`\`\``, + value: projectName, inline: true, }, { name: "`⚙️`・Application", - value: `\`\`\`${applicationName}\`\`\``, + value: applicationName, inline: true, }, { name: "`❔`・Type", - value: `\`\`\`${applicationType}\`\`\``, + value: applicationType, + inline: true, + }, + { + name: "`📅`・Date", + value: date.toLocaleDateString(), + inline: true, + }, + { + name: "`⌚`・Time", + value: date.toLocaleTimeString(), + inline: true, + }, + { + name: "`❓`・Type", + value: "Failed", inline: true, }, { @@ -83,7 +98,7 @@ export const sendBuildErrorNotifications = async ({ }, { name: "`🧷`・Build Link", - value: buildLink, + value: `[Click here to access build link](${buildLink})`, }, ], timestamp: date.toISOString(), diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 4475bd44f..0e09e87d1 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -62,22 +62,37 @@ export const sendBuildSuccessNotifications = async ({ fields: [ { name: "`🛠️`・Project", - value: `\`\`\`${projectName}\`\`\``, + value: projectName, inline: true, }, { name: "`⚙️`・Application", - value: `\`\`\`${applicationName}\`\`\``, + value: applicationName, inline: true, }, { - name: "`❔`・Type", - value: `\`\`\`${applicationType}\`\`\``, + name: "`❔`・Application Type", + value: applicationType, + inline: true, + }, + { + name: "`📅`・Date", + value: date.toLocaleDateString(), + inline: true, + }, + { + name: "`⌚`・Time", + value: date.toLocaleTimeString(), + inline: true, + }, + { + name: "`❓`・Type", + value: "Successful", inline: true, }, { name: "`🧷`・Build Link", - value: buildLink, + value: `[Click here to access build link](${buildLink})`, }, ], timestamp: date.toISOString(), diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 63a842ab4..115d8b206 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -70,28 +70,33 @@ export const sendDatabaseBackupNotifications = async ({ fields: [ { name: "`🛠️`・Project", - value: `\`\`\`${projectName}\`\`\``, - inline: false, + value: projectName, + inline: true, }, { name: "`⚙️`・Application", - value: `\`\`\`${applicationName}\`\`\``, + value: applicationName, + inline: true, + }, + { + name: "`❔`・Database", + value: databaseType, inline: true, }, { - name: "`❔`・Type", - value: `\`\`\`${databaseType}\`\`\``, + name: "`📅`・Date", + value: date.toLocaleDateString(), inline: true, }, { - name: "`📅`・Time", - value: `\`\`\`${date.toLocaleString()}\`\`\``, - inline: false, + name: "`⌚`・Time", + value: date.toLocaleTimeString(), + inline: true, }, { - name: "`❔`・Type", - value: `\`\`\`${type}\`\`\``, - inline: false, + name: "`❓`・Type", + value: type.replace("error", "Failed").replace("success", "Successful"), + inline: true, }, ...(type === "error" && errorMessage ? [ diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index 68c3f119e..31c1c6c9d 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -48,6 +48,22 @@ export const sendDockerCleanupNotifications = async ( title: "> `✅` - Docker Cleanup", color: 0x57f287, fields: [ + + { + name: "`📅`・Date", + value: date.toLocaleDateString(), + inline: true, + }, + { + name: "`⌚`・Time", + value: date.toLocaleTimeString(), + inline: true, + }, + { + name: "`❓`・Type", + value: "Successful", + inline: true, + }, { name: "`📜`・Message", value: `\`\`\`${message}\`\`\``, diff --git a/packages/server/src/utils/notifications/dokploy-restart.ts b/packages/server/src/utils/notifications/dokploy-restart.ts index 451044f4c..c552e2695 100644 --- a/packages/server/src/utils/notifications/dokploy-restart.ts +++ b/packages/server/src/utils/notifications/dokploy-restart.ts @@ -38,8 +38,18 @@ export const sendDokployRestartNotifications = async () => { color: 0x57f287, fields: [ { - name: "`📅`・Time", - value: `\`\`\`${date.toLocaleString()}\`\`\``, + name: "`📅`・Date", + value: date.toLocaleDateString(), + inline: true, + }, + { + name: "`⌚`・Time", + value: date.toLocaleTimeString(), + inline: true, + }, + { + name: "`❓`・Type", + value: "Successful", inline: true, }, ], From 2daa159e29984b73abbdc5b0f75427ed1de77e17 Mon Sep 17 00:00:00 2001 From: 190km Date: Sun, 1 Dec 2024 19:29:16 +0100 Subject: [PATCH 05/52] style: add RequestAddr in the requests table --- .../components/dashboard/requests/columns.tsx | 167 +++++++++--------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/apps/dokploy/components/dashboard/requests/columns.tsx b/apps/dokploy/components/dashboard/requests/columns.tsx index ebcfb4531..0bd4507b0 100644 --- a/apps/dokploy/components/dashboard/requests/columns.tsx +++ b/apps/dokploy/components/dashboard/requests/columns.tsx @@ -7,89 +7,92 @@ import * as React from "react"; import type { LogEntry } from "./show-requests"; export const getStatusColor = (status: number) => { - if (status >= 100 && status < 200) { - return "outline"; - } - if (status >= 200 && status < 300) { - return "default"; - } - if (status >= 300 && status < 400) { - return "outline"; - } - if (status >= 400 && status < 500) { - return "destructive"; - } - return "destructive"; + if (status >= 100 && status < 200) { + return "outline"; + } + if (status >= 200 && status < 300) { + return "default"; + } + if (status >= 300 && status < 400) { + return "outline"; + } + if (status >= 400 && status < 500) { + return "destructive"; + } + return "destructive"; }; export const columns: ColumnDef[] = [ - { - accessorKey: "level", - header: ({ column }) => { - return ; - }, - cell: ({ row }) => { - return
{row.original.level}
; - }, - }, - { - accessorKey: "RequestPath", - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const log = row.original; - return ( -
-
- {log.RequestMethod}{" "} - {log.RequestPath.length > 100 - ? `${log.RequestPath.slice(0, 82)}...` - : log.RequestPath} -
-
- - Status: {log.OriginStatus} - - - Exec Time: {`${log.Duration / 1000000000}s`} - - IP: {log.ClientAddr} -
-
- ); - }, - }, - { - accessorKey: "time", - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const log = row.original; - return ( -
-
- {format(new Date(log.StartUTC), "yyyy-MM-dd HH:mm:ss")} -
-
- ); - }, - }, + { + accessorKey: "level", + header: ({ column }) => { + return ; + }, + cell: ({ row }) => { + return
{row.original.level}
; + }, + }, + { + accessorKey: "RequestPath", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const log = row.original; + return ( +
+
+ {log.RequestMethod}{" "} +
+ {log.RequestAddr} +
+ {log.RequestPath.length > 100 + ? `${log.RequestPath.slice(0, 82)}...` + : log.RequestPath} +
+
+ + Status: {log.OriginStatus} + + + Exec Time: {`${log.Duration / 1000000000}s`} + + IP: {log.ClientAddr} +
+
+ ); + }, + }, + { + accessorKey: "time", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const log = row.original; + return ( +
+
+ {format(new Date(log.StartUTC), "yyyy-MM-dd HH:mm:ss")} +
+
+ ); + }, + }, ]; From 5f6516ab7d21a3656378e57243db53471d3821ce Mon Sep 17 00:00:00 2001 From: usopp Date: Sun, 1 Dec 2024 19:42:56 +0100 Subject: [PATCH 06/52] Update columns.tsx --- .../components/dashboard/requests/columns.tsx | 170 +++++++++--------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/apps/dokploy/components/dashboard/requests/columns.tsx b/apps/dokploy/components/dashboard/requests/columns.tsx index 0bd4507b0..258237523 100644 --- a/apps/dokploy/components/dashboard/requests/columns.tsx +++ b/apps/dokploy/components/dashboard/requests/columns.tsx @@ -7,92 +7,92 @@ import * as React from "react"; import type { LogEntry } from "./show-requests"; export const getStatusColor = (status: number) => { - if (status >= 100 && status < 200) { - return "outline"; - } - if (status >= 200 && status < 300) { - return "default"; - } - if (status >= 300 && status < 400) { - return "outline"; - } - if (status >= 400 && status < 500) { - return "destructive"; - } - return "destructive"; + if (status >= 100 && status < 200) { + return "outline"; + } + if (status >= 200 && status < 300) { + return "default"; + } + if (status >= 300 && status < 400) { + return "outline"; + } + if (status >= 400 && status < 500) { + return "destructive"; + } + return "destructive"; }; export const columns: ColumnDef[] = [ - { - accessorKey: "level", - header: ({ column }) => { - return ; - }, - cell: ({ row }) => { - return
{row.original.level}
; - }, - }, - { - accessorKey: "RequestPath", - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const log = row.original; - return ( -
-
- {log.RequestMethod}{" "} -
- {log.RequestAddr} -
- {log.RequestPath.length > 100 - ? `${log.RequestPath.slice(0, 82)}...` - : log.RequestPath} -
-
- - Status: {log.OriginStatus} - - - Exec Time: {`${log.Duration / 1000000000}s`} - - IP: {log.ClientAddr} -
-
- ); - }, - }, - { - accessorKey: "time", - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const log = row.original; - return ( -
-
- {format(new Date(log.StartUTC), "yyyy-MM-dd HH:mm:ss")} -
-
- ); - }, - }, + { + accessorKey: "level", + header: ({ column }) => { + return ; + }, + cell: ({ row }) => { + return
{row.original.level}
; + }, + }, + { + accessorKey: "RequestPath", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const log = row.original; + return ( +
+
+ {log.RequestMethod}{" "} +
+ {log.RequestAddr} +
+ {log.RequestPath.length > 100 + ? `${log.RequestPath.slice(0, 82)}...` + : log.RequestPath} +
+
+ + Status: {log.OriginStatus} + + + Exec Time: {`${log.Duration / 1000000000}s`} + + IP: {log.ClientAddr} +
+
+ ); + }, + }, + { + accessorKey: "time", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const log = row.original; + return ( +
+
+ {format(new Date(log.StartUTC), "yyyy-MM-dd HH:mm:ss")} +
+
+ ); + }, + }, ]; From 841b264257930ca78ac5f996b9d1d33b533cf0d1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:29:40 -0600 Subject: [PATCH 07/52] feat: add preview deployments #379 --- .circleci/config.yml | 6 +- LICENSE.MD | 8 +- apps/dokploy/LICENSE.MD | 8 +- .../__test__/compose/domain/labels.test.ts | 2 + apps/dokploy/__test__/drop/drop.test.test.ts | 9 + apps/dokploy/__test__/traefik/traefik.test.ts | 11 + .../deployments/show-deployments.tsx | 1 + .../application/environment/show.tsx | 2 +- .../add-preview-domain.tsx | 304 ++ .../show-preview-builds.tsx | 87 + .../show-preview-deployments.tsx | 212 + .../show-preview-settings.tsx | 351 ++ apps/dokploy/components/ui/secrets.tsx | 4 +- .../drizzle/0047_red_stephen_strange.sql | 60 + .../drizzle/0048_clumsy_matthew_murdock.sql | 1 + apps/dokploy/drizzle/0049_useful_mole_man.sql | 3 + apps/dokploy/drizzle/meta/0047_snapshot.json | 4237 ++++++++++++++++ apps/dokploy/drizzle/meta/0048_snapshot.json | 4245 +++++++++++++++++ apps/dokploy/drizzle/meta/0049_snapshot.json | 4226 ++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 21 + apps/dokploy/package.json | 3 +- apps/dokploy/pages/api/deploy/github.ts | 382 +- .../services/application/[applicationId].tsx | 16 +- apps/dokploy/server/api/root.ts | 2 + apps/dokploy/server/api/routers/domain.ts | 22 + .../server/api/routers/preview-deployment.ts | 54 + .../server/queues/deployments-queue.ts | 29 + apps/dokploy/server/queues/queue-types.ts | 10 + apps/dokploy/server/server.ts | 2 +- package.json | 1 + packages/server/src/db/schema/application.ts | 26 +- packages/server/src/db/schema/deployment.ts | 34 +- packages/server/src/db/schema/domain.ts | 18 +- packages/server/src/db/schema/index.ts | 1 + .../src/db/schema/preview-deployments.ts | 74 + packages/server/src/index.ts | 1 + packages/server/src/services/application.ts | 202 +- packages/server/src/services/compose.ts | 17 +- packages/server/src/services/deployment.ts | 135 +- packages/server/src/services/docker.ts | 2 +- packages/server/src/services/domain.ts | 4 + packages/server/src/services/github.ts | 54 + .../server/src/services/preview-deployment.ts | 285 ++ packages/server/src/utils/builders/index.ts | 2 + .../server/src/utils/builders/nixpacks.ts | 2 +- packages/server/src/utils/providers/github.ts | 33 +- 46 files changed, 15029 insertions(+), 180 deletions(-) create mode 100644 apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx create mode 100644 apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx create mode 100644 apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx create mode 100644 apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx create mode 100644 apps/dokploy/drizzle/0047_red_stephen_strange.sql create mode 100644 apps/dokploy/drizzle/0048_clumsy_matthew_murdock.sql create mode 100644 apps/dokploy/drizzle/0049_useful_mole_man.sql create mode 100644 apps/dokploy/drizzle/meta/0047_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0048_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0049_snapshot.json create mode 100644 apps/dokploy/server/api/routers/preview-deployment.ts create mode 100644 packages/server/src/db/schema/preview-deployments.ts create mode 100644 packages/server/src/services/preview-deployment.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index c5e57a973..dd91309d3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -99,14 +99,14 @@ workflows: only: - main - canary - - fix/build-i18n + - 379-preview-deployment - build-arm64: filters: branches: only: - main - canary - - fix/build-i18n + - 379-preview-deployment - combine-manifests: requires: - build-amd64 @@ -116,4 +116,4 @@ workflows: only: - main - canary - - fix/build-i18n + - 379-preview-deployment diff --git a/LICENSE.MD b/LICENSE.MD index 59e9d822e..8a508efb4 100644 --- a/LICENSE.MD +++ b/LICENSE.MD @@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations ## Additional Terms for Specific Features -The following additional terms apply to the multi-node support, Docker Compose file and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License: +The following additional terms apply to the multi-node support, Docker Compose file, Preview Deployments and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License: -- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support and Multi Server, will always be free to use in the self-hosted version. -- **Restriction on Resale**: The multi-node support, Docker Compose file support and Multi Server features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent. -- **Modification Distribution**: Any modifications to the multi-node support, Docker Compose file support and Multi Server features must be distributed freely and cannot be sold or offered as a service. +- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support, Preview Deployments and Multi Server, will always be free to use in the self-hosted version. +- **Restriction on Resale**: The multi-node support, Docker Compose file support, Preview Deployments and Multi Server features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent. +- **Modification Distribution**: Any modifications to the multi-node support, Docker Compose file support, Preview Deployments and Multi Server features must be distributed freely and cannot be sold or offered as a service. For further inquiries or permissions, please contact us directly. diff --git a/apps/dokploy/LICENSE.MD b/apps/dokploy/LICENSE.MD index 59e9d822e..8a508efb4 100644 --- a/apps/dokploy/LICENSE.MD +++ b/apps/dokploy/LICENSE.MD @@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations ## Additional Terms for Specific Features -The following additional terms apply to the multi-node support, Docker Compose file and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License: +The following additional terms apply to the multi-node support, Docker Compose file, Preview Deployments and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License: -- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support and Multi Server, will always be free to use in the self-hosted version. -- **Restriction on Resale**: The multi-node support, Docker Compose file support and Multi Server features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent. -- **Modification Distribution**: Any modifications to the multi-node support, Docker Compose file support and Multi Server features must be distributed freely and cannot be sold or offered as a service. +- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support, Preview Deployments and Multi Server, will always be free to use in the self-hosted version. +- **Restriction on Resale**: The multi-node support, Docker Compose file support, Preview Deployments and Multi Server features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent. +- **Modification Distribution**: Any modifications to the multi-node support, Docker Compose file support, Preview Deployments and Multi Server features must be distributed freely and cannot be sold or offered as a service. For further inquiries or permissions, please contact us directly. diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 8b5c2f9c4..8a2409651 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -17,6 +17,8 @@ describe("createDomainLabels", () => { domainId: "", path: "/", createdAt: "", + isPreviewDeployment: false, + previewDeploymentId: "", }; it("should create basic labels for web entrypoint", async () => { diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index 53ab02f2b..1fff60fb3 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -33,6 +33,15 @@ const baseApp: ApplicationNested = { registryUrl: "", branch: null, dockerBuildStage: "", + isPreviewDeploymentsActive: false, + previewBuildArgs: null, + previewCertificateType: "none", + previewEnv: null, + previewHttps: false, + previewPath: "/", + previewPort: 3000, + previewLimit: 0, + previewWildcard: "", project: { env: "", adminId: "", diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 7e11160bd..54f236e28 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -14,6 +14,15 @@ const baseApp: ApplicationNested = { dockerBuildStage: "", registryUrl: "", buildArgs: null, + isPreviewDeploymentsActive: false, + previewBuildArgs: null, + previewCertificateType: "none", + previewEnv: null, + previewHttps: false, + previewPath: "/", + previewPort: 3000, + previewLimit: 0, + previewWildcard: "", project: { env: "", adminId: "", @@ -95,6 +104,8 @@ const baseDomain: Domain = { composeId: "", domainType: "application", uniqueConfigKey: 1, + isPreviewDeployment: false, + previewDeploymentId: "", }; const baseRedirect: Redirect = { diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index c2288bb8a..a767350f9 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -28,6 +28,7 @@ export const ShowDeployments = ({ applicationId }: Props) => { refetchInterval: 1000, }, ); + const [url, setUrl] = React.useState(""); useEffect(() => { setUrl(document.location.origin); diff --git a/apps/dokploy/components/dashboard/application/environment/show.tsx b/apps/dokploy/components/dashboard/application/environment/show.tsx index 9b38f1ced..c7b8d4bbd 100644 --- a/apps/dokploy/components/dashboard/application/environment/show.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show.tsx @@ -61,7 +61,7 @@ export const ShowEnvironment = ({ applicationId }: Props) => { onSubmit={form.handleSubmit(onSubmit)} className="flex w-full flex-col gap-5 " > - + ; + +interface Props { + previewDeploymentId: string; + domainId?: string; + children: React.ReactNode; +} + +export const AddPreviewDomain = ({ + previewDeploymentId, + domainId = "", + children, +}: Props) => { + const [isOpen, setIsOpen] = useState(false); + const utils = api.useUtils(); + const { data, refetch } = api.domain.one.useQuery( + { + domainId, + }, + { + enabled: !!domainId, + }, + ); + + const { data: previewDeployment } = api.previewDeployment.one.useQuery( + { + previewDeploymentId, + }, + { + enabled: !!previewDeploymentId, + }, + ); + + const { mutateAsync, isError, error, isLoading } = domainId + ? api.domain.update.useMutation() + : api.domain.create.useMutation(); + + const { mutateAsync: generateDomain, isLoading: isLoadingGenerate } = + api.domain.generateDomain.useMutation(); + + const form = useForm({ + resolver: zodResolver(domain), + }); + + useEffect(() => { + if (data) { + form.reset({ + ...data, + /* Convert null to undefined */ + path: data?.path || undefined, + port: data?.port || undefined, + }); + } + + if (!domainId) { + form.reset({}); + } + }, [form, form.reset, data, isLoading]); + + const dictionary = { + success: domainId ? "Domain Updated" : "Domain Created", + error: domainId + ? "Error to update the domain" + : "Error to create the domain", + submit: domainId ? "Update" : "Create", + dialogDescription: domainId + ? "In this section you can edit a domain" + : "In this section you can add domains", + }; + + const onSubmit = async (data: Domain) => { + await mutateAsync({ + domainId, + previewDeploymentId, + ...data, + }) + .then(async () => { + toast.success(dictionary.success); + await utils.previewDeployment.all.invalidate({ + applicationId: previewDeployment?.applicationId, + }); + + if (domainId) { + refetch(); + } + setIsOpen(false); + }) + .catch(() => { + toast.error(dictionary.error); + }); + }; + return ( + + + {children} + + + + Domain + {dictionary.dialogDescription} + + {isError && {error?.message}} + +
+ +
+
+ ( + + Host +
+ + + + + + + + + +

Generate traefik.me domain

+
+
+
+
+ + +
+ )} + /> + + { + return ( + + Path + + + + + + ); + }} + /> + + { + return ( + + Container Port + + + + + + ); + }} + /> + + ( + +
+ HTTPS + + Automatically provision SSL Certificate. + + +
+ + + +
+ )} + /> + + {form.getValues().https && ( + ( + + Certificate + + + + )} + /> + )} +
+
+
+ + + + + +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx new file mode 100644 index 000000000..4eb2107f6 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx @@ -0,0 +1,87 @@ +import { DateTooltip } from "@/components/shared/date-tooltip"; +import { StatusTooltip } from "@/components/shared/status-tooltip"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import type { RouterOutputs } from "@/utils/api"; +import { useState } from "react"; +import { ShowDeployment } from "../deployments/show-deployment"; + +interface Props { + deployments: RouterOutputs["deployment"]["all"]; + serverId?: string; +} + +export const ShowPreviewBuilds = ({ deployments, serverId }: Props) => { + const [activeLog, setActiveLog] = useState(null); + const [isOpen, setIsOpen] = useState(false); + return ( + + + + + + + Preview Builds + + See all the preview builds for this application on this Pull Request + + +
+ {deployments?.map((deployment) => ( +
+
+ + {deployment.status} + + + + + {deployment.title} + + {deployment.description && ( + + {deployment.description} + + )} +
+
+
+ +
+ + +
+
+ ))} +
+
+ setActiveLog(null)} + logPath={activeLog} + /> +
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx new file mode 100644 index 000000000..45451e78f --- /dev/null +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx @@ -0,0 +1,212 @@ +import { DateTooltip } from "@/components/shared/date-tooltip"; +import { StatusTooltip } from "@/components/shared/status-tooltip"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Switch } from "@/components/ui/switch"; +import { api } from "@/utils/api"; +import { Pencil, RocketIcon } from "lucide-react"; +import React, { useEffect, useState } from "react"; +import { toast } from "sonner"; +import { ShowDeployment } from "../deployments/show-deployment"; +import Link from "next/link"; +import { ShowModalLogs } from "../../settings/web-server/show-modal-logs"; +import { DialogAction } from "@/components/shared/dialog-action"; +import { AddPreviewDomain } from "./add-preview-domain"; +import { GithubIcon } from "@/components/icons/data-tools-icons"; +import { ShowPreviewSettings } from "./show-preview-settings"; +import { ShowPreviewBuilds } from "./show-preview-builds"; + +interface Props { + applicationId: string; +} + +export const ShowPreviewDeployments = ({ applicationId }: Props) => { + const [activeLog, setActiveLog] = useState(null); + const { data } = api.application.one.useQuery({ applicationId }); + + const { mutateAsync: deletePreviewDeployment, isLoading } = + api.previewDeployment.delete.useMutation(); + const { data: previewDeployments, refetch: refetchPreviewDeployments } = + api.previewDeployment.all.useQuery( + { applicationId }, + { + enabled: !!applicationId, + }, + ); + // const [url, setUrl] = React.useState(""); + // useEffect(() => { + // setUrl(document.location.origin); + // }, []); + + return ( + + +
+ Preview Deployments + See all the preview deployments +
+ {data?.isPreviewDeploymentsActive && ( + + )} +
+ + {data?.isPreviewDeploymentsActive ? ( + <> +
+ + Preview deployments are a way to test your application before it + is deployed to production. It will create a new deployment for + each pull request you create. + +
+ {data?.previewDeployments?.length === 0 ? ( +
+ + + No preview deployments found + +
+ ) : ( +
+ {previewDeployments?.map((previewDeployment) => { + const { deployments, domain } = previewDeployment; + + return ( +
+
+
+ {deployments?.length === 0 ? ( +
+ + No deployments found + +
+ ) : ( +
+ + {previewDeployment?.pullRequestTitle} + + +
+ )} +
+ {previewDeployment?.pullRequestTitle && ( +
+ + Title: {previewDeployment?.pullRequestTitle} + +
+ )} + + {previewDeployment?.pullRequestURL && ( +
+ + + Pull Request URL + +
+ )} +
+
+ Domain +
+ + {domain?.host} + + + + +
+
+
+ +
+ {previewDeployment?.createdAt && ( +
+ +
+ )} + + + + + + + { + deletePreviewDeployment({ + previewDeploymentId: + previewDeployment.previewDeploymentId, + }) + .then(() => { + refetchPreviewDeployments(); + toast.success("Preview deployment deleted"); + }) + .catch((error) => { + toast.error(error.message); + }); + }} + > + + +
+
+
+ ); + })} +
+ )} + + ) : ( +
+ + + Preview deployments are disabled for this application, please + enable it + + +
+ )} +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx new file mode 100644 index 000000000..6e56bbdd0 --- /dev/null +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -0,0 +1,351 @@ +import { api } from "@/utils/api"; +import { useEffect, useState } from "react"; +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, NumberInput } from "@/components/ui/input"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { Secrets } from "@/components/ui/secrets"; +import { toast } from "sonner"; +import { Switch } from "@/components/ui/switch"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +const schema = z.object({ + env: z.string(), + buildArgs: z.string(), + wildcardDomain: z.string(), + port: z.number(), + previewLimit: z.number(), + previewHttps: z.boolean(), + previewPath: z.string(), + previewCertificateType: z.enum(["letsencrypt", "none"]), +}); + +type Schema = z.infer; + +interface Props { + applicationId: string; +} + +export const ShowPreviewSettings = ({ applicationId }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [isEnabled, setIsEnabled] = useState(false); + const { mutateAsync: updateApplication, isLoading } = + api.application.update.useMutation(); + + const { data, refetch } = api.application.one.useQuery({ applicationId }); + + const form = useForm({ + defaultValues: { + env: "", + wildcardDomain: "*.traefik.me", + port: 3000, + previewLimit: 3, + previewHttps: false, + previewPath: "/", + previewCertificateType: "none", + }, + resolver: zodResolver(schema), + }); + + const previewHttps = form.watch("previewHttps"); + + useEffect(() => { + setIsEnabled(data?.isPreviewDeploymentsActive || false); + }, [data?.isPreviewDeploymentsActive]); + + useEffect(() => { + if (data) { + form.reset({ + env: data.previewEnv || "", + buildArgs: data.previewBuildArgs || "", + wildcardDomain: data.previewWildcard || "*.traefik.me", + port: data.previewPort || 3000, + previewLimit: data.previewLimit || 3, + previewHttps: data.previewHttps || false, + previewPath: data.previewPath || "/", + previewCertificateType: data.previewCertificateType || "none", + }); + } + }, [data]); + + const onSubmit = async (formData: Schema) => { + updateApplication({ + previewEnv: formData.env, + previewBuildArgs: formData.buildArgs, + previewWildcard: formData.wildcardDomain, + previewPort: formData.port, + applicationId, + previewLimit: formData.previewLimit, + previewHttps: formData.previewHttps, + previewPath: formData.previewPath, + previewCertificateType: formData.previewCertificateType, + }) + .then(() => { + toast.success("Preview Deployments settings updated"); + }) + .catch((error) => { + toast.error(error.message); + }); + }; + return ( +
+ + + + + + + Preview Deployment Settings + + Adjust the settings for preview deployments of this application, + including environment variables, build options, and deployment + rules. + + +
+
+ +
+ ( + + Wildcard Domain + + + + + + )} + /> + ( + + Preview Path + + + + + + )} + /> + ( + + Port + + + + + + )} + /> + ( + + Preview Limit + {/* + Set the limit of preview deployments that can be + created for this app. + */} + + + + + + )} + /> + ( + +
+ HTTPS + + Automatically provision SSL Certificate. + + +
+ + + +
+ )} + /> + {previewHttps && ( + ( + + Certificate + + + + )} + /> + )} +
+
+
+
+ + Enable preview deployments + + + Enable or disable preview deployments for this + application. + +
+ { + updateApplication({ + isPreviewDeploymentsActive: checked, + applicationId, + }) + .then(() => { + refetch(); + toast.success("Preview deployments enabled"); + }) + .catch((error) => { + toast.error(error.message); + }); + }} + /> +
+
+ + ( + + + + {/* */} + + + + )} + /> + {data?.buildType === "dockerfile" && ( + + Available only at build-time. See documentation  + + here + + . + + } + placeholder="NPM_TOKEN=xyz" + /> + )} + + +
+ + + + +
+
+ {/* */} +
+ ); +}; diff --git a/apps/dokploy/components/ui/secrets.tsx b/apps/dokploy/components/ui/secrets.tsx index a596ef1ba..5669b0518 100644 --- a/apps/dokploy/components/ui/secrets.tsx +++ b/apps/dokploy/components/ui/secrets.tsx @@ -29,7 +29,7 @@ export const Secrets = (props: Props) => { return ( <> - +
{props.title} {props.description} @@ -47,7 +47,7 @@ export const Secrets = (props: Props) => { )} - + statement-breakpoint +CREATE TABLE IF NOT EXISTS "preview_deployments" ( + "previewDeploymentId" text PRIMARY KEY NOT NULL, + "branch" text NOT NULL, + "pullRequestId" text NOT NULL, + "pullRequestNumber" text NOT NULL, + "pullRequestURL" text NOT NULL, + "pullRequestTitle" text NOT NULL, + "pullRequestCommentId" text NOT NULL, + "appName" text NOT NULL, + "applicationId" text NOT NULL, + "domainId" text, + "deploymentId" text, + "createdAt" text NOT NULL, + "expiresAt" text, + CONSTRAINT "preview_deployments_appName_unique" UNIQUE("appName") +); +--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewEnv" text;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewBuildArgs" text;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewWildcard" text;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewPort" integer DEFAULT 3000;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewHttps" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewPath" text DEFAULT '/';--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "certificateType" "certificateType" DEFAULT 'none' NOT NULL;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "previewLimit" integer DEFAULT 3;--> statement-breakpoint +ALTER TABLE "application" ADD COLUMN "isPreviewDeploymentsActive" boolean DEFAULT false;--> statement-breakpoint +ALTER TABLE "domain" ADD COLUMN "isPreviewDeployment" boolean DEFAULT false;--> statement-breakpoint +ALTER TABLE "domain" ADD COLUMN "previewDeploymentId" text;--> statement-breakpoint +ALTER TABLE "deployment" ADD COLUMN "isPreviewDeployment" boolean DEFAULT false;--> statement-breakpoint +ALTER TABLE "deployment" ADD COLUMN "previewDeploymentId" text;--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "preview_deployments" ADD CONSTRAINT "preview_deployments_applicationId_application_applicationId_fk" FOREIGN KEY ("applicationId") REFERENCES "public"."application"("applicationId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "preview_deployments" ADD CONSTRAINT "preview_deployments_domainId_domain_domainId_fk" FOREIGN KEY ("domainId") REFERENCES "public"."domain"("domainId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "preview_deployments" ADD CONSTRAINT "preview_deployments_deploymentId_deployment_deploymentId_fk" FOREIGN KEY ("deploymentId") REFERENCES "public"."deployment"("deploymentId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "domain" ADD CONSTRAINT "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk" FOREIGN KEY ("previewDeploymentId") REFERENCES "public"."preview_deployments"("previewDeploymentId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "deployment" ADD CONSTRAINT "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk" FOREIGN KEY ("previewDeploymentId") REFERENCES "public"."preview_deployments"("previewDeploymentId") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/apps/dokploy/drizzle/0048_clumsy_matthew_murdock.sql b/apps/dokploy/drizzle/0048_clumsy_matthew_murdock.sql new file mode 100644 index 000000000..ef939a029 --- /dev/null +++ b/apps/dokploy/drizzle/0048_clumsy_matthew_murdock.sql @@ -0,0 +1 @@ +ALTER TABLE "preview_deployments" ADD COLUMN "previewStatus" "applicationStatus" DEFAULT 'idle' NOT NULL; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0049_useful_mole_man.sql b/apps/dokploy/drizzle/0049_useful_mole_man.sql new file mode 100644 index 000000000..c672e02f3 --- /dev/null +++ b/apps/dokploy/drizzle/0049_useful_mole_man.sql @@ -0,0 +1,3 @@ +ALTER TABLE "preview_deployments" DROP CONSTRAINT "preview_deployments_deploymentId_deployment_deploymentId_fk"; +--> statement-breakpoint +ALTER TABLE "preview_deployments" DROP COLUMN IF EXISTS "deploymentId"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0047_snapshot.json b/apps/dokploy/drizzle/meta/0047_snapshot.json new file mode 100644 index 000000000..34574a694 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0047_snapshot.json @@ -0,0 +1,4237 @@ +{ + "id": "053ad983-6299-4420-a551-645490689733", + "prevId": "d70bcec5-e7af-4872-b2eb-f0a22ae2e3e8", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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'" + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "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": { + "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'" + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": { + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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 + } + }, + "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.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 + }, + "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_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 + }, + "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'" + }, + "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 + }, + "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 + }, + "deploymentId": { + "name": "deploymentId", + "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" + }, + "preview_deployments_deploymentId_deployment_deploymentId_fk": { + "name": "preview_deployments_deploymentId_deployment_deploymentId_fk", + "tableFrom": "preview_deployments", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "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" + ] + }, + "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/0048_snapshot.json b/apps/dokploy/drizzle/meta/0048_snapshot.json new file mode 100644 index 000000000..47ad2cb90 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0048_snapshot.json @@ -0,0 +1,4245 @@ +{ + "id": "20236ed8-104c-487b-bcdf-d08b69fbc80a", + "prevId": "053ad983-6299-4420-a551-645490689733", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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'" + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "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": { + "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'" + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": { + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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 + } + }, + "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.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 + }, + "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_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 + }, + "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'" + }, + "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 + }, + "deploymentId": { + "name": "deploymentId", + "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" + }, + "preview_deployments_deploymentId_deployment_deploymentId_fk": { + "name": "preview_deployments_deploymentId_deployment_deploymentId_fk", + "tableFrom": "preview_deployments", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "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" + ] + }, + "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/0049_snapshot.json b/apps/dokploy/drizzle/meta/0049_snapshot.json new file mode 100644 index 000000000..43c6e0c02 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0049_snapshot.json @@ -0,0 +1,4226 @@ +{ + "id": "71016fed-2c39-4d31-aa33-0e0aceb313ef", + "prevId": "20236ed8-104c-487b-bcdf-d08b69fbc80a", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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'" + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "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": { + "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'" + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": { + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "integer", + "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 + } + }, + "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.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 + }, + "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_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 + }, + "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'" + }, + "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" + ] + }, + "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 a2bb1be1b..2ffe385ef 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -330,6 +330,27 @@ "when": 1732851191048, "tag": "0046_purple_sleeper", "breakpoints": true + }, + { + "idx": 47, + "version": "6", + "when": 1733089956329, + "tag": "0047_red_stephen_strange", + "breakpoints": true + }, + { + "idx": 48, + "version": "6", + "when": 1733091544421, + "tag": "0048_clumsy_matthew_murdock", + "breakpoints": true + }, + { + "idx": 49, + "version": "6", + "when": 1733091820570, + "tag": "0049_useful_mole_man", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f1e9505f1..11439f581 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -11,7 +11,8 @@ "build-next": "next build", "setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run", "reset-password": "node -r dotenv/config dist/reset-password.mjs", - "dev": "TURBOPACK=1 tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ", + "dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ", + "dev-turbopack": "TURBOPACK=1 tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json", "studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts", "migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts", "migration:run": "tsx -r dotenv/config migration.ts", diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 1d8c094af..e89523e95 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -3,145 +3,259 @@ import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; import { deploy } from "@/server/utils/deploy"; -import { IS_CLOUD } from "@dokploy/server"; +import { + createPreviewDeployment, + type Domain, + findPreviewDeploymentByApplicationId, + findPreviewDeploymentsByPullRequestId, + IS_CLOUD, + removePreviewDeployment, +} from "@dokploy/server"; import { Webhooks } from "@octokit/webhooks"; import { and, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; +import { generateRandomDomain } from "@/templates/utils"; export default async function handler( - req: NextApiRequest, - res: NextApiResponse + req: NextApiRequest, + res: NextApiResponse, ) { - const signature = req.headers["x-hub-signature-256"]; - const githubBody = req.body; - - if (!githubBody?.installation?.id) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - const githubResult = await db.query.github.findFirst({ - where: eq(github.githubInstallationId, githubBody.installation.id), - }); - - if (!githubResult) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - if (!githubResult.githubWebhookSecret) { - res.status(400).json({ message: "Github Webhook Secret not set" }); - return; - } - const webhooks = new Webhooks({ - secret: githubResult.githubWebhookSecret, - }); - - const verified = await webhooks.verify( - JSON.stringify(githubBody), - signature as string - ); - - if (!verified) { - res.status(401).json({ message: "Unauthorized" }); - return; - } - - if (req.headers["x-github-event"] === "ping") { - res.status(200).json({ message: "Ping received, webhook is active" }); - return; - } - - if (req.headers["x-github-event"] !== "push") { - res.status(400).json({ message: "We only accept push events" }); - return; - } - - try { - const branchName = githubBody?.ref?.replace("refs/heads/", ""); - const repository = githubBody?.repository?.name; - const deploymentTitle = extractCommitMessage(req.headers, req.body); - const deploymentHash = extractHash(req.headers, req.body); - - const apps = await db.query.applications.findMany({ - where: and( - eq(applications.sourceType, "github"), - eq(applications.autoDeploy, true), - eq(applications.branch, branchName), - eq(applications.repository, repository) - ), - }); - - for (const app of apps) { - const jobData: DeploymentJob = { - applicationId: app.applicationId as string, - titleLog: deploymentTitle, - descriptionLog: `Hash: ${deploymentHash}`, - type: "deploy", - applicationType: "application", - server: !!app.serverId, - }; - - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const composeApps = await db.query.compose.findMany({ - where: and( - eq(compose.sourceType, "github"), - eq(compose.autoDeploy, true), - eq(compose.branch, branchName), - eq(compose.repository, repository) - ), - }); - - for (const composeApp of composeApps) { - const jobData: DeploymentJob = { - composeId: composeApp.composeId as string, - titleLog: deploymentTitle, - type: "deploy", - applicationType: "compose", - descriptionLog: `Hash: ${deploymentHash}`, - server: !!composeApp.serverId, - }; - - if (IS_CLOUD && composeApp.serverId) { - jobData.serverId = composeApp.serverId; - await deploy(jobData); - return true; - } - - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const totalApps = apps.length + composeApps.length; - const emptyApps = totalApps === 0; - - if (emptyApps) { - res.status(200).json({ message: "No apps to deploy" }); - return; - } - res.status(200).json({ message: `Deployed ${totalApps} apps` }); - } catch (error) { - res.status(400).json({ message: "Error To Deploy Application", error }); - } + const signature = req.headers["x-hub-signature-256"]; + const githubBody = req.body; + + if (!githubBody?.installation?.id) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + const githubResult = await db.query.github.findFirst({ + where: eq(github.githubInstallationId, githubBody.installation.id), + }); + + if (!githubResult) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + if (!githubResult.githubWebhookSecret) { + res.status(400).json({ message: "Github Webhook Secret not set" }); + return; + } + const webhooks = new Webhooks({ + secret: githubResult.githubWebhookSecret, + }); + + const verified = await webhooks.verify( + JSON.stringify(githubBody), + signature as string, + ); + + if (!verified) { + res.status(401).json({ message: "Unauthorized" }); + return; + } + + if (req.headers["x-github-event"] === "ping") { + res.status(200).json({ message: "Ping received, webhook is active" }); + return; + } + + if ( + req.headers["x-github-event"] !== "push" && + req.headers["x-github-event"] !== "pull_request" + ) { + res + .status(400) + .json({ message: "We only accept push events or pull_request events" }); + return; + } + + if (req.headers["x-github-event"] === "push") { + try { + const branchName = githubBody?.ref?.replace("refs/heads/", ""); + const repository = githubBody?.repository?.name; + const deploymentTitle = extractCommitMessage(req.headers, req.body); + const deploymentHash = extractHash(req.headers, req.body); + + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.autoDeploy, true), + eq(applications.branch, branchName), + eq(applications.repository, repository), + ), + }); + + for (const app of apps) { + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: deploymentTitle, + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application", + server: !!app.serverId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const composeApps = await db.query.compose.findMany({ + where: and( + eq(compose.sourceType, "github"), + eq(compose.autoDeploy, true), + eq(compose.branch, branchName), + eq(compose.repository, repository), + ), + }); + + for (const composeApp of composeApps) { + const jobData: DeploymentJob = { + composeId: composeApp.composeId as string, + titleLog: deploymentTitle, + type: "deploy", + applicationType: "compose", + descriptionLog: `Hash: ${deploymentHash}`, + server: !!composeApp.serverId, + }; + + if (IS_CLOUD && composeApp.serverId) { + jobData.serverId = composeApp.serverId; + await deploy(jobData); + return true; + } + + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const totalApps = apps.length + composeApps.length; + const emptyApps = totalApps === 0; + + if (emptyApps) { + res.status(200).json({ message: "No apps to deploy" }); + return; + } + res.status(200).json({ message: `Deployed ${totalApps} apps` }); + } catch (error) { + res.status(400).json({ message: "Error To Deploy Application", error }); + } + } else if (req.headers["x-github-event"] === "pull_request") { + const prId = githubBody?.pull_request?.id; + if (githubBody?.action === "closed") { + const previewDeploymentResult = + await findPreviewDeploymentsByPullRequestId(prId); + + if (previewDeploymentResult.length > 0) { + for (const previewDeployment of previewDeploymentResult) { + try { + await removePreviewDeployment( + previewDeployment.previewDeploymentId, + ); + } catch (error) { + console.log(error); + } + } + } + res.status(200).json({ message: "Preview Deployment Closed" }); + return; + } + // opened or synchronize or reopened + const repository = githubBody?.repository?.name; + const deploymentHash = githubBody?.pull_request?.head?.sha; + const branch = githubBody?.pull_request?.base?.ref; + + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.autoDeploy, true), + eq(applications.repository, repository), + eq(applications.branch, branch), + eq(applications.isPreviewDeploymentsActive, true), + ), + with: { + previewDeployments: true, + }, + }); + + const prBranch = githubBody?.pull_request?.head?.ref; + + const prNumber = githubBody?.pull_request?.number; + const prTitle = githubBody?.pull_request?.title; + const prURL = githubBody?.pull_request?.html_url; + + for (const app of apps) { + const previewLimit = app?.previewLimit || 0; + if (app?.previewDeployments?.length > previewLimit) { + continue; + } + const previewDeploymentResult = + await findPreviewDeploymentByApplicationId(app.applicationId, prId); + + let previewDeploymentId = + previewDeploymentResult?.previewDeploymentId || ""; + + if (!previewDeploymentResult) { + const previewDeployment = await createPreviewDeployment({ + applicationId: app.applicationId as string, + branch: prBranch, + pullRequestId: prId, + pullRequestNumber: prNumber, + pullRequestTitle: prTitle, + pullRequestURL: prURL, + }); + previewDeploymentId = previewDeployment.previewDeploymentId; + } + + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: "Preview Deployment", + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application-preview", + server: !!app.serverId, + previewDeploymentId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + return res.status(200).json({ message: "Apps Deployed" }); + } + + return res.status(400).json({ message: "No Actions matched" }); } + +// Genera el dominio random +// Crea el pull request entity +// Crea el deployment entity +// Luego lo que sigue...., unicamente github es soportado por ahora3 diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 156e1973a..bcbd4b78b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -12,6 +12,7 @@ import { ShowDomains } from "@/components/dashboard/application/domains/show-dom import { ShowEnvironment } from "@/components/dashboard/application/environment/show"; import { ShowGeneralApplication } from "@/components/dashboard/application/general/show"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { ShowPreviewDeployments } from "@/components/dashboard/application/preview-deployments/show-preview-deployments"; import { UpdateApplication } from "@/components/dashboard/application/update-application"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { ProjectLayout } from "@/components/layouts/project-layout"; @@ -51,7 +52,8 @@ type TabState = | "advanced" | "deployments" | "domains" - | "monitoring"; + | "monitoring" + | "preview-deployments"; const Service = ( props: InferGetServerSidePropsType, @@ -191,8 +193,8 @@ const Service = (
General @@ -202,6 +204,9 @@ const Service = ( )} Logs Deployments + + Preview Deployments + Domains Advanced @@ -244,6 +249,11 @@ const Service = (
+ +
+ +
+
diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts index 1b67d3507..85eb9763e 100644 --- a/apps/dokploy/server/api/root.ts +++ b/apps/dokploy/server/api/root.ts @@ -31,6 +31,7 @@ import { settingsRouter } from "./routers/settings"; import { sshRouter } from "./routers/ssh-key"; import { stripeRouter } from "./routers/stripe"; import { userRouter } from "./routers/user"; +import { previewDeploymentRouter } from "./routers/preview-deployment"; /** * This is the primary router for your server. @@ -55,6 +56,7 @@ export const appRouter = createTRPCRouter({ destination: destinationRouter, backup: backupRouter, deployment: deploymentRouter, + previewDeployment: previewDeploymentRouter, mounts: mountRouter, certificates: certificateRouter, settings: settingsRouter, diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index 3c94fc933..b0a1000c5 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -13,6 +13,7 @@ import { findDomainById, findDomainsByApplicationId, findDomainsByComposeId, + findPreviewDeploymentById, generateTraefikMeDomain, manageDomain, removeDomain, @@ -108,12 +109,33 @@ export const domainRouter = createTRPCRouter({ message: "You are not authorized to access this compose", }); } + } else if (currentDomain.previewDeploymentId) { + const newPreviewDeployment = await findPreviewDeploymentById( + currentDomain.previewDeploymentId, + ); + if ( + newPreviewDeployment.application.project.adminId !== ctx.user.adminId + ) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this preview deployment", + }); + } } const result = await updateDomainById(input.domainId, input); const domain = await findDomainById(input.domainId); if (domain.applicationId) { const application = await findApplicationById(domain.applicationId); await manageDomain(application, domain); + } else if (domain.previewDeploymentId) { + const previewDeployment = await findPreviewDeploymentById( + domain.previewDeploymentId, + ); + const application = await findApplicationById( + previewDeployment.applicationId, + ); + application.appName = previewDeployment.appName; + await manageDomain(application, domain); } return result; }), diff --git a/apps/dokploy/server/api/routers/preview-deployment.ts b/apps/dokploy/server/api/routers/preview-deployment.ts new file mode 100644 index 000000000..74b8461ae --- /dev/null +++ b/apps/dokploy/server/api/routers/preview-deployment.ts @@ -0,0 +1,54 @@ +import { apiFindAllByApplication } from "@/server/db/schema"; +import { + findApplicationById, + findPreviewDeploymentById, + findPreviewDeploymentsByApplicationId, + removePreviewDeployment, +} from "@dokploy/server"; +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { createTRPCRouter, protectedProcedure } from "../trpc"; + +export const previewDeploymentRouter = createTRPCRouter({ + all: protectedProcedure + .input(apiFindAllByApplication) + .query(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this application", + }); + } + return await findPreviewDeploymentsByApplicationId(input.applicationId); + }), + delete: protectedProcedure + .input(z.object({ previewDeploymentId: z.string() })) + .mutation(async ({ input, ctx }) => { + const previewDeployment = await findPreviewDeploymentById( + input.previewDeploymentId, + ); + if (previewDeployment.application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to delete this preview deployment", + }); + } + await removePreviewDeployment(input.previewDeploymentId); + return true; + }), + one: protectedProcedure + .input(z.object({ previewDeploymentId: z.string() })) + .query(async ({ input, ctx }) => { + const previewDeployment = await findPreviewDeploymentById( + input.previewDeploymentId, + ); + if (previewDeployment.application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this preview deployment", + }); + } + return previewDeployment; + }), +}); diff --git a/apps/dokploy/server/queues/deployments-queue.ts b/apps/dokploy/server/queues/deployments-queue.ts index 08e0c9a12..9ff8a1573 100644 --- a/apps/dokploy/server/queues/deployments-queue.ts +++ b/apps/dokploy/server/queues/deployments-queue.ts @@ -1,14 +1,17 @@ import { deployApplication, deployCompose, + deployPreviewApplication, deployRemoteApplication, deployRemoteCompose, + deployRemotePreviewApplication, rebuildApplication, rebuildCompose, rebuildRemoteApplication, rebuildRemoteCompose, updateApplicationStatus, updateCompose, + updatePreviewDeployment, } from "@dokploy/server"; import { type Job, Worker } from "bullmq"; import type { DeploymentJob } from "./queue-types"; @@ -18,8 +21,11 @@ export const deploymentWorker = new Worker( "deployments", async (job: Job) => { try { + console.log(job.data); + if (job.data.applicationType === "application") { await updateApplicationStatus(job.data.applicationId, "running"); + if (job.data.server) { if (job.data.type === "redeploy") { await rebuildRemoteApplication({ @@ -83,6 +89,29 @@ export const deploymentWorker = new Worker( }); } } + } else if (job.data.applicationType === "application-preview") { + await updatePreviewDeployment(job.data.previewDeploymentId, { + previewStatus: "running", + }); + if (job.data.server) { + if (job.data.type === "deploy") { + await deployRemotePreviewApplication({ + applicationId: job.data.applicationId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + previewDeploymentId: job.data.previewDeploymentId, + }); + } + } else { + if (job.data.type === "deploy") { + await deployPreviewApplication({ + applicationId: job.data.applicationId, + titleLog: job.data.titleLog, + descriptionLog: job.data.descriptionLog, + previewDeploymentId: job.data.previewDeploymentId, + }); + } + } } } catch (error) { console.log("Error", error); diff --git a/apps/dokploy/server/queues/queue-types.ts b/apps/dokploy/server/queues/queue-types.ts index f467836a2..ef8df6943 100644 --- a/apps/dokploy/server/queues/queue-types.ts +++ b/apps/dokploy/server/queues/queue-types.ts @@ -16,6 +16,16 @@ type DeployJob = type: "deploy" | "redeploy"; applicationType: "compose"; serverId?: string; + } + | { + applicationId: string; + titleLog: string; + descriptionLog: string; + server?: boolean; + type: "deploy"; + applicationType: "application-preview"; + previewDeploymentId: string; + serverId?: string; }; export type DeploymentJob = DeployJob; diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index b65446f8b..b13e5df5f 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -24,7 +24,7 @@ import { setupTerminalWebSocketServer } from "./wss/terminal"; config({ path: ".env" }); const PORT = Number.parseInt(process.env.PORT || "3000", 10); const dev = process.env.NODE_ENV !== "production"; -const app = next({ dev, turbopack: dev }); +const app = next({ dev, turbopack: process.env.TURBOPACK === "1" }); const handle = app.getRequestHandler(); void app.prepare().then(async () => { try { diff --git a/package.json b/package.json index 94d176a62..f520707db 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "dokploy:setup": "pnpm --filter=dokploy run setup", "dokploy:dev": "pnpm --filter=dokploy run dev", + "dokploy:dev:turbopack": "pnpm --filter=dokploy run dev-turbopack", "dokploy:build": "pnpm --filter=dokploy run build", "dokploy:start": "pnpm --filter=dokploy run start", "test": "pnpm --filter=dokploy run test", diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 3afc96845..382a2a0ac 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -22,9 +22,10 @@ import { redirects } from "./redirects"; import { registry } from "./registry"; import { security } from "./security"; import { server } from "./server"; -import { applicationStatus } from "./shared"; +import { applicationStatus, certificateType } from "./shared"; import { sshKeys } from "./ssh-key"; import { generateAppName } from "./utils"; +import { previewDeployments } from "./preview-deployments"; export const sourceType = pgEnum("sourceType", [ "docker", @@ -114,6 +115,19 @@ export const applications = pgTable("application", { .unique(), description: text("description"), env: text("env"), + previewEnv: text("previewEnv"), + previewBuildArgs: text("previewBuildArgs"), + previewWildcard: text("previewWildcard"), + previewPort: integer("previewPort").default(3000), + previewHttps: boolean("previewHttps").notNull().default(false), + previewPath: text("previewPath").default("/"), + previewCertificateType: certificateType("certificateType") + .notNull() + .default("none"), + previewLimit: integer("previewLimit").default(3), + isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( + false, + ), buildArgs: text("buildArgs"), memoryReservation: integer("memoryReservation"), memoryLimit: integer("memoryLimit"), @@ -239,6 +253,7 @@ export const applicationsRelations = relations( fields: [applications.serverId], references: [server.serverId], }), + previewDeployments: many(previewDeployments), }), ); @@ -348,6 +363,7 @@ const createSchema = createInsertSchema(applications, { subtitle: z.string().optional(), dockerImage: z.string().optional(), username: z.string().optional(), + isPreviewDeploymentsActive: z.boolean().optional(), password: z.string().optional(), registryUrl: z.string().optional(), customGitSSHKeyId: z.string().optional(), @@ -378,6 +394,14 @@ const createSchema = createInsertSchema(applications, { modeSwarm: ServiceModeSwarmSchema.nullable(), labelsSwarm: LabelsSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(), + previewPort: z.number().optional(), + previewEnv: z.string().optional(), + previewBuildArgs: z.string().optional(), + previewWildcard: z.string().optional(), + previewLimit: z.number().optional(), + previewHttps: z.boolean().optional(), + previewPath: z.string().optional(), + previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), }); export const apiCreateApplication = createSchema.pick({ diff --git a/packages/server/src/db/schema/deployment.ts b/packages/server/src/db/schema/deployment.ts index db9838f05..f79b48ee9 100644 --- a/packages/server/src/db/schema/deployment.ts +++ b/packages/server/src/db/schema/deployment.ts @@ -1,11 +1,18 @@ -import { relations } from "drizzle-orm"; -import { pgEnum, pgTable, text } from "drizzle-orm/pg-core"; +import { is, relations } from "drizzle-orm"; +import { + type AnyPgColumn, + boolean, + pgEnum, + pgTable, + text, +} from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; +import { previewDeployments } from "./preview-deployments"; export const deploymentStatus = pgEnum("deploymentStatus", [ "running", @@ -32,6 +39,11 @@ export const deployments = pgTable("deployment", { serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), + isPreviewDeployment: boolean("isPreviewDeployment").default(false), + previewDeploymentId: text("previewDeploymentId").references( + (): AnyPgColumn => previewDeployments.previewDeploymentId, + { onDelete: "cascade" }, + ), createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), @@ -50,6 +62,10 @@ export const deploymentsRelations = relations(deployments, ({ one }) => ({ fields: [deployments.serverId], references: [server.serverId], }), + previewDeployment: one(previewDeployments, { + fields: [deployments.previewDeploymentId], + references: [previewDeployments.previewDeploymentId], + }), })); const schema = createInsertSchema(deployments, { @@ -59,6 +75,7 @@ const schema = createInsertSchema(deployments, { applicationId: z.string(), composeId: z.string(), description: z.string().optional(), + previewDeploymentId: z.string(), }); export const apiCreateDeployment = schema @@ -68,11 +85,24 @@ export const apiCreateDeployment = schema logPath: true, applicationId: true, description: true, + previewDeploymentId: true, }) .extend({ applicationId: z.string().min(1), }); +export const apiCreateDeploymentPreview = schema + .pick({ + title: true, + status: true, + logPath: true, + description: true, + previewDeploymentId: true, + }) + .extend({ + previewDeploymentId: z.string().min(1), + }); + export const apiCreateDeploymentCompose = schema .pick({ title: true, diff --git a/packages/server/src/db/schema/domain.ts b/packages/server/src/db/schema/domain.ts index 288291189..7c7696891 100644 --- a/packages/server/src/db/schema/domain.ts +++ b/packages/server/src/db/schema/domain.ts @@ -1,5 +1,6 @@ import { relations } from "drizzle-orm"; import { + type AnyPgColumn, boolean, integer, pgEnum, @@ -14,8 +15,13 @@ import { domain } from "../validations/domain"; import { applications } from "./application"; import { compose } from "./compose"; import { certificateType } from "./shared"; +import { previewDeployments } from "./preview-deployments"; -export const domainType = pgEnum("domainType", ["compose", "application"]); +export const domainType = pgEnum("domainType", [ + "compose", + "application", + "preview", +]); export const domains = pgTable("domain", { domainId: text("domainId") @@ -28,6 +34,7 @@ export const domains = pgTable("domain", { path: text("path").default("/"), serviceName: text("serviceName"), domainType: domainType("domainType").default("application"), + isPreviewDeployment: boolean("isPreviewDeployment").default(false), // TODO: remove uniqueConfigKey: serial("uniqueConfigKey"), createdAt: text("createdAt") .notNull() @@ -39,6 +46,10 @@ export const domains = pgTable("domain", { () => applications.applicationId, { onDelete: "cascade" }, ), + previewDeploymentId: text("previewDeploymentId").references( + (): AnyPgColumn => previewDeployments.previewDeploymentId, + { onDelete: "cascade" }, + ), certificateType: certificateType("certificateType").notNull().default("none"), }); @@ -51,6 +62,10 @@ export const domainsRelations = relations(domains, ({ one }) => ({ fields: [domains.composeId], references: [compose.composeId], }), + previewDeployment: one(previewDeployments, { + fields: [domains.previewDeploymentId], + references: [previewDeployments.previewDeploymentId], + }), })); const createSchema = createInsertSchema(domains, domain._def.schema.shape); @@ -65,6 +80,7 @@ export const apiCreateDomain = createSchema.pick({ composeId: true, serviceName: true, domainType: true, + previewDeploymentId: true, }); export const apiFindDomain = createSchema diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index 4a6103688..f07a18707 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -29,3 +29,4 @@ export * from "./github"; export * from "./gitlab"; export * from "./server"; export * from "./utils"; +export * from "./preview-deployments"; \ No newline at end of file diff --git a/packages/server/src/db/schema/preview-deployments.ts b/packages/server/src/db/schema/preview-deployments.ts new file mode 100644 index 000000000..5d0671e8d --- /dev/null +++ b/packages/server/src/db/schema/preview-deployments.ts @@ -0,0 +1,74 @@ +import { relations } from "drizzle-orm"; +import { pgTable, text } from "drizzle-orm/pg-core"; +import { nanoid } from "nanoid"; +import { applications } from "./application"; +import { domains } from "./domain"; +import { deployments } from "./deployment"; +import { createInsertSchema } from "drizzle-zod"; +import { z } from "zod"; +import { generateAppName } from "./utils"; +import { applicationStatus } from "./shared"; + +export const previewDeployments = pgTable("preview_deployments", { + previewDeploymentId: text("previewDeploymentId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + branch: text("branch").notNull(), + pullRequestId: text("pullRequestId").notNull(), + pullRequestNumber: text("pullRequestNumber").notNull(), + pullRequestURL: text("pullRequestURL").notNull(), + pullRequestTitle: text("pullRequestTitle").notNull(), + pullRequestCommentId: text("pullRequestCommentId").notNull(), + previewStatus: applicationStatus("previewStatus").notNull().default("idle"), + appName: text("appName") + .notNull() + .$defaultFn(() => generateAppName("preview")) + .unique(), + applicationId: text("applicationId") + .notNull() + .references(() => applications.applicationId, { + onDelete: "cascade", + }), + domainId: text("domainId").references(() => domains.domainId, { + onDelete: "cascade", + }), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), + expiresAt: text("expiresAt"), +}); + +export const previewDeploymentsRelations = relations( + previewDeployments, + ({ one, many }) => ({ + deployments: many(deployments), + domain: one(domains, { + fields: [previewDeployments.domainId], + references: [domains.domainId], + }), + application: one(applications, { + fields: [previewDeployments.applicationId], + references: [applications.applicationId], + }), + }), +); + +export const createSchema = createInsertSchema(previewDeployments, { + applicationId: z.string(), +}); + +export const apiCreatePreviewDeployment = createSchema + .pick({ + applicationId: true, + domainId: true, + branch: true, + pullRequestId: true, + pullRequestNumber: true, + pullRequestURL: true, + pullRequestTitle: true, + }) + .extend({ + applicationId: z.string().min(1), + // deploymentId: z.string().min(1), + }); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 12f3b64e5..b8ec30e24 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -20,6 +20,7 @@ export * from "./services/mount"; export * from "./services/certificate"; export * from "./services/redirect"; export * from "./services/security"; +export * from "./services/preview-deployment"; export * from "./services/port"; export * from "./services/redis"; export * from "./services/compose"; diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index ae8a9b76c..ba36dcec3 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -28,6 +28,7 @@ import { getCustomGitCloneCommand, } from "@dokploy/server/utils/providers/git"; import { + authGithub, cloneGithubRepository, getGithubCloneCommand, } from "@dokploy/server/utils/providers/github"; @@ -40,8 +41,18 @@ import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; import { getDokployUrl } from "./admin"; -import { createDeployment, updateDeploymentStatus } from "./deployment"; +import { + createDeployment, + createDeploymentPreview, + updateDeploymentStatus, +} from "./deployment"; import { validUniqueServerAppName } from "./project"; +import { + findPreviewDeploymentById, + updatePreviewDeployment, +} from "./preview-deployment"; +import { getIssueComment, updateIssueComment } from "./github"; +import { type Domain, getDomainHost } from "./domain"; export type Application = typeof applications.$inferSelect; export const createApplication = async ( @@ -100,6 +111,7 @@ export const findApplicationById = async (applicationId: string) => { github: true, bitbucket: true, server: true, + previewDeployments: true, }, }); if (!application) { @@ -168,7 +180,10 @@ export const deployApplication = async ({ try { if (application.sourceType === "github") { - await cloneGithubRepository(application, deployment.logPath); + await cloneGithubRepository({ + ...application, + logPath: deployment.logPath, + }); await buildApplication(application, deployment.logPath); } else if (application.sourceType === "gitlab") { await cloneGitlabRepository(application, deployment.logPath); @@ -276,7 +291,11 @@ export const deployRemoteApplication = async ({ if (application.serverId) { let command = "set -e;"; if (application.sourceType === "github") { - command += await getGithubCloneCommand(application, deployment.logPath); + command += await getGithubCloneCommand({ + ...application, + serverId: application.serverId, + logPath: deployment.logPath, + }); } else if (application.sourceType === "gitlab") { command += await getGitlabCloneCommand(application, deployment.logPath); } else if (application.sourceType === "bitbucket") { @@ -348,6 +367,183 @@ export const deployRemoteApplication = async ({ return true; }; +export const deployPreviewApplication = async ({ + applicationId, + titleLog = "Preview Deployment", + descriptionLog = "", + previewDeploymentId, +}: { + applicationId: string; + titleLog: string; + descriptionLog: string; + previewDeploymentId: string; +}) => { + const application = await findApplicationById(applicationId); + const deployment = await createDeploymentPreview({ + title: titleLog, + description: descriptionLog, + previewDeploymentId: previewDeploymentId, + }); + + const previewDeployment = + await findPreviewDeploymentById(previewDeploymentId); + + await updatePreviewDeployment(previewDeploymentId, { + createdAt: new Date().toISOString(), + }); + + const previewDomain = getDomainHost(previewDeployment?.domain as Domain); + const issueParams = { + owner: application?.owner || "", + repository: application?.repository || "", + issue_number: previewDeployment.pullRequestNumber, + comment_id: Number.parseInt(previewDeployment.pullRequestCommentId), + githubId: application?.githubId || "", + }; + try { + const buildingComment = getIssueComment( + application.name, + "running", + previewDomain, + ); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${buildingComment}`, + }); + application.appName = previewDeployment.appName; + application.env = application.previewEnv; + application.buildArgs = application.previewBuildArgs; + + if (application.sourceType === "github") { + await cloneGithubRepository({ + ...application, + appName: previewDeployment.appName, + branch: previewDeployment.branch, + logPath: deployment.logPath, + }); + await buildApplication(application, deployment.logPath); + } + // 4eef09efc46009187d668cf1c25f768d0bde4f91 + const successComment = getIssueComment( + application.name, + "success", + previewDomain, + ); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${successComment}`, + }); + await updateDeploymentStatus(deployment.deploymentId, "done"); + await updatePreviewDeployment(previewDeploymentId, { + previewStatus: "done", + }); + } catch (error) { + const comment = getIssueComment(application.name, "error", previewDomain); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${comment}`, + }); + await updateDeploymentStatus(deployment.deploymentId, "error"); + await updatePreviewDeployment(previewDeploymentId, { + previewStatus: "error", + }); + throw error; + } + + return true; +}; + +export const deployRemotePreviewApplication = async ({ + applicationId, + titleLog = "Preview Deployment", + descriptionLog = "", + previewDeploymentId, +}: { + applicationId: string; + titleLog: string; + descriptionLog: string; + previewDeploymentId: string; +}) => { + const application = await findApplicationById(applicationId); + const deployment = await createDeploymentPreview({ + title: titleLog, + description: descriptionLog, + previewDeploymentId: previewDeploymentId, + }); + + const previewDeployment = + await findPreviewDeploymentById(previewDeploymentId); + + await updatePreviewDeployment(previewDeploymentId, { + createdAt: new Date().toISOString(), + }); + + const previewDomain = getDomainHost(previewDeployment?.domain as Domain); + const issueParams = { + owner: application?.owner || "", + repository: application?.repository || "", + issue_number: previewDeployment.pullRequestNumber, + comment_id: Number.parseInt(previewDeployment.pullRequestCommentId), + githubId: application?.githubId || "", + }; + try { + const buildingComment = getIssueComment( + application.name, + "running", + previewDomain, + ); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${buildingComment}`, + }); + application.appName = previewDeployment.appName; + application.env = application.previewEnv; + application.buildArgs = application.previewBuildArgs; + + if (application.serverId) { + let command = "set -e;"; + if (application.sourceType === "github") { + command += await getGithubCloneCommand({ + ...application, + serverId: application.serverId, + logPath: deployment.logPath, + }); + } + + command += getBuildCommand(application, deployment.logPath); + await execAsyncRemote(application.serverId, command); + await mechanizeDockerContainer(application); + } + + const successComment = getIssueComment( + application.name, + "success", + previewDomain, + ); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${successComment}`, + }); + await updateDeploymentStatus(deployment.deploymentId, "done"); + await updatePreviewDeployment(previewDeploymentId, { + previewStatus: "done", + }); + } catch (error) { + const comment = getIssueComment(application.name, "error", previewDomain); + await updateIssueComment({ + ...issueParams, + body: `### Dokploy Preview Deployment\n\n${comment}`, + }); + await updateDeploymentStatus(deployment.deploymentId, "error"); + await updatePreviewDeployment(previewDeploymentId, { + previewStatus: "error", + }); + throw error; + } + + return true; +}; + export const rebuildRemoteApplication = async ({ applicationId, titleLog = "Rebuild deployment", diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 63d29539d..a12fc7d27 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -214,7 +214,11 @@ export const deployCompose = async ({ try { if (compose.sourceType === "github") { - await cloneGithubRepository(compose, deployment.logPath, true); + await cloneGithubRepository({ + ...compose, + logPath: deployment.logPath, + type: "compose", + }); } else if (compose.sourceType === "gitlab") { await cloneGitlabRepository(compose, deployment.logPath, true); } else if (compose.sourceType === "bitbucket") { @@ -314,11 +318,12 @@ export const deployRemoteCompose = async ({ let command = "set -e;"; if (compose.sourceType === "github") { - command += await getGithubCloneCommand( - compose, - deployment.logPath, - true, - ); + command += await getGithubCloneCommand({ + ...compose, + logPath: deployment.logPath, + type: "compose", + serverId: compose.serverId, + }); } else if (compose.sourceType === "gitlab") { command += await getGitlabCloneCommand( compose, diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index 63f3cf23f..b18b132de 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -5,13 +5,14 @@ import { db } from "@dokploy/server/db"; import { type apiCreateDeployment, type apiCreateDeploymentCompose, + type apiCreateDeploymentPreview, type apiCreateDeploymentServer, deployments, } from "@dokploy/server/db/schema"; import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory"; import { TRPCError } from "@trpc/server"; import { format } from "date-fns"; -import { desc, eq } from "drizzle-orm"; +import { and, desc, eq, isNull } from "drizzle-orm"; import { type Application, findApplicationById, @@ -21,6 +22,11 @@ import { type Compose, findComposeById, updateCompose } from "./compose"; import { type Server, findServerById } from "./server"; import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; +import { + findPreviewDeploymentById, + type PreviewDeployment, + updatePreviewDeployment, +} from "./preview-deployment"; export type Deployment = typeof deployments.$inferSelect; @@ -101,6 +107,74 @@ export const createDeployment = async ( } }; +export const createDeploymentPreview = async ( + deployment: Omit< + typeof apiCreateDeploymentPreview._type, + "deploymentId" | "createdAt" | "status" | "logPath" + >, +) => { + const previewDeployment = await findPreviewDeploymentById( + deployment.previewDeploymentId, + ); + try { + await removeLastTenPreviewDeploymenById( + deployment.previewDeploymentId, + previewDeployment?.application?.serverId, + ); + + const appName = `${previewDeployment.appName}`; + const { LOGS_PATH } = paths(!!previewDeployment?.application?.serverId); + const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); + const fileName = `${appName}-${formattedDateTime}.log`; + const logFilePath = path.join(LOGS_PATH, appName, fileName); + + if (previewDeployment?.application?.serverId) { + const server = await findServerById( + previewDeployment?.application?.serverId, + ); + + const command = ` + mkdir -p ${LOGS_PATH}/${appName}; + echo "Initializing deployment" >> ${logFilePath}; + `; + + await execAsyncRemote(server.serverId, command); + } else { + await fsPromises.mkdir(path.join(LOGS_PATH, appName), { + recursive: true, + }); + await fsPromises.writeFile(logFilePath, "Initializing deployment"); + } + + const deploymentCreate = await db + .insert(deployments) + .values({ + title: deployment.title || "Deployment", + status: "running", + logPath: logFilePath, + description: deployment.description || "", + previewDeploymentId: deployment.previewDeploymentId, + }) + .returning(); + if (deploymentCreate.length === 0 || !deploymentCreate[0]) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to create the deployment", + }); + } + return deploymentCreate[0]; + } catch (error) { + await updatePreviewDeployment(deployment.previewDeploymentId, { + previewStatus: "error", + }); + console.log(error); + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to create the deployment", + }); + } +}; + export const createDeploymentCompose = async ( deployment: Omit< typeof apiCreateDeploymentCompose._type, @@ -257,6 +331,41 @@ const removeLastTenComposeDeployments = async ( } }; +export const removeLastTenPreviewDeploymenById = async ( + previewDeploymentId: string, + serverId: string | null, +) => { + const deploymentList = await db.query.deployments.findMany({ + where: eq(deployments.previewDeploymentId, previewDeploymentId), + orderBy: desc(deployments.createdAt), + }); + + if (deploymentList.length > 10) { + const deploymentsToDelete = deploymentList.slice(10); + if (serverId) { + let command = ""; + for (const oldDeployment of deploymentsToDelete) { + const logPath = path.join(oldDeployment.logPath); + + command += ` + rm -rf ${logPath}; + `; + await removeDeployment(oldDeployment.deploymentId); + } + + await execAsyncRemote(serverId, command); + } else { + for (const oldDeployment of deploymentsToDelete) { + const logPath = path.join(oldDeployment.logPath); + if (existsSync(logPath)) { + await fsPromises.unlink(logPath); + } + await removeDeployment(oldDeployment.deploymentId); + } + } + } +}; + export const removeDeployments = async (application: Application) => { const { appName, applicationId } = application; const { LOGS_PATH } = paths(!!application.serverId); @@ -269,6 +378,30 @@ export const removeDeployments = async (application: Application) => { await removeDeploymentsByApplicationId(applicationId); }; +export const removeDeploymentsByPreviewDeploymentId = async ( + previewDeployment: PreviewDeployment, + serverId: string | null, +) => { + const { appName } = previewDeployment; + const { LOGS_PATH } = paths(!!serverId); + const logsPath = path.join(LOGS_PATH, appName); + if (serverId) { + await execAsyncRemote(serverId, `rm -rf ${logsPath}`); + } else { + await removeDirectoryIfExistsContent(logsPath); + } + + await db + .delete(deployments) + .where( + eq( + deployments.previewDeploymentId, + previewDeployment.previewDeploymentId, + ), + ) + .returning(); +}; + export const removeDeploymentsByComposeId = async (compose: Compose) => { const { appName } = compose; const { LOGS_PATH } = paths(!!compose.serverId); diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index d5a40fe03..6ac613542 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -110,7 +110,7 @@ export const getContainersByAppNameMatch = async ( const command = appType === "docker-compose" ? `${cmd} --filter='label=com.docker.compose.project=${appName}'` - : `${cmd} | grep ${appName}`; + : `${cmd} | grep '^.*Name: ${appName}'`; if (serverId) { const { stdout, stderr } = await execAsyncRemote(serverId, command); diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index 28dd3ba24..b99c4869d 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -134,3 +134,7 @@ export const removeDomainById = async (domainId: string) => { return result[0]; }; + +export const getDomainHost = (domain: Domain) => { + return `${domain.https ? "https" : "http"}://${domain.host}`; +}; diff --git a/packages/server/src/services/github.ts b/packages/server/src/services/github.ts index a7317bc72..17ec7d720 100644 --- a/packages/server/src/services/github.ts +++ b/packages/server/src/services/github.ts @@ -6,6 +6,7 @@ import { } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; +import { authGithub } from "../utils/providers/github"; export type Github = typeof github.$inferSelect; export const createGithub = async ( @@ -72,3 +73,56 @@ export const updateGithub = async ( .returning() .then((response) => response[0]); }; + +export const getIssueComment = ( + appName: string, + status: "success" | "error" | "running" | "initializing", + previewDomain: string, +) => { + let statusMessage = ""; + if (status === "success") { + statusMessage = "✅ Done"; + } else if (status === "error") { + statusMessage = "❌ Failed"; + } else if (status === "initializing") { + statusMessage = "🔄 Building"; + } else { + statusMessage = "🔄 Building"; + } + const finished = ` +| Name | Status | Preview | Updated (UTC) | +|------------|--------------|-------------------------------------|-----------------------| +| ${appName} | ${statusMessage} | [Preview URL](${previewDomain}) | ${new Date().toISOString()} | +`; + + return finished; +}; + +interface Comment { + owner: string; + repository: string; + issue_number: string; + body: string; + comment_id: number; + githubId: string; +} + +export const updateIssueComment = async ({ + owner, + repository, + issue_number, + body, + comment_id, + githubId, +}: Comment) => { + const github = await findGithubById(githubId); + const octokit = authGithub(github); + + await octokit.rest.issues.updateComment({ + owner: owner || "", + repo: repository || "", + issue_number: issue_number, + body, + comment_id: comment_id, + }); +}; diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts new file mode 100644 index 000000000..59788adc6 --- /dev/null +++ b/packages/server/src/services/preview-deployment.ts @@ -0,0 +1,285 @@ +import { db } from "@dokploy/server/db"; +import { + type apiCreatePreviewDeployment, + deployments, + previewDeployments, +} from "@dokploy/server/db/schema"; +import { TRPCError } from "@trpc/server"; +import { and, desc, eq } from "drizzle-orm"; +import { slugify } from "../setup/server-setup"; +import { findApplicationById } from "./application"; +import { createDomain } from "./domain"; +import { generatePassword, generateRandomDomain } from "../templates/utils"; +import { manageDomain } from "../utils/traefik/domain"; +import { + removeDeployments, + removeDeploymentsByPreviewDeploymentId, +} from "./deployment"; +import { removeDirectoryCode } from "../utils/filesystem/directory"; +import { removeTraefikConfig } from "../utils/traefik/application"; +import { removeService } from "../utils/docker/utils"; +import { authGithub } from "../utils/providers/github"; +import { getIssueComment, type Github } from "./github"; +import { findAdminById } from "./admin"; + +export type PreviewDeployment = typeof previewDeployments.$inferSelect; + +export const findPreviewDeploymentById = async ( + previewDeploymentId: string, +) => { + const application = await db.query.previewDeployments.findFirst({ + where: eq(previewDeployments.previewDeploymentId, previewDeploymentId), + with: { + domain: true, + application: { + with: { + server: true, + project: true, + }, + }, + }, + }); + if (!application) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Preview Deployment not found", + }); + } + return application; +}; + +export const findApplicationByPreview = async (applicationId: string) => { + const application = await db.query.applications.findFirst({ + with: { + previewDeployments: { + where: eq(previewDeployments.applicationId, applicationId), + }, + project: true, + domains: true, + deployments: true, + mounts: true, + redirects: true, + security: true, + ports: true, + registry: true, + gitlab: true, + github: true, + bitbucket: true, + server: true, + }, + }); + + if (!application) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Applicationnot found", + }); + } + return application; +}; + +export const removePreviewDeployment = async (previewDeploymentId: string) => { + try { + const application = await findApplicationByPreview(previewDeploymentId); + const previewDeployment = + await findPreviewDeploymentById(previewDeploymentId); + + const deployment = await db + .delete(previewDeployments) + .where(eq(previewDeployments.previewDeploymentId, previewDeploymentId)) + .returning(); + + application.appName = previewDeployment.appName; + const cleanupOperations = [ + async () => + await removeDeploymentsByPreviewDeploymentId( + previewDeployment, + application.serverId, + ), + async () => + await removeDirectoryCode(application.appName, application.serverId), + async () => + await removeTraefikConfig(application.appName, application.serverId), + async () => + await removeService(application?.appName, application.serverId), + ]; + for (const operation of cleanupOperations) { + try { + await operation(); + } catch (error) {} + } + return deployment[0]; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to delete this preview deployment", + }); + } +}; +// testing-tesoitnmg-ddq0ul-preview-ihl44o +export const updatePreviewDeployment = async ( + previewDeploymentId: string, + previewDeploymentData: Partial, +) => { + const application = await db + .update(previewDeployments) + .set({ + ...previewDeploymentData, + }) + .where(eq(previewDeployments.previewDeploymentId, previewDeploymentId)) + .returning(); + + return application; +}; + +export const findPreviewDeploymentsByApplicationId = async ( + applicationId: string, +) => { + const deploymentsList = await db.query.previewDeployments.findMany({ + where: eq(previewDeployments.applicationId, applicationId), + orderBy: desc(previewDeployments.createdAt), + with: { + deployments: { + orderBy: desc(deployments.createdAt), + }, + domain: true, + }, + }); + return deploymentsList; +}; + +export const createPreviewDeployment = async ( + schema: typeof apiCreatePreviewDeployment._type, +) => { + const application = await findApplicationById(schema.applicationId); + const appName = `preview-${application.appName}-${generatePassword(6)}`; + + const generateDomain = await generateWildcardDomain( + application.previewWildcard || "*.traefik.me", + appName, + application.server?.ipAddress || "", + application.project.adminId, + ); + + const octokit = authGithub(application?.github as Github); + + const runningComment = getIssueComment( + application.name, + "initializing", + generateDomain, + ); + + const issue = await octokit.rest.issues.createComment({ + owner: application?.owner || "", + repo: application?.repository || "", + issue_number: Number.parseInt(schema.pullRequestNumber), + body: `### Dokploy Preview Deployment\n\n${runningComment}`, + }); + + const previewDeployment = await db + .insert(previewDeployments) + .values({ + ...schema, + appName: appName, + pullRequestCommentId: `${issue.data.id}`, + }) + .returning() + .then((value) => value[0]); + + if (!previewDeployment) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to create the preview deployment", + }); + } + + const newDomain = await createDomain({ + host: generateDomain, + path: application.previewPath, + port: application.previewPort, + https: application.previewHttps, + certificateType: application.previewCertificateType, + domainType: "preview", + previewDeploymentId: previewDeployment.previewDeploymentId, + }); + + application.appName = appName; + + console.log(application); + + await manageDomain(application, newDomain); + + await db + .update(previewDeployments) + .set({ + domainId: newDomain.domainId, + }) + .where( + eq( + previewDeployments.previewDeploymentId, + previewDeployment.previewDeploymentId, + ), + ); + + return previewDeployment; +}; + +export const findPreviewDeploymentsByPullRequestId = async ( + pullRequestId: string, +) => { + const previewDeploymentResult = await db.query.previewDeployments.findMany({ + where: eq(previewDeployments.pullRequestId, pullRequestId), + }); + + return previewDeploymentResult; +}; + +export const findPreviewDeploymentByApplicationId = async ( + applicationId: string, + pullRequestId: string, +) => { + const previewDeploymentResult = await db.query.previewDeployments.findFirst({ + where: and( + eq(previewDeployments.applicationId, applicationId), + eq(previewDeployments.pullRequestId, pullRequestId), + ), + }); + + return previewDeploymentResult; +}; + +const generateWildcardDomain = async ( + baseDomain: string, + appName: string, + serverIp: string, + adminId: string, +): Promise => { + if (!baseDomain.startsWith("*.")) { + throw new Error('The base domain must start with "*."'); + } + const hash = `${appName}`; + if (baseDomain.includes("traefik.me")) { + let ip = ""; + + if (process.env.NODE_ENV === "development") { + ip = "127.0.0.1"; + } + + if (serverIp) { + ip = serverIp; + } + + if (!ip) { + const admin = await findAdminById(adminId); + ip = admin?.serverIp || ""; + } + + const slugIp = ip.replaceAll(".", "-"); + return baseDomain.replace( + "*", + `${hash}${slugIp === "" ? "" : `-${slugIp}`}`, + ); + } + + return baseDomain.replace("*", hash); +}; diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index 702121d2d..1cdc9787c 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -17,6 +17,7 @@ import { buildHeroku, getHerokuCommand } from "./heroku"; import { buildNixpacks, getNixpacksCommand } from "./nixpacks"; import { buildPaketo, getPaketoCommand } from "./paketo"; import { buildStatic, getStaticCommand } from "./static"; +import { nanoid } from "nanoid"; // NIXPACKS codeDirectory = where is the path of the code directory // HEROKU codeDirectory = where is the path of the code directory @@ -33,6 +34,7 @@ export type ApplicationNested = InferResultType< project: true; } >; + export const buildApplication = async ( application: ApplicationNested, logPath: string, diff --git a/packages/server/src/utils/builders/nixpacks.ts b/packages/server/src/utils/builders/nixpacks.ts index 7c10e4c07..56560e4e2 100644 --- a/packages/server/src/utils/builders/nixpacks.ts +++ b/packages/server/src/utils/builders/nixpacks.ts @@ -14,7 +14,7 @@ export const buildNixpacks = async ( application: ApplicationNested, writeStream: WriteStream, ) => { - const { env, appName, publishDirectory, serverId } = application; + const { env, appName, publishDirectory } = application; const buildAppDirectory = getBuildAppDirectory(application); const buildContainerId = `${appName}-${nanoid(10)}`; diff --git a/packages/server/src/utils/providers/github.ts b/packages/server/src/utils/providers/github.ts index a63a822df..c366eeba5 100644 --- a/packages/server/src/utils/providers/github.ts +++ b/packages/server/src/utils/providers/github.ts @@ -74,11 +74,22 @@ export type ApplicationWithGithub = InferResultType< >; export type ComposeWithGithub = InferResultType<"compose", { github: true }>; -export const cloneGithubRepository = async ( - entity: ApplicationWithGithub | ComposeWithGithub, - logPath: string, - isCompose = false, -) => { + +interface CloneGithubRepository { + appName: string; + owner: string | null; + branch: string | null; + githubId: string | null; + repository: string | null; + logPath: string; + type?: "application" | "compose"; +} +export const cloneGithubRepository = async ({ + logPath, + type = "application", + ...entity +}: CloneGithubRepository) => { + const isCompose = type === "compose"; const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(); const writeStream = createWriteStream(logPath, { flags: "a" }); const { appName, repository, owner, branch, githubId } = entity; @@ -145,13 +156,13 @@ export const cloneGithubRepository = async ( } }; -export const getGithubCloneCommand = async ( - entity: ApplicationWithGithub | ComposeWithGithub, - logPath: string, - isCompose = false, -) => { +export const getGithubCloneCommand = async ({ + logPath, + type = "application", + ...entity +}: CloneGithubRepository & { serverId: string }) => { const { appName, repository, owner, branch, githubId, serverId } = entity; - + const isCompose = type === "compose"; if (!serverId) { throw new TRPCError({ code: "NOT_FOUND", From fd0a472468cce82b235a9b869927a5b62c910d25 Mon Sep 17 00:00:00 2001 From: 190km Date: Tue, 3 Dec 2024 02:20:20 +0100 Subject: [PATCH 08/52] feat/fix: fixed stop button & added start button --- .../dashboard/compose/general/actions.tsx | 6 +- .../dashboard/compose/start-compose.tsx | 65 +++++ .../dashboard/compose/stop-compose.tsx | 65 +++++ apps/dokploy/pages/api/deploy/github.ts | 266 +++++++++--------- apps/dokploy/server/api/routers/compose.ts | 15 + packages/server/src/services/compose.ts | 30 ++ 6 files changed, 313 insertions(+), 134 deletions(-) create mode 100644 apps/dokploy/components/dashboard/compose/start-compose.tsx create mode 100644 apps/dokploy/components/dashboard/compose/stop-compose.tsx diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index 365e37f51..140b8d96b 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -14,6 +14,7 @@ import { CheckCircle2, ExternalLink, Globe, Terminal } from "lucide-react"; import Link from "next/link"; import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; +import { StartCompose } from "../start-compose"; import { DeployCompose } from "./deploy-compose"; import { RedbuildCompose } from "./rebuild-compose"; import { StopCompose } from "./stop-compose"; @@ -71,7 +72,10 @@ export const ComposeActions = ({ composeId }: Props) => { Autodeploy {data?.autoDeploy && } - {data?.composeType === "docker-compose" && ( + {data?.composeType === "docker-compose" && + data?.composeStatus === "idle" ? ( + + ) : ( )} diff --git a/apps/dokploy/components/dashboard/compose/start-compose.tsx b/apps/dokploy/components/dashboard/compose/start-compose.tsx new file mode 100644 index 000000000..20f990bb3 --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/start-compose.tsx @@ -0,0 +1,65 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { Button } from "@/components/ui/button"; +import { api } from "@/utils/api"; +import { CheckCircle2 } from "lucide-react"; +import { toast } from "sonner"; + +interface Props { + composeId: string; +} + +export const StartCompose = ({ composeId }: Props) => { + const { mutateAsync, isLoading } = api.compose.start.useMutation(); + const utils = api.useUtils(); + return ( + + + + + + + + Are you sure to start the compose? + + + This will start the compose + + + + Cancel + { + await mutateAsync({ + composeId, + }) + .then(async () => { + await utils.compose.one.invalidate({ + composeId, + }); + toast.success("Compose started succesfully"); + }) + .catch(() => { + toast.error("Error to start the Compose"); + }); + }} + > + Confirm + + + + + ); +}; diff --git a/apps/dokploy/components/dashboard/compose/stop-compose.tsx b/apps/dokploy/components/dashboard/compose/stop-compose.tsx new file mode 100644 index 000000000..3080e755a --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/stop-compose.tsx @@ -0,0 +1,65 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { Button } from "@/components/ui/button"; +import { api } from "@/utils/api"; +import { Ban } from "lucide-react"; +import { toast } from "sonner"; + +interface Props { + composeId: string; +} + +export const StopCompose = ({ composeId }: Props) => { + const { mutateAsync, isLoading } = api.compose.stop.useMutation(); + const utils = api.useUtils(); + return ( + + + + + + + + Are you absolutely sure to stop the compose? + + + This will stop the compose + + + + Cancel + { + await mutateAsync({ + composeId, + }) + .then(async () => { + await utils.compose.one.invalidate({ + composeId, + }); + toast.success("Compose stopped succesfully"); + }) + .catch(() => { + toast.error("Error to stop the Compose"); + }); + }} + > + Confirm + + + + + ); +}; diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 1d8c094af..08589fae1 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -10,138 +10,138 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; export default async function handler( - req: NextApiRequest, - res: NextApiResponse + req: NextApiRequest, + res: NextApiResponse, ) { - const signature = req.headers["x-hub-signature-256"]; - const githubBody = req.body; - - if (!githubBody?.installation?.id) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - const githubResult = await db.query.github.findFirst({ - where: eq(github.githubInstallationId, githubBody.installation.id), - }); - - if (!githubResult) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - if (!githubResult.githubWebhookSecret) { - res.status(400).json({ message: "Github Webhook Secret not set" }); - return; - } - const webhooks = new Webhooks({ - secret: githubResult.githubWebhookSecret, - }); - - const verified = await webhooks.verify( - JSON.stringify(githubBody), - signature as string - ); - - if (!verified) { - res.status(401).json({ message: "Unauthorized" }); - return; - } - - if (req.headers["x-github-event"] === "ping") { - res.status(200).json({ message: "Ping received, webhook is active" }); - return; - } - - if (req.headers["x-github-event"] !== "push") { - res.status(400).json({ message: "We only accept push events" }); - return; - } - - try { - const branchName = githubBody?.ref?.replace("refs/heads/", ""); - const repository = githubBody?.repository?.name; - const deploymentTitle = extractCommitMessage(req.headers, req.body); - const deploymentHash = extractHash(req.headers, req.body); - - const apps = await db.query.applications.findMany({ - where: and( - eq(applications.sourceType, "github"), - eq(applications.autoDeploy, true), - eq(applications.branch, branchName), - eq(applications.repository, repository) - ), - }); - - for (const app of apps) { - const jobData: DeploymentJob = { - applicationId: app.applicationId as string, - titleLog: deploymentTitle, - descriptionLog: `Hash: ${deploymentHash}`, - type: "deploy", - applicationType: "application", - server: !!app.serverId, - }; - - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const composeApps = await db.query.compose.findMany({ - where: and( - eq(compose.sourceType, "github"), - eq(compose.autoDeploy, true), - eq(compose.branch, branchName), - eq(compose.repository, repository) - ), - }); - - for (const composeApp of composeApps) { - const jobData: DeploymentJob = { - composeId: composeApp.composeId as string, - titleLog: deploymentTitle, - type: "deploy", - applicationType: "compose", - descriptionLog: `Hash: ${deploymentHash}`, - server: !!composeApp.serverId, - }; - - if (IS_CLOUD && composeApp.serverId) { - jobData.serverId = composeApp.serverId; - await deploy(jobData); - return true; - } - - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const totalApps = apps.length + composeApps.length; - const emptyApps = totalApps === 0; - - if (emptyApps) { - res.status(200).json({ message: "No apps to deploy" }); - return; - } - res.status(200).json({ message: `Deployed ${totalApps} apps` }); - } catch (error) { - res.status(400).json({ message: "Error To Deploy Application", error }); - } + const signature = req.headers["x-hub-signature-256"]; + const githubBody = req.body; + + if (!githubBody?.installation?.id) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + const githubResult = await db.query.github.findFirst({ + where: eq(github.githubInstallationId, githubBody.installation.id), + }); + + if (!githubResult) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + if (!githubResult.githubWebhookSecret) { + res.status(400).json({ message: "Github Webhook Secret not set" }); + return; + } + const webhooks = new Webhooks({ + secret: githubResult.githubWebhookSecret, + }); + + const verified = await webhooks.verify( + JSON.stringify(githubBody), + signature as string, + ); + + if (!verified) { + res.status(401).json({ message: "Unauthorized" }); + return; + } + + if (req.headers["x-github-event"] === "ping") { + res.status(200).json({ message: "Ping received, webhook is active" }); + return; + } + + if (req.headers["x-github-event"] !== "push") { + res.status(400).json({ message: "We only accept push events" }); + return; + } + + try { + const branchName = githubBody?.ref?.replace("refs/heads/", ""); + const repository = githubBody?.repository?.name; + const deploymentTitle = extractCommitMessage(req.headers, req.body); + const deploymentHash = extractHash(req.headers, req.body); + + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.autoDeploy, true), + eq(applications.branch, branchName), + eq(applications.repository, repository), + ), + }); + + for (const app of apps) { + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: deploymentTitle, + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application", + server: !!app.serverId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const composeApps = await db.query.compose.findMany({ + where: and( + eq(compose.sourceType, "github"), + eq(compose.autoDeploy, true), + eq(compose.branch, branchName), + eq(compose.repository, repository), + ), + }); + + for (const composeApp of composeApps) { + const jobData: DeploymentJob = { + composeId: composeApp.composeId as string, + titleLog: deploymentTitle, + type: "deploy", + applicationType: "compose", + descriptionLog: `Hash: ${deploymentHash}`, + server: !!composeApp.serverId, + }; + + if (IS_CLOUD && composeApp.serverId) { + jobData.serverId = composeApp.serverId; + await deploy(jobData); + return true; + } + + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const totalApps = apps.length + composeApps.length; + const emptyApps = totalApps === 0; + + if (emptyApps) { + res.status(200).json({ message: "No apps to deploy" }); + return; + } + res.status(200).json({ message: `Deployed ${totalApps} apps` }); + } catch (error) { + res.status(400).json({ message: "Error To Deploy Application", error }); + } } diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 257f374f2..6d04e815f 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -48,6 +48,7 @@ import { removeCompose, removeComposeDirectory, removeDeploymentsByComposeId, + startCompose, stopCompose, updateCompose, } from "@dokploy/server"; @@ -309,6 +310,20 @@ export const composeRouter = createTRPCRouter({ } await stopCompose(input.composeId); + return true; + }), + start: protectedProcedure + .input(apiFindCompose) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to stop this compose", + }); + } + await startCompose(input.composeId); + return true; }), getDefaultCommand: protectedProcedure diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 63d29539d..604c2150e 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -463,6 +463,36 @@ export const removeCompose = async (compose: Compose) => { return true; }; +export const startCompose = async (composeId: string) => { + const compose = await findComposeById(composeId); + try { + const { COMPOSE_PATH } = paths(!!compose.serverId); + if (compose.composeType === "docker-compose") { + if (compose.serverId) { + await execAsyncRemote( + compose.serverId, + `cd ${join(COMPOSE_PATH, compose.appName, "code")} && docker compose -p ${compose.appName} up -d`, + ); + } else { + await execAsync(`docker compose -p ${compose.appName} up -d`, { + cwd: join(COMPOSE_PATH, compose.appName, "code"), + }); + } + } + + await updateCompose(composeId, { + composeStatus: "done", + }); + } catch (error) { + await updateCompose(composeId, { + composeStatus: "idle", + }); + throw error; + } + + return true; +}; + export const stopCompose = async (composeId: string) => { const compose = await findComposeById(composeId); try { From 40c97b8e9cf4433e3fa9b5beed992a26ea22d6fe Mon Sep 17 00:00:00 2001 From: usopp Date: Tue, 3 Dec 2024 02:42:19 +0100 Subject: [PATCH 09/52] Update github.ts --- apps/dokploy/pages/api/deploy/github.ts | 266 ++++++++++++------------ 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 08589fae1..1d8c094af 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -10,138 +10,138 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; export default async function handler( - req: NextApiRequest, - res: NextApiResponse, + req: NextApiRequest, + res: NextApiResponse ) { - const signature = req.headers["x-hub-signature-256"]; - const githubBody = req.body; - - if (!githubBody?.installation?.id) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - const githubResult = await db.query.github.findFirst({ - where: eq(github.githubInstallationId, githubBody.installation.id), - }); - - if (!githubResult) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - if (!githubResult.githubWebhookSecret) { - res.status(400).json({ message: "Github Webhook Secret not set" }); - return; - } - const webhooks = new Webhooks({ - secret: githubResult.githubWebhookSecret, - }); - - const verified = await webhooks.verify( - JSON.stringify(githubBody), - signature as string, - ); - - if (!verified) { - res.status(401).json({ message: "Unauthorized" }); - return; - } - - if (req.headers["x-github-event"] === "ping") { - res.status(200).json({ message: "Ping received, webhook is active" }); - return; - } - - if (req.headers["x-github-event"] !== "push") { - res.status(400).json({ message: "We only accept push events" }); - return; - } - - try { - const branchName = githubBody?.ref?.replace("refs/heads/", ""); - const repository = githubBody?.repository?.name; - const deploymentTitle = extractCommitMessage(req.headers, req.body); - const deploymentHash = extractHash(req.headers, req.body); - - const apps = await db.query.applications.findMany({ - where: and( - eq(applications.sourceType, "github"), - eq(applications.autoDeploy, true), - eq(applications.branch, branchName), - eq(applications.repository, repository), - ), - }); - - for (const app of apps) { - const jobData: DeploymentJob = { - applicationId: app.applicationId as string, - titleLog: deploymentTitle, - descriptionLog: `Hash: ${deploymentHash}`, - type: "deploy", - applicationType: "application", - server: !!app.serverId, - }; - - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - } - - const composeApps = await db.query.compose.findMany({ - where: and( - eq(compose.sourceType, "github"), - eq(compose.autoDeploy, true), - eq(compose.branch, branchName), - eq(compose.repository, repository), - ), - }); - - for (const composeApp of composeApps) { - const jobData: DeploymentJob = { - composeId: composeApp.composeId as string, - titleLog: deploymentTitle, - type: "deploy", - applicationType: "compose", - descriptionLog: `Hash: ${deploymentHash}`, - server: !!composeApp.serverId, - }; - - if (IS_CLOUD && composeApp.serverId) { - jobData.serverId = composeApp.serverId; - await deploy(jobData); - return true; - } - - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - } - - const totalApps = apps.length + composeApps.length; - const emptyApps = totalApps === 0; - - if (emptyApps) { - res.status(200).json({ message: "No apps to deploy" }); - return; - } - res.status(200).json({ message: `Deployed ${totalApps} apps` }); - } catch (error) { - res.status(400).json({ message: "Error To Deploy Application", error }); - } + const signature = req.headers["x-hub-signature-256"]; + const githubBody = req.body; + + if (!githubBody?.installation?.id) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + const githubResult = await db.query.github.findFirst({ + where: eq(github.githubInstallationId, githubBody.installation.id), + }); + + if (!githubResult) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + if (!githubResult.githubWebhookSecret) { + res.status(400).json({ message: "Github Webhook Secret not set" }); + return; + } + const webhooks = new Webhooks({ + secret: githubResult.githubWebhookSecret, + }); + + const verified = await webhooks.verify( + JSON.stringify(githubBody), + signature as string + ); + + if (!verified) { + res.status(401).json({ message: "Unauthorized" }); + return; + } + + if (req.headers["x-github-event"] === "ping") { + res.status(200).json({ message: "Ping received, webhook is active" }); + return; + } + + if (req.headers["x-github-event"] !== "push") { + res.status(400).json({ message: "We only accept push events" }); + return; + } + + try { + const branchName = githubBody?.ref?.replace("refs/heads/", ""); + const repository = githubBody?.repository?.name; + const deploymentTitle = extractCommitMessage(req.headers, req.body); + const deploymentHash = extractHash(req.headers, req.body); + + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.autoDeploy, true), + eq(applications.branch, branchName), + eq(applications.repository, repository) + ), + }); + + for (const app of apps) { + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: deploymentTitle, + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application", + server: !!app.serverId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + } + + const composeApps = await db.query.compose.findMany({ + where: and( + eq(compose.sourceType, "github"), + eq(compose.autoDeploy, true), + eq(compose.branch, branchName), + eq(compose.repository, repository) + ), + }); + + for (const composeApp of composeApps) { + const jobData: DeploymentJob = { + composeId: composeApp.composeId as string, + titleLog: deploymentTitle, + type: "deploy", + applicationType: "compose", + descriptionLog: `Hash: ${deploymentHash}`, + server: !!composeApp.serverId, + }; + + if (IS_CLOUD && composeApp.serverId) { + jobData.serverId = composeApp.serverId; + await deploy(jobData); + return true; + } + + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + } + + const totalApps = apps.length + composeApps.length; + const emptyApps = totalApps === 0; + + if (emptyApps) { + res.status(200).json({ message: "No apps to deploy" }); + return; + } + res.status(200).json({ message: `Deployed ${totalApps} apps` }); + } catch (error) { + res.status(400).json({ message: "Error To Deploy Application", error }); + } } From d8787ec11d5c0780491f28f053068abdc776ec7f Mon Sep 17 00:00:00 2001 From: 190km Date: Tue, 3 Dec 2024 03:55:56 +0100 Subject: [PATCH 10/52] style: added autodeploy switch --- .../dashboard/application/general/show.tsx | 46 +++++++++---------- .../dashboard/compose/general/actions.tsx | 43 ++++++++--------- apps/dokploy/components/ui/switch.tsx | 6 ++- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 277ae1ebb..65c99e9b8 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -2,9 +2,9 @@ import { ShowBuildChooseForm } from "@/components/dashboard/application/build/sh import { ShowProviderForm } from "@/components/dashboard/application/general/generic/show"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Toggle } from "@/components/ui/toggle"; +import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { CheckCircle2, Terminal } from "lucide-react"; +import { Terminal } from "lucide-react"; import React from "react"; import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; @@ -39,27 +39,6 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { appName={data?.appName || ""} /> - { - await update({ - applicationId, - autoDeploy: enabled, - }) - .then(async () => { - toast.success("Auto Deploy Updated"); - await refetch(); - }) - .catch(() => { - toast.error("Error to update Auto Deploy"); - }); - }} - className="flex flex-row gap-2 items-center" - > - Autodeploy - {data?.autoDeploy && } - {data?.applicationStatus === "idle" ? ( @@ -75,6 +54,27 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { Open Terminal +
+ Autodeploy + { + await update({ + applicationId, + autoDeploy: enabled, + }) + .then(async () => { + toast.success("Auto Deploy Updated"); + await refetch(); + }) + .catch(() => { + toast.error("Error to update Auto Deploy"); + }); + }} + className="flex flex-row gap-2 items-center" + /> +
diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index 365e37f51..1531942f8 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -8,7 +8,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { Toggle } from "@/components/ui/toggle"; +import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { CheckCircle2, ExternalLink, Globe, Terminal } from "lucide-react"; import Link from "next/link"; @@ -50,26 +50,6 @@ export const ComposeActions = ({ composeId }: Props) => { return (
- { - await update({ - composeId, - autoDeploy: enabled, - }) - .then(async () => { - toast.success("Auto Deploy Updated"); - await refetch(); - }) - .catch(() => { - toast.error("Error to update Auto Deploy"); - }); - }} - className="flex flex-row gap-2 items-center" - > - Autodeploy {data?.autoDeploy && } - {data?.composeType === "docker-compose" && ( @@ -84,6 +64,27 @@ export const ComposeActions = ({ composeId }: Props) => { Open Terminal +
+ Autodeploy + { + await update({ + composeId, + autoDeploy: enabled, + }) + .then(async () => { + toast.success("Auto Deploy Updated"); + await refetch(); + }) + .catch(() => { + toast.error("Error to update Auto Deploy"); + }); + }} + className="flex flex-row gap-2 items-center" + /> +
{domains.length > 0 && ( diff --git a/apps/dokploy/components/ui/switch.tsx b/apps/dokploy/components/ui/switch.tsx index 96809024e..21cb85826 100644 --- a/apps/dokploy/components/ui/switch.tsx +++ b/apps/dokploy/components/ui/switch.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as SwitchPrimitives from "@radix-ui/react-switch"; import * as React from "react"; @@ -9,7 +11,7 @@ const Switch = React.forwardRef< >(({ className, ...props }, ref) => ( From c4c4b459cc423597e770fb75c4f605501345d780 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:26:52 -0600 Subject: [PATCH 11/52] fix: allow multiple repositories from same name github #690 --- apps/dokploy/pages/api/deploy/github.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 1d8c094af..7f0e7f0b0 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -63,13 +63,16 @@ export default async function handler( const repository = githubBody?.repository?.name; const deploymentTitle = extractCommitMessage(req.headers, req.body); const deploymentHash = extractHash(req.headers, req.body); + const owner = githubBody?.repository?.owner?.name; + const apps = await db.query.applications.findMany({ where: and( eq(applications.sourceType, "github"), eq(applications.autoDeploy, true), eq(applications.branch, branchName), - eq(applications.repository, repository) + eq(applications.repository, repository), + eq(applications.owner, owner) ), }); @@ -103,7 +106,8 @@ export default async function handler( eq(compose.sourceType, "github"), eq(compose.autoDeploy, true), eq(compose.branch, branchName), - eq(compose.repository, repository) + eq(compose.repository, repository), + eq(compose.owner, owner) ), }); From 00c7ae3f407c125e5ef75793ae060e1089d0521c Mon Sep 17 00:00:00 2001 From: Daniel Gietmann Date: Tue, 3 Dec 2024 20:48:46 +0100 Subject: [PATCH 12/52] Updated Umami to v2.14.0 --- apps/dokploy/templates/templates.ts | 2 +- apps/dokploy/templates/umami/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 6b7c5d6c7..878d01763 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -380,7 +380,7 @@ export const templates: TemplateData[] = [ { id: "umami", name: "Umami", - version: "v2.12.1", + version: "v2.14.0", description: "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", logo: "umami.png", diff --git a/apps/dokploy/templates/umami/docker-compose.yml b/apps/dokploy/templates/umami/docker-compose.yml index 228762100..191c4803d 100644 --- a/apps/dokploy/templates/umami/docker-compose.yml +++ b/apps/dokploy/templates/umami/docker-compose.yml @@ -1,6 +1,6 @@ services: umami: - image: ghcr.io/umami-software/umami:postgresql-v2.13.2 + image: ghcr.io/umami-software/umami:postgresql-v2.14.0 restart: always healthcheck: test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] From cbbbe44802157be0c89455c070e162f2670d8e7d Mon Sep 17 00:00:00 2001 From: Pedro Ramon Date: Wed, 4 Dec 2024 07:39:36 -0300 Subject: [PATCH 13/52] feat(i18n): add portuguese language support --- .../dashboard/settings/appearance-form.tsx | 3 +- apps/dokploy/next-i18next.config.cjs | 1 + apps/dokploy/pages/_app.tsx | 1 + apps/dokploy/public/locales/pt-br/common.json | 1 + .../public/locales/pt-br/settings.json | 44 +++++++++++++++++++ apps/dokploy/utils/hooks/use-locale.ts | 1 + 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 apps/dokploy/public/locales/pt-br/common.json create mode 100644 apps/dokploy/public/locales/pt-br/settings.json diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index 2a3f5132b..e9edbfe10 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -38,7 +38,7 @@ const appearanceFormSchema = z.object({ required_error: "Please select a theme.", }), language: z.enum( - ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa", "ko"], + ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa", "ko", "pt-br"], { required_error: "Please select a language.", }, @@ -186,6 +186,7 @@ export function AppearanceForm() { { label: "Türkçe", value: "tr" }, { label: "Persian", value: "fa" }, { label: "한국어", value: "ko" }, + { label: "Português", value: "pt-br" }, ].map((preset) => ( {preset.label} diff --git a/apps/dokploy/next-i18next.config.cjs b/apps/dokploy/next-i18next.config.cjs index adf741e91..ac4a5d06d 100644 --- a/apps/dokploy/next-i18next.config.cjs +++ b/apps/dokploy/next-i18next.config.cjs @@ -13,6 +13,7 @@ module.exports = { "zh-Hans", "fa", "ko", + "pt-br", ], localeDetection: false, }, diff --git a/apps/dokploy/pages/_app.tsx b/apps/dokploy/pages/_app.tsx index 17b986c57..d7034c97b 100644 --- a/apps/dokploy/pages/_app.tsx +++ b/apps/dokploy/pages/_app.tsx @@ -82,6 +82,7 @@ export default api.withTRPC( "zh-Hans", "fa", "ko", + "pt-br", ], localeDetection: false, }, diff --git a/apps/dokploy/public/locales/pt-br/common.json b/apps/dokploy/public/locales/pt-br/common.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/apps/dokploy/public/locales/pt-br/common.json @@ -0,0 +1 @@ +{} diff --git a/apps/dokploy/public/locales/pt-br/settings.json b/apps/dokploy/public/locales/pt-br/settings.json new file mode 100644 index 000000000..fc964be6c --- /dev/null +++ b/apps/dokploy/public/locales/pt-br/settings.json @@ -0,0 +1,44 @@ +{ + "settings.common.save": "Salvar", + "settings.server.domain.title": "Domínio do Servidor", + "settings.server.domain.description": "Configure o domínio do servidor", + "settings.server.domain.form.domain": "Domínio", + "settings.server.domain.form.letsEncryptEmail": "Email do Let's Encrypt", + "settings.server.domain.form.certificate.label": "Certificado", + "settings.server.domain.form.certificate.placeholder": "Selecione um Certificado", + "settings.server.domain.form.certificateOptions.none": "Nenhum", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Padrão)", + + "settings.server.webServer.title": "Servidor web", + "settings.server.webServer.description": "Limpar e recarregar servidor web.", + "settings.server.webServer.actions": "Ações", + "settings.server.webServer.reload": "Recarregar", + "settings.server.webServer.watchLogs": "Ver logs", + "settings.server.webServer.updateServerIp": "Atualizar IP do Servidor", + "settings.server.webServer.server.label": "Servidor", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "Alterar Env", + "settings.server.webServer.storage.label": "Armazenamento", + "settings.server.webServer.storage.cleanUnusedImages": "Limpar imagens não utilizadas", + "settings.server.webServer.storage.cleanUnusedVolumes": "Limpar volumes não utilizados", + "settings.server.webServer.storage.cleanStoppedContainers": "Limpar containers parados", + "settings.server.webServer.storage.cleanDockerBuilder": "Limpar Docker Builder & System", + "settings.server.webServer.storage.cleanMonitoring": "Limpar Monitoramento", + "settings.server.webServer.storage.cleanAll": "Limpar Tudo", + + "settings.profile.title": "Conta", + "settings.profile.description": "Altere os detalhes do seu perfil aqui.", + "settings.profile.email": "Email", + "settings.profile.password": "Senha", + "settings.profile.avatar": "Avatar", + + "settings.appearance.title": "Aparência", + "settings.appearance.description": "Personalize o tema do seu dashboard.", + "settings.appearance.theme": "Tema", + "settings.appearance.themeDescription": "Selecione um tema para o dashboard", + "settings.appearance.themes.light": "Claro", + "settings.appearance.themes.dark": "Escuro", + "settings.appearance.themes.system": "Automático", + "settings.appearance.language": "Linguagem", + "settings.appearance.languageDescription": "Selecione o idioma do dashboard" +} diff --git a/apps/dokploy/utils/hooks/use-locale.ts b/apps/dokploy/utils/hooks/use-locale.ts index eaeb1612f..5a2ec44b0 100644 --- a/apps/dokploy/utils/hooks/use-locale.ts +++ b/apps/dokploy/utils/hooks/use-locale.ts @@ -11,6 +11,7 @@ const SUPPORTED_LOCALES = [ "zh-Hans", "fa", "ko", + "pt-br", ] as const; type Locale = (typeof SUPPORTED_LOCALES)[number]; From 14573f90f7a052fb399da0c9c339249b1786a1c1 Mon Sep 17 00:00:00 2001 From: Shahriar <31452340+ShahriarKh@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:21:07 +0330 Subject: [PATCH 14/52] fix: small typo --- .../dashboard/settings/notifications/add-notification.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/settings/notifications/add-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/add-notification.tsx index 77621bec8..74a933a69 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/add-notification.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/add-notification.tsx @@ -667,7 +667,7 @@ export const AddNotification = () => {
Dokploy Restart - Trigger the action when a dokploy is restarted. + Trigger the action when dokploy is restarted.
From c9d36160882abcf21367a0bb94ae552fcfb8c89d Mon Sep 17 00:00:00 2001 From: yerkow Date: Thu, 5 Dec 2024 10:17:54 +0500 Subject: [PATCH 15/52] feat(i18n): add kazakh language support --- .../dashboard/settings/appearance-form.tsx | 2 +- apps/dokploy/next-i18next.config.cjs | 37 +++++++++-------- apps/dokploy/pages/_app.tsx | 1 + apps/dokploy/public/locales/kz/common.json | 1 + apps/dokploy/public/locales/kz/settings.json | 41 +++++++++++++++++++ apps/dokploy/utils/hooks/use-locale.ts | 1 + 6 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 apps/dokploy/public/locales/kz/common.json create mode 100644 apps/dokploy/public/locales/kz/settings.json diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index 2a3f5132b..f128948d7 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -38,7 +38,7 @@ const appearanceFormSchema = z.object({ required_error: "Please select a theme.", }), language: z.enum( - ["en", "pl", "ru", "fr", "de", "tr", "zh-Hant", "zh-Hans", "fa", "ko"], + ["en", "pl", "ru", "fr", "de", "tr", "kz", "zh-Hant", "zh-Hans", "fa", "ko"], { required_error: "Please select a language.", }, diff --git a/apps/dokploy/next-i18next.config.cjs b/apps/dokploy/next-i18next.config.cjs index adf741e91..d044c0c11 100644 --- a/apps/dokploy/next-i18next.config.cjs +++ b/apps/dokploy/next-i18next.config.cjs @@ -1,21 +1,22 @@ /** @type {import('next-i18next').UserConfig} */ module.exports = { - i18n: { - defaultLocale: "en", - locales: [ - "en", - "pl", - "ru", - "fr", - "de", - "tr", - "zh-Hant", - "zh-Hans", - "fa", - "ko", - ], - localeDetection: false, - }, - fallbackLng: "en", - keySeparator: false, + i18n: { + defaultLocale: "en", + locales: [ + "en", + "pl", + "ru", + "fr", + "de", + "tr", + "kz", + "zh-Hant", + "zh-Hans", + "fa", + "ko", + ], + localeDetection: false, + }, + fallbackLng: "en", + keySeparator: false, }; diff --git a/apps/dokploy/pages/_app.tsx b/apps/dokploy/pages/_app.tsx index 17b986c57..2f16f8886 100644 --- a/apps/dokploy/pages/_app.tsx +++ b/apps/dokploy/pages/_app.tsx @@ -78,6 +78,7 @@ export default api.withTRPC( "fr", "de", "tr", + "kz", "zh-Hant", "zh-Hans", "fa", diff --git a/apps/dokploy/public/locales/kz/common.json b/apps/dokploy/public/locales/kz/common.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/apps/dokploy/public/locales/kz/common.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/apps/dokploy/public/locales/kz/settings.json b/apps/dokploy/public/locales/kz/settings.json new file mode 100644 index 000000000..97403d0e2 --- /dev/null +++ b/apps/dokploy/public/locales/kz/settings.json @@ -0,0 +1,41 @@ +{ + "settings.common.save": "Сақтау", + "settings.server.domain.title": "Сервер домені", + "settings.server.domain.description": "Dokploy сервер қолданбасына домен енгізіңіз.", + "settings.server.domain.form.domain": "Домен", + "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Эл. поштасы", + "settings.server.domain.form.certificate.label": "Сертификат", + "settings.server.domain.form.certificate.placeholder": "Сертификатты таңдаңыз", + "settings.server.domain.form.certificateOptions.none": "Жоқ", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Стандартты)", + "settings.server.webServer.title": "Веб-Сервер", + "settings.server.webServer.description": "Веб-серверді қайта жүктеу немесе тазалау.", + "settings.server.webServer.actions": "Әрекеттер", + "settings.server.webServer.reload": "Қайта жүктеу", + "settings.server.webServer.watchLogs": "Журналдарды қарау", + "settings.server.webServer.updateServerIp": "Сервердің IP жаңарту", + "settings.server.webServer.server.label": "Сервер", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "Env Өзгерту", + "settings.server.webServer.storage.label": "Диск кеңістігі", + "settings.server.webServer.storage.cleanUnusedImages": "Пайдаланылмаған образды тазалау", + "settings.server.webServer.storage.cleanUnusedVolumes": "Пайдаланылмаған томды тазалау", + "settings.server.webServer.storage.cleanStoppedContainers": "Тоқтатылған контейнерлерді тазалау", + "settings.server.webServer.storage.cleanDockerBuilder": "Docker Builder & Системаны тазалау", + "settings.server.webServer.storage.cleanMonitoring": "Мониторингті тазалау", + "settings.server.webServer.storage.cleanAll": "Барлығын тазалау", + "settings.profile.title": "Аккаунт", + "settings.profile.description": "Профиль мәліметтерін осы жерден өзгертіңіз.", + "settings.profile.email": "Эл. пошта", + "settings.profile.password": "Құпия сөз", + "settings.profile.avatar": "Аватар", + "settings.appearance.title": "Сыртқы түрі", + "settings.appearance.description": "Dokploy сыртқы келбетін өзгерту.", + "settings.appearance.theme": "Келбеті", + "settings.appearance.themeDescription": "Жүйе тақтасының келбетің таңдаңыз", + "settings.appearance.themes.light": "Жарық", + "settings.appearance.themes.dark": "Қараңғы", + "settings.appearance.themes.system": "Жүйелік", + "settings.appearance.language": "Тіл", + "settings.appearance.languageDescription": "Жүйе тақтасының тілің таңдаңыз" +} diff --git a/apps/dokploy/utils/hooks/use-locale.ts b/apps/dokploy/utils/hooks/use-locale.ts index eaeb1612f..df30b556c 100644 --- a/apps/dokploy/utils/hooks/use-locale.ts +++ b/apps/dokploy/utils/hooks/use-locale.ts @@ -7,6 +7,7 @@ const SUPPORTED_LOCALES = [ "fr", "de", "tr", + "kz", "zh-Hant", "zh-Hans", "fa", From 27f43e774a3063e35403a84295823afff4568313 Mon Sep 17 00:00:00 2001 From: yerkow Date: Thu, 5 Dec 2024 10:39:10 +0500 Subject: [PATCH 16/52] fix: kz label --- apps/dokploy/components/dashboard/settings/appearance-form.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index f128948d7..85db83772 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -184,6 +184,7 @@ export function AppearanceForm() { { label: "繁體中文", value: "zh-Hant" }, { label: "简体中文", value: "zh-Hans" }, { label: "Türkçe", value: "tr" }, + { label: "Қазақ", value: "tr" }, { label: "Persian", value: "fa" }, { label: "한국어", value: "ko" }, ].map((preset) => ( From b9faf4bd1a5607e9649cf1fb35d1d76bc0a3ecd4 Mon Sep 17 00:00:00 2001 From: 190km Date: Thu, 5 Dec 2024 23:39:03 +0100 Subject: [PATCH 17/52] feat: add prevent dialog when leaving a terminal --- .../docker/terminal/docker-terminal-modal.tsx | 120 +++++++---- .../web-server/docker-terminal-modal.tsx | 204 +++++++++++------- 2 files changed, 201 insertions(+), 123 deletions(-) diff --git a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx index 876d6838b..f0f8ca6ad 100644 --- a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx @@ -1,56 +1,94 @@ +import { Button } from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import dynamic from "next/dynamic"; +import { useState } from "react"; const Terminal = dynamic( - () => import("./docker-terminal").then((e) => e.DockerTerminal), - { - ssr: false, - }, + () => import("./docker-terminal").then((e) => e.DockerTerminal), + { + ssr: false, + } ); interface Props { - containerId: string; - serverId?: string; - children?: React.ReactNode; + containerId: string; + serverId?: string; + children?: React.ReactNode; } export const DockerTerminalModal = ({ - children, - containerId, - serverId, + children, + containerId, + serverId, }: Props) => { - return ( - - - e.preventDefault()} - > - {children} - - - - - Docker Terminal - - Easy way to access to docker container - - + const [mainDialogOpen, setMainDialogOpen] = useState(false); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); - - - - ); + const handleMainDialogOpenChange = (open: boolean) => { + if (!open) { + setConfirmDialogOpen(true); + } else { + setMainDialogOpen(true); + } + }; + + const handleConfirm = () => { + setConfirmDialogOpen(false); + setMainDialogOpen(false); + }; + + const handleCancel = () => { + setConfirmDialogOpen(false); + }; + return ( + + + e.preventDefault()} + > + {children} + + + + + Docker Terminal + + Easy way to access to docker container + + + + + + + + Are you sure you want to close the terminal? + + By clicking the confirm button, the terminal will be closed. + + + + + + + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx index f38ecf128..ba3253a4a 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx @@ -1,20 +1,22 @@ +import { Button } from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, } from "@/components/ui/select"; import { api } from "@/utils/api"; import { Loader2 } from "lucide-react"; @@ -23,80 +25,118 @@ import type React from "react"; import { useEffect, useState } from "react"; const Terminal = dynamic( - () => - import("@/components/dashboard/docker/terminal/docker-terminal").then( - (e) => e.DockerTerminal, - ), - { - ssr: false, - }, + () => + import("@/components/dashboard/docker/terminal/docker-terminal").then( + (e) => e.DockerTerminal + ), + { + ssr: false, + } ); interface Props { - appName: string; - children?: React.ReactNode; - serverId?: string; + appName: string; + children?: React.ReactNode; + serverId?: string; } export const DockerTerminalModal = ({ children, appName, serverId }: Props) => { - const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery( - { - appName, - serverId, - }, - { - enabled: !!appName, - }, - ); - const [containerId, setContainerId] = useState(); + const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery( + { + appName, + serverId, + }, + { + enabled: !!appName, + } + ); + const [containerId, setContainerId] = useState(); + const [mainDialogOpen, setMainDialogOpen] = useState(false); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); - useEffect(() => { - if (data && data?.length > 0) { - setContainerId(data[0]?.containerId); - } - }, [data]); - return ( - - {children} - - - Docker Terminal - - Easy way to access to docker container - - - - - - - - ); + const handleMainDialogOpenChange = (open: boolean) => { + if (!open) { + setConfirmDialogOpen(true); + } else { + setMainDialogOpen(true); + } + }; + + const handleConfirm = () => { + setConfirmDialogOpen(false); + setMainDialogOpen(false); + }; + + const handleCancel = () => { + setConfirmDialogOpen(false); + }; + + useEffect(() => { + if (data && data?.length > 0) { + setContainerId(data[0]?.containerId); + } + }, [data]); + + return ( + + {children} + + + Docker Terminal + + Easy way to access to docker container + + + + + + + + + + Are you sure you want to close the terminal? + + + By clicking the confirm button, the terminal will be closed. + + + + + + + + + + + ); }; From 7f53e9cf075903f49b72a6727676fdadefc19453 Mon Sep 17 00:00:00 2001 From: mafrasil Date: Fri, 6 Dec 2024 17:48:21 +0400 Subject: [PATCH 18/52] add trigger template --- apps/dokploy/public/templates/trigger.svg | 2 + apps/dokploy/templates/templates.ts | 1953 +++++++++-------- .../templates/trigger/docker-compose.yml | 107 + apps/dokploy/templates/trigger/index.ts | 56 + 4 files changed, 1149 insertions(+), 969 deletions(-) create mode 100644 apps/dokploy/public/templates/trigger.svg create mode 100644 apps/dokploy/templates/trigger/docker-compose.yml create mode 100644 apps/dokploy/templates/trigger/index.ts diff --git a/apps/dokploy/public/templates/trigger.svg b/apps/dokploy/public/templates/trigger.svg new file mode 100644 index 000000000..4e0957f44 --- /dev/null +++ b/apps/dokploy/public/templates/trigger.svg @@ -0,0 +1,2 @@ + +Trigger.dev logo \ No newline at end of file diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 878d01763..23fb26a96 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,975 +1,990 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.0", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.0", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.48.1", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "5.8.3", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.48.1", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "5.8.3", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file", "manager"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.3", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.dev/", - docs: "https://ryot.dev/docs/getting-started", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file", "manager"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.3", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.dev/", + docs: "https://ryot.dev/docs/getting-started", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "trigger", + name: "Trigger", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "trigger.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./trigger/index").then((m) => m.generate), + }, ]; diff --git a/apps/dokploy/templates/trigger/docker-compose.yml b/apps/dokploy/templates/trigger/docker-compose.yml new file mode 100644 index 000000000..424f912bd --- /dev/null +++ b/apps/dokploy/templates/trigger/docker-compose.yml @@ -0,0 +1,107 @@ +x-webapp-env: &webapp-env + LOGIN_ORIGIN: &trigger-url ${TRIGGER_PROTOCOL:-http}://${TRIGGER_DOMAIN:-localhost:3040} + APP_ORIGIN: *trigger-url + DEV_OTEL_EXPORTER_OTLP_ENDPOINT: &trigger-otel ${TRIGGER_PROTOCOL:-http}://${TRIGGER_DOMAIN:-localhost:3040}/otel + ELECTRIC_ORIGIN: http://electric:3000 + +x-worker-env: &worker-env + PLATFORM_HOST: webapp + PLATFORM_WS_PORT: 3030 + SECURE_CONNECTION: "false" + OTEL_EXPORTER_OTLP_ENDPOINT: *trigger-otel + +volumes: + postgres-data: + redis-data: + +networks: + webapp: + +services: + webapp: + image: ghcr.io/triggerdotdev/trigger.dev:${TRIGGER_IMAGE_TAG:-v3} + restart: ${RESTART_POLICY:-unless-stopped} + env_file: + - .env + environment: + <<: *webapp-env + ports: + - ${WEBAPP_PUBLISH_IP:-127.0.0.1}:3040:3030 + depends_on: + - postgres + - redis + networks: + - webapp + + postgres: + image: postgres:${POSTGRES_IMAGE_TAG:-16} + restart: ${RESTART_POLICY:-unless-stopped} + volumes: + - postgres-data:/var/lib/postgresql/data/ + env_file: + - .env + networks: + - webapp + ports: + - ${DOCKER_PUBLISH_IP:-127.0.0.1}:5433:5432 + command: + - -c + - wal_level=logical + + redis: + image: redis:${REDIS_IMAGE_TAG:-7} + restart: ${RESTART_POLICY:-unless-stopped} + volumes: + - redis-data:/data + networks: + - webapp + ports: + - ${DOCKER_PUBLISH_IP:-127.0.0.1}:6389:6379 + + docker-provider: + image: ghcr.io/triggerdotdev/provider/docker:${TRIGGER_IMAGE_TAG:-v3} + restart: ${RESTART_POLICY:-unless-stopped} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + user: root + networks: + - webapp + depends_on: + - webapp + ports: + - ${DOCKER_PUBLISH_IP:-127.0.0.1}:9021:9020 + env_file: + - .env + environment: + <<: *worker-env + PLATFORM_SECRET: $PROVIDER_SECRET + + coordinator: + image: ghcr.io/triggerdotdev/coordinator:${TRIGGER_IMAGE_TAG:-v3} + restart: ${RESTART_POLICY:-unless-stopped} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + user: root + networks: + - webapp + depends_on: + - webapp + ports: + - ${DOCKER_PUBLISH_IP:-127.0.0.1}:9020:9020 + env_file: + - .env + environment: + <<: *worker-env + PLATFORM_SECRET: $COORDINATOR_SECRET + + electric: + image: electricsql/electric:${ELECTRIC_IMAGE_TAG:-latest} + restart: ${RESTART_POLICY:-unless-stopped} + environment: + DATABASE_URL: ${DATABASE_URL}?sslmode=disable + networks: + - webapp + depends_on: + - postgres + ports: + - ${DOCKER_PUBLISH_IP:-127.0.0.1}:3061:3000 diff --git a/apps/dokploy/templates/trigger/index.ts b/apps/dokploy/templates/trigger/index.ts new file mode 100644 index 000000000..cb6c8a706 --- /dev/null +++ b/apps/dokploy/templates/trigger/index.ts @@ -0,0 +1,56 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const triggerDomain = generateRandomDomain(schema); + const providerSecret = generateBase64(32); + const coordinatorSecret = generateBase64(32); + const dbPassword = generateBase64(24); + const dbUser = "triggeruser"; + const dbName = "triggerdb"; + + const domains: DomainSchema[] = [ + { + host: triggerDomain, + port: 3040, + serviceName: "webapp", + }, + ]; + + const envs = [ + // Database configuration with secure credentials + `POSTGRES_USER=${dbUser}`, + `POSTGRES_PASSWORD=${dbPassword}`, + `POSTGRES_DB=${dbName}`, + `DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`, + + // Trigger configuration + `TRIGGER_DOMAIN=${triggerDomain}`, + "TRIGGER_PROTOCOL=http", + + // Secrets for services + `PROVIDER_SECRET=${providerSecret}`, + `COORDINATOR_SECRET=${coordinatorSecret}`, + + // Optional configurations with defaults + "TRIGGER_IMAGE_TAG=v3", + "POSTGRES_IMAGE_TAG=16", + "REDIS_IMAGE_TAG=7", + "ELECTRIC_IMAGE_TAG=latest", + "RESTART_POLICY=unless-stopped", + + // Network bindings + "WEBAPP_PUBLISH_IP=127.0.0.1", + "DOCKER_PUBLISH_IP=127.0.0.1", + ]; + + return { + envs, + domains, + }; +} From 2f15f34a19a2958966c8142c20933827043e5b9d Mon Sep 17 00:00:00 2001 From: mafrasil Date: Fri, 6 Dec 2024 18:22:04 +0400 Subject: [PATCH 19/52] rename as triggerdotdev --- .../public/templates/{trigger.svg => triggerdotdev.svg} | 0 apps/dokploy/templates/templates.ts | 8 ++++---- .../{trigger => triggerdotdev}/docker-compose.yml | 0 .../dokploy/templates/{trigger => triggerdotdev}/index.ts | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename apps/dokploy/public/templates/{trigger.svg => triggerdotdev.svg} (100%) rename apps/dokploy/templates/{trigger => triggerdotdev}/docker-compose.yml (100%) rename apps/dokploy/templates/{trigger => triggerdotdev}/index.ts (100%) diff --git a/apps/dokploy/public/templates/trigger.svg b/apps/dokploy/public/templates/triggerdotdev.svg similarity index 100% rename from apps/dokploy/public/templates/trigger.svg rename to apps/dokploy/public/templates/triggerdotdev.svg diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 23fb26a96..bbb907a32 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -973,18 +973,18 @@ export const templates: TemplateData[] = [ load: () => import("./ontime/index").then((m) => m.generate), }, { - id: "trigger", - name: "Trigger", + id: "triggerdotdev", + name: "Trigger.dev", version: "v3", description: "Trigger is a platform for building event-driven applications.", - logo: "trigger.svg", + logo: "triggerdotdev.svg", links: { github: "https://github.com/triggerdotdev/trigger.dev", website: "https://trigger.dev/", docs: "https://trigger.dev/docs", }, tags: ["event-driven", "applications"], - load: () => import("./trigger/index").then((m) => m.generate), + load: () => import("./triggerdotdev/index").then((m) => m.generate), }, ]; diff --git a/apps/dokploy/templates/trigger/docker-compose.yml b/apps/dokploy/templates/triggerdotdev/docker-compose.yml similarity index 100% rename from apps/dokploy/templates/trigger/docker-compose.yml rename to apps/dokploy/templates/triggerdotdev/docker-compose.yml diff --git a/apps/dokploy/templates/trigger/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts similarity index 100% rename from apps/dokploy/templates/trigger/index.ts rename to apps/dokploy/templates/triggerdotdev/index.ts From 5e590c1ce82ce9b1a5ec8bff28236837f91765ed Mon Sep 17 00:00:00 2001 From: mafrasil Date: Fri, 6 Dec 2024 20:35:34 +0400 Subject: [PATCH 20/52] add extra env --- apps/dokploy/templates/triggerdotdev/index.ts | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/apps/dokploy/templates/triggerdotdev/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts index cb6c8a706..7d92543a0 100644 --- a/apps/dokploy/templates/triggerdotdev/index.ts +++ b/apps/dokploy/templates/triggerdotdev/index.ts @@ -1,3 +1,4 @@ +import { Secrets } from "@/components/ui/secrets"; import { type DomainSchema, type Schema, @@ -8,8 +9,13 @@ import { export function generate(schema: Schema): Template { const triggerDomain = generateRandomDomain(schema); + + const magicLinkSecret = generateBase64(16); + const sessionSecret = generateBase64(16); + const encryptionKey = generateBase64(32); const providerSecret = generateBase64(32); const coordinatorSecret = generateBase64(32); + const dbPassword = generateBase64(24); const dbUser = "triggeruser"; const dbName = "triggerdb"; @@ -23,30 +29,56 @@ export function generate(schema: Schema): Template { ]; const envs = [ + `NODE_ENV=production`, + `RUNTIME_PLATFORM=docker-compose`, + `V3_ENABLED=true`, + + // Trigger configuration + `TRIGGER_DOMAIN=${triggerDomain}`, + `TRIGGER_PROTOCOL=http`, + // Database configuration with secure credentials `POSTGRES_USER=${dbUser}`, `POSTGRES_PASSWORD=${dbPassword}`, `POSTGRES_DB=${dbName}`, `DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`, - // Trigger configuration - `TRIGGER_DOMAIN=${triggerDomain}`, - "TRIGGER_PROTOCOL=http", - - // Secrets for services + // Secrets + `MAGIC_LINK_SECRET=${magicLinkSecret}`, + `SESSION_SECRET=${sessionSecret}`, + `ENCRYPTION_KEY=${encryptionKey}`, `PROVIDER_SECRET=${providerSecret}`, `COORDINATOR_SECRET=${coordinatorSecret}`, - // Optional configurations with defaults - "TRIGGER_IMAGE_TAG=v3", - "POSTGRES_IMAGE_TAG=16", - "REDIS_IMAGE_TAG=7", - "ELECTRIC_IMAGE_TAG=latest", - "RESTART_POLICY=unless-stopped", + `# TRIGGER_TELEMETRY_DISABLED=1`, + `INTERNAL_OTEL_TRACE_DISABLED=1`, + `INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0`, + + `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300`, + `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100`, + + `# If this is set, emails that are not specified won't be able to log in`, + `# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"`, + `# Accounts with these emails will become admins when signing up and get access to the admin panel`, + `# ADMIN_EMAILS="admin@example.com|another-admin@example.com"`, + + `# If this is set, your users will be able to log in via GitHub`, + `# AUTH_GITHUB_CLIENT_ID=`, + `# AUTH_GITHUB_CLIENT_SECRET=`, + + `# E-mail settings`, + `# Ensure the FROM_EMAIL matches what you setup with Resend.com`, + `# If these are not set, emails will be printed to the console`, + `# FROM_EMAIL=`, + `# REPLY_TO_EMAIL=`, + `# RESEND_API_KEY=`, - // Network bindings - "WEBAPP_PUBLISH_IP=127.0.0.1", - "DOCKER_PUBLISH_IP=127.0.0.1", + `# Worker settings`, + `HTTP_SERVER_PORT=9020`, + `COORDINATOR_HOST=127.0.0.1`, + `COORDINATOR_PORT=\${HTTP_SERVER_PORT}`, + `# REGISTRY_HOST=\${DEPLOY_REGISTRY_HOST}`, + `# REGISTRY_NAMESPACE=\${DEPLOY_REGISTRY_NAMESPACE}`, ]; return { From 4c34643287a63b6ae20528780e8c34832edcfe4b Mon Sep 17 00:00:00 2001 From: mafrasil Date: Fri, 6 Dec 2024 20:38:02 +0400 Subject: [PATCH 21/52] tweak env --- apps/dokploy/templates/triggerdotdev/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/triggerdotdev/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts index 7d92543a0..2411c2b83 100644 --- a/apps/dokploy/templates/triggerdotdev/index.ts +++ b/apps/dokploy/templates/triggerdotdev/index.ts @@ -33,17 +33,17 @@ export function generate(schema: Schema): Template { `RUNTIME_PLATFORM=docker-compose`, `V3_ENABLED=true`, - // Trigger configuration + `# Domain configuration`, `TRIGGER_DOMAIN=${triggerDomain}`, `TRIGGER_PROTOCOL=http`, - // Database configuration with secure credentials + `# Database configuration with secure credentials`, `POSTGRES_USER=${dbUser}`, `POSTGRES_PASSWORD=${dbPassword}`, `POSTGRES_DB=${dbName}`, `DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`, - // Secrets + `# Secrets`, `MAGIC_LINK_SECRET=${magicLinkSecret}`, `SESSION_SECRET=${sessionSecret}`, `ENCRYPTION_KEY=${encryptionKey}`, @@ -57,6 +57,11 @@ export function generate(schema: Schema): Template { `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300`, `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100`, + `DIRECT_URL=\${DATABASE_URL}`, + `REDIS_HOST=redis`, + `REDIS_PORT=6379`, + `REDIS_TLS_DISABLED=true`, + `# If this is set, emails that are not specified won't be able to log in`, `# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"`, `# Accounts with these emails will become admins when signing up and get access to the admin panel`, From 556a8470540af0456d15914fd21a53cdab95f213 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:04:15 -0600 Subject: [PATCH 22/52] Apply suggestions from code review --- .../templates/triggerdotdev/docker-compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/templates/triggerdotdev/docker-compose.yml b/apps/dokploy/templates/triggerdotdev/docker-compose.yml index 424f912bd..f53980277 100644 --- a/apps/dokploy/templates/triggerdotdev/docker-compose.yml +++ b/apps/dokploy/templates/triggerdotdev/docker-compose.yml @@ -26,7 +26,7 @@ services: environment: <<: *webapp-env ports: - - ${WEBAPP_PUBLISH_IP:-127.0.0.1}:3040:3030 + - 3000 depends_on: - postgres - redis @@ -43,7 +43,7 @@ services: networks: - webapp ports: - - ${DOCKER_PUBLISH_IP:-127.0.0.1}:5433:5432 + - 5432 command: - -c - wal_level=logical @@ -56,7 +56,7 @@ services: networks: - webapp ports: - - ${DOCKER_PUBLISH_IP:-127.0.0.1}:6389:6379 + - 6379 docker-provider: image: ghcr.io/triggerdotdev/provider/docker:${TRIGGER_IMAGE_TAG:-v3} @@ -69,7 +69,7 @@ services: depends_on: - webapp ports: - - ${DOCKER_PUBLISH_IP:-127.0.0.1}:9021:9020 + - 9020 env_file: - .env environment: @@ -87,7 +87,7 @@ services: depends_on: - webapp ports: - - ${DOCKER_PUBLISH_IP:-127.0.0.1}:9020:9020 + - 9020 env_file: - .env environment: @@ -104,4 +104,4 @@ services: depends_on: - postgres ports: - - ${DOCKER_PUBLISH_IP:-127.0.0.1}:3061:3000 + - 3000 From 5db407b6743898f7548bdf111feb77b988197361 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:05:00 -0600 Subject: [PATCH 23/52] Update apps/dokploy/templates/triggerdotdev/index.ts --- apps/dokploy/templates/triggerdotdev/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/triggerdotdev/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts index 2411c2b83..9a98b46cc 100644 --- a/apps/dokploy/templates/triggerdotdev/index.ts +++ b/apps/dokploy/templates/triggerdotdev/index.ts @@ -23,7 +23,7 @@ export function generate(schema: Schema): Template { const domains: DomainSchema[] = [ { host: triggerDomain, - port: 3040, + port: 3000, serviceName: "webapp", }, ]; From 1056810170ae597e9f56f4fbf82f5b738614beea Mon Sep 17 00:00:00 2001 From: idicesystem <79581397+PiyushDixit96@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:07:51 +0530 Subject: [PATCH 24/52] Add support for configurable Heroku stack version Added a new environment variable HEROKU_STACK_VERSION that can be used to specify the desired Heroku stack version. Updated the buildHeroku and getHerokuCommand functions to use the specified stack version, or default to 24 if the environment variable is not set. Provided information about the available Heroku stack versions and their support details, as per the documentation from the Heroku DevCenter. https://devcenter.heroku.com/articles/stack#stack-support-details --- packages/server/src/utils/builders/heroku.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/builders/heroku.ts b/packages/server/src/utils/builders/heroku.ts index 999b5fe61..21442d9ac 100644 --- a/packages/server/src/utils/builders/heroku.ts +++ b/packages/server/src/utils/builders/heroku.ts @@ -16,13 +16,14 @@ export const buildHeroku = async ( application.project.env, ); try { + const builderVersion = env.HEROKU_STACK_VERSION || '24'; const args = [ "build", appName, "--path", buildAppDirectory, "--builder", - "heroku/builder:24", + `heroku/builder:${builderVersion}`, ]; for (const env of envVariables) { @@ -52,13 +53,14 @@ export const getHerokuCommand = ( application.project.env, ); + const builderVersion = env.HEROKU_STACK_VERSION || '24'; const args = [ "build", appName, "--path", buildAppDirectory, "--builder", - "heroku/builder:24", + `heroku/builder:${builderVersion}`, ]; for (const env of envVariables) { From 4c45be14474e9f5bef230c05366b8493294d7f77 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:27:54 -0600 Subject: [PATCH 25/52] feat: add heroku version field --- .../dashboard/application/build/show.tsx | 32 + apps/dokploy/drizzle/0047_tidy_revanche.sql | 1 + apps/dokploy/drizzle/0048_flat_expediter.sql | 1 + apps/dokploy/drizzle/meta/0047_snapshot.json | 3993 ++++++++++++++++ apps/dokploy/drizzle/meta/0048_snapshot.json | 3994 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 14 + .../dokploy/server/api/routers/application.ts | 1 + packages/server/src/db/schema/application.ts | 3 + packages/server/src/utils/builders/heroku.ts | 6 +- 9 files changed, 8041 insertions(+), 4 deletions(-) create mode 100644 apps/dokploy/drizzle/0047_tidy_revanche.sql create mode 100644 apps/dokploy/drizzle/0048_flat_expediter.sql create mode 100644 apps/dokploy/drizzle/meta/0047_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0048_snapshot.json diff --git a/apps/dokploy/components/dashboard/application/build/show.tsx b/apps/dokploy/components/dashboard/application/build/show.tsx index 4bd4ddd7b..edfb38ae8 100644 --- a/apps/dokploy/components/dashboard/application/build/show.tsx +++ b/apps/dokploy/components/dashboard/application/build/show.tsx @@ -41,6 +41,7 @@ const mySchema = z.discriminatedUnion("buildType", [ }), z.object({ buildType: z.literal("heroku_buildpacks"), + herokuVersion: z.string().nullable().default(""), }), z.object({ buildType: z.literal("paketo_buildpacks"), @@ -90,6 +91,13 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { dockerBuildStage: data.dockerBuildStage || "", }), }); + } else if (data.buildType === "heroku_buildpacks") { + form.reset({ + buildType: data.buildType, + ...(data.buildType && { + herokuVersion: data.herokuVersion || "", + }), + }); } else { form.reset({ buildType: data.buildType, @@ -110,6 +118,8 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { data.buildType === "dockerfile" ? data.dockerContextPath : null, dockerBuildStage: data.buildType === "dockerfile" ? data.dockerBuildStage : null, + herokuVersion: + data.buildType === "heroku_buildpacks" ? data.herokuVersion : null, }) .then(async () => { toast.success("Build type saved"); @@ -200,6 +210,28 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { ); }} /> + {buildType === "heroku_buildpacks" && ( + { + return ( + + Heroku Version (Optional) + + + + + + + ); + }} + /> + )} {buildType === "dockerfile" && ( <> Date: Sat, 7 Dec 2024 13:45:50 -0600 Subject: [PATCH 26/52] refactor: improve I18N --- .circleci/config.yml | 6 +-- Dockerfile | 2 +- .../dashboard/settings/appearance-form.tsx | 53 +++++-------------- apps/dokploy/lib/languages.ts | 16 ++++++ apps/dokploy/next-i18next.config.cjs | 2 + apps/dokploy/pages/_app.tsx | 16 +----- .../public/locales/pt-br/settings.json | 2 +- apps/dokploy/utils/hooks/use-locale.ts | 22 ++------ apps/dokploy/utils/i18n.ts | 14 +++-- 9 files changed, 53 insertions(+), 80 deletions(-) create mode 100644 apps/dokploy/lib/languages.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index c5e57a973..25982d59b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -99,14 +99,14 @@ workflows: only: - main - canary - - fix/build-i18n + - refactor/enhancement-languages - build-arm64: filters: branches: only: - main - canary - - fix/build-i18n + - refactor/enhancement-languages - combine-manifests: requires: - build-amd64 @@ -116,4 +116,4 @@ workflows: only: - main - canary - - fix/build-i18n + - refactor/enhancement-languages diff --git a/Dockerfile b/Dockerfile index 838fbe4f6..ebc61a2b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var COPY --from=build /prod/dokploy/.next ./.next COPY --from=build /prod/dokploy/dist ./dist COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs -COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs +# COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs COPY --from=build /prod/dokploy/public ./public COPY --from=build /prod/dokploy/package.json ./package.json COPY --from=build /prod/dokploy/drizzle ./drizzle diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index 9f740efd8..209ac5421 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -32,30 +32,15 @@ import { useTranslation } from "next-i18next"; import { useTheme } from "next-themes"; import { useEffect } from "react"; import { toast } from "sonner"; +import { Languages } from "@/lib/languages"; const appearanceFormSchema = z.object({ theme: z.enum(["light", "dark", "system"], { required_error: "Please select a theme.", }), - language: z.enum( - [ - "en", - "pl", - "ru", - "fr", - "de", - "tr", - "zh-Hant", - "kz", - "zh-Hans", - "fa", - "ko", - "pt-br", - ], - { - required_error: "Please select a language.", - }, - ), + language: z.nativeEnum(Languages, { + required_error: "Please select a language.", + }), }); type AppearanceFormValues = z.infer; @@ -63,7 +48,7 @@ type AppearanceFormValues = z.infer; // This can come from your database or API. const defaultValues: Partial = { theme: "system", - language: "en", + language: Languages.English, }; export function AppearanceForm() { @@ -188,25 +173,15 @@ export function AppearanceForm() { - {[ - { label: "English", value: "en" }, - { label: "Polski", value: "pl" }, - { label: "Русский", value: "ru" }, - { label: "Français", value: "fr" }, - { label: "Deutsch", value: "de" }, - { label: "繁體中文", value: "zh-Hant" }, - { label: "简体中文", value: "zh-Hans" }, - { label: "Türkçe", value: "tr" }, - { label: "Қазақ", value: "tr" }, - { label: "Kazakh", value: "kz" }, - { label: "Persian", value: "fa" }, - { label: "한국어", value: "ko" }, - { label: "Português", value: "pt-br" }, - ].map((preset) => ( - - {preset.label} - - ))} + {Object.keys(Languages).map((preset) => { + const value = + Languages[preset as keyof typeof Languages]; + return ( + + {preset} + + ); + })} diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts new file mode 100644 index 000000000..59bcc097d --- /dev/null +++ b/apps/dokploy/lib/languages.ts @@ -0,0 +1,16 @@ +export enum Languages { + English = "en", + Polish = "pl", + Russian = "ru", + French = "fr", + German = "de", + ChineseTraditional = "zh-Hant", + ChineseSimplified = "zh-Hans", + Turkish = "tr", + Kazakh = "kz", + Persian = "fa", + Korean = "ko", + Portuguese = "pt-br", +} + +export type Language = keyof typeof Languages; diff --git a/apps/dokploy/next-i18next.config.cjs b/apps/dokploy/next-i18next.config.cjs index fef1df4a2..fe1b53ace 100644 --- a/apps/dokploy/next-i18next.config.cjs +++ b/apps/dokploy/next-i18next.config.cjs @@ -1,3 +1,5 @@ + + /** @type {import('next-i18next').UserConfig} */ module.exports = { fallbackLng: "en", diff --git a/apps/dokploy/pages/_app.tsx b/apps/dokploy/pages/_app.tsx index 511ac93ba..8bea3898b 100644 --- a/apps/dokploy/pages/_app.tsx +++ b/apps/dokploy/pages/_app.tsx @@ -10,6 +10,7 @@ import { Inter } from "next/font/google"; import Head from "next/head"; import Script from "next/script"; import type { ReactElement, ReactNode } from "react"; +import { Languages } from "@/lib/languages"; const inter = Inter({ subsets: ["latin"] }); @@ -71,20 +72,7 @@ export default api.withTRPC( { i18n: { defaultLocale: "en", - locales: [ - "en", - "pl", - "ru", - "fr", - "de", - "tr", - "kz", - "zh-Hant", - "zh-Hans", - "fa", - "ko", - "pt-br", - ], + locales: Object.values(Languages), localeDetection: false, }, fallbackLng: "en", diff --git a/apps/dokploy/public/locales/pt-br/settings.json b/apps/dokploy/public/locales/pt-br/settings.json index fc964be6c..7a00fd388 100644 --- a/apps/dokploy/public/locales/pt-br/settings.json +++ b/apps/dokploy/public/locales/pt-br/settings.json @@ -32,7 +32,7 @@ "settings.profile.password": "Senha", "settings.profile.avatar": "Avatar", - "settings.appearance.title": "Aparência", + "settings.appearance.title": "Aparencia", "settings.appearance.description": "Personalize o tema do seu dashboard.", "settings.appearance.theme": "Tema", "settings.appearance.themeDescription": "Selecione um tema para o dashboard", diff --git a/apps/dokploy/utils/hooks/use-locale.ts b/apps/dokploy/utils/hooks/use-locale.ts index 134418fcf..de979f7bf 100644 --- a/apps/dokploy/utils/hooks/use-locale.ts +++ b/apps/dokploy/utils/hooks/use-locale.ts @@ -1,26 +1,10 @@ +import type { Languages } from "@/lib/languages"; import Cookies from "js-cookie"; -const SUPPORTED_LOCALES = [ - "en", - "pl", - "ru", - "fr", - "de", - "tr", - "kz", - "zh-Hant", - "zh-Hans", - "fa", - "ko", - "pt-br", -] as const; - -type Locale = (typeof SUPPORTED_LOCALES)[number]; - export default function useLocale() { - const currentLocale = (Cookies.get("DOKPLOY_LOCALE") ?? "en") as Locale; + const currentLocale = (Cookies.get("DOKPLOY_LOCALE") ?? "en") as Languages; - const setLocale = (locale: Locale) => { + const setLocale = (locale: Languages) => { Cookies.set("DOKPLOY_LOCALE", locale, { expires: 365 }); window.location.reload(); }; diff --git a/apps/dokploy/utils/i18n.ts b/apps/dokploy/utils/i18n.ts index 4790f1a70..08370766e 100644 --- a/apps/dokploy/utils/i18n.ts +++ b/apps/dokploy/utils/i18n.ts @@ -5,11 +5,19 @@ export function getLocale(cookies: NextApiRequestCookies) { return locale; } -// libs/i18n.js import { serverSideTranslations as originalServerSideTranslations } from "next-i18next/serverSideTranslations"; -import nextI18NextConfig from "../next-i18next.config.cjs"; +import { Languages } from "@/lib/languages"; export const serverSideTranslations = ( locale: string, namespaces = ["common"], -) => originalServerSideTranslations(locale, namespaces, nextI18NextConfig); +) => + originalServerSideTranslations(locale, namespaces, { + fallbackLng: "en", + keySeparator: false, + i18n: { + defaultLocale: "en", + locales: Object.values(Languages), + localeDetection: false, + }, + }); From 1c1e52f77786091c17ee08291a7881440401de63 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:50:38 -0600 Subject: [PATCH 27/52] fix: lint --- .../dashboard/compose/general/actions.tsx | 2 +- .../docker/terminal/docker-terminal-modal.tsx | 150 +- .../dashboard/settings/appearance-form.tsx | 2 +- .../web-server/docker-terminal-modal.tsx | 234 +- apps/dokploy/next-i18next.config.cjs | 42 +- apps/dokploy/pages/_app.tsx | 2 +- apps/dokploy/pages/api/deploy/github.ts | 273 ++- apps/dokploy/public/locales/ko/settings.json | 78 +- apps/dokploy/public/locales/kz/common.json | 2 +- apps/dokploy/public/locales/kz/settings.json | 78 +- .../public/locales/pt-br/settings.json | 2 +- .../server/api/routers/notification.ts | 2 +- apps/dokploy/templates/templates.ts | 1968 ++++++++--------- apps/dokploy/templates/triggerdotdev/index.ts | 174 +- apps/dokploy/utils/i18n.ts | 2 +- .../utils/notifications/database-backup.ts | 4 +- .../src/utils/notifications/docker-cleanup.ts | 1 - 17 files changed, 1508 insertions(+), 1508 deletions(-) diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index 0fa5ae38e..439669669 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -68,7 +68,7 @@ export const ComposeActions = ({ composeId }: Props) => { Open Terminal -
+
Autodeploy import("./docker-terminal").then((e) => e.DockerTerminal), - { - ssr: false, - } + () => import("./docker-terminal").then((e) => e.DockerTerminal), + { + ssr: false, + }, ); interface Props { - containerId: string; - serverId?: string; - children?: React.ReactNode; + containerId: string; + serverId?: string; + children?: React.ReactNode; } export const DockerTerminalModal = ({ - children, - containerId, - serverId, + children, + containerId, + serverId, }: Props) => { - const [mainDialogOpen, setMainDialogOpen] = useState(false); - const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); + const [mainDialogOpen, setMainDialogOpen] = useState(false); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); - const handleMainDialogOpenChange = (open: boolean) => { - if (!open) { - setConfirmDialogOpen(true); - } else { - setMainDialogOpen(true); - } - }; + const handleMainDialogOpenChange = (open: boolean) => { + if (!open) { + setConfirmDialogOpen(true); + } else { + setMainDialogOpen(true); + } + }; - const handleConfirm = () => { - setConfirmDialogOpen(false); - setMainDialogOpen(false); - }; + const handleConfirm = () => { + setConfirmDialogOpen(false); + setMainDialogOpen(false); + }; - const handleCancel = () => { - setConfirmDialogOpen(false); - }; - return ( - - - e.preventDefault()} - > - {children} - - - - - Docker Terminal - - Easy way to access to docker container - - + const handleCancel = () => { + setConfirmDialogOpen(false); + }; + return ( + + + e.preventDefault()} + > + {children} + + + + + Docker Terminal + + Easy way to access to docker container + + - - - - - Are you sure you want to close the terminal? - - By clicking the confirm button, the terminal will be closed. - - - - - - - - - - - ); + + + + + + Are you sure you want to close the terminal? + + + By clicking the confirm button, the terminal will be closed. + + + + + + + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/settings/appearance-form.tsx b/apps/dokploy/components/dashboard/settings/appearance-form.tsx index 209ac5421..d5b90182d 100644 --- a/apps/dokploy/components/dashboard/settings/appearance-form.tsx +++ b/apps/dokploy/components/dashboard/settings/appearance-form.tsx @@ -27,12 +27,12 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { Languages } from "@/lib/languages"; import useLocale from "@/utils/hooks/use-locale"; import { useTranslation } from "next-i18next"; import { useTheme } from "next-themes"; import { useEffect } from "react"; import { toast } from "sonner"; -import { Languages } from "@/lib/languages"; const appearanceFormSchema = z.object({ theme: z.enum(["light", "dark", "system"], { diff --git a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx index ba3253a4a..f81be0adf 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx @@ -1,22 +1,22 @@ import { Button } from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, } from "@/components/ui/select"; import { api } from "@/utils/api"; import { Loader2 } from "lucide-react"; @@ -25,118 +25,118 @@ import type React from "react"; import { useEffect, useState } from "react"; const Terminal = dynamic( - () => - import("@/components/dashboard/docker/terminal/docker-terminal").then( - (e) => e.DockerTerminal - ), - { - ssr: false, - } + () => + import("@/components/dashboard/docker/terminal/docker-terminal").then( + (e) => e.DockerTerminal, + ), + { + ssr: false, + }, ); interface Props { - appName: string; - children?: React.ReactNode; - serverId?: string; + appName: string; + children?: React.ReactNode; + serverId?: string; } export const DockerTerminalModal = ({ children, appName, serverId }: Props) => { - const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery( - { - appName, - serverId, - }, - { - enabled: !!appName, - } - ); - const [containerId, setContainerId] = useState(); - const [mainDialogOpen, setMainDialogOpen] = useState(false); - const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); + const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery( + { + appName, + serverId, + }, + { + enabled: !!appName, + }, + ); + const [containerId, setContainerId] = useState(); + const [mainDialogOpen, setMainDialogOpen] = useState(false); + const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); - const handleMainDialogOpenChange = (open: boolean) => { - if (!open) { - setConfirmDialogOpen(true); - } else { - setMainDialogOpen(true); - } - }; + const handleMainDialogOpenChange = (open: boolean) => { + if (!open) { + setConfirmDialogOpen(true); + } else { + setMainDialogOpen(true); + } + }; - const handleConfirm = () => { - setConfirmDialogOpen(false); - setMainDialogOpen(false); - }; + const handleConfirm = () => { + setConfirmDialogOpen(false); + setMainDialogOpen(false); + }; - const handleCancel = () => { - setConfirmDialogOpen(false); - }; + const handleCancel = () => { + setConfirmDialogOpen(false); + }; - useEffect(() => { - if (data && data?.length > 0) { - setContainerId(data[0]?.containerId); - } - }, [data]); + useEffect(() => { + if (data && data?.length > 0) { + setContainerId(data[0]?.containerId); + } + }, [data]); - return ( - - {children} - - - Docker Terminal - - Easy way to access to docker container - - - - - - - - - - Are you sure you want to close the terminal? - - - By clicking the confirm button, the terminal will be closed. - - - - - - - - - - - ); + return ( + + {children} + + + Docker Terminal + + Easy way to access to docker container + + + + + + + + + + Are you sure you want to close the terminal? + + + By clicking the confirm button, the terminal will be closed. + + + + + + + + + + + ); }; diff --git a/apps/dokploy/next-i18next.config.cjs b/apps/dokploy/next-i18next.config.cjs index fe1b53ace..9f030ecb2 100644 --- a/apps/dokploy/next-i18next.config.cjs +++ b/apps/dokploy/next-i18next.config.cjs @@ -1,25 +1,23 @@ - - /** @type {import('next-i18next').UserConfig} */ module.exports = { - fallbackLng: "en", - keySeparator: false, - i18n: { - defaultLocale: "en", - locales: [ - "en", - "pl", - "ru", - "fr", - "de", - "tr", - "kz", - "zh-Hant", - "zh-Hans", - "fa", - "ko", - "pt-br", - ], - localeDetection: false, - }, + fallbackLng: "en", + keySeparator: false, + i18n: { + defaultLocale: "en", + locales: [ + "en", + "pl", + "ru", + "fr", + "de", + "tr", + "kz", + "zh-Hant", + "zh-Hans", + "fa", + "ko", + "pt-br", + ], + localeDetection: false, + }, }; diff --git a/apps/dokploy/pages/_app.tsx b/apps/dokploy/pages/_app.tsx index 8bea3898b..e7c0befc3 100644 --- a/apps/dokploy/pages/_app.tsx +++ b/apps/dokploy/pages/_app.tsx @@ -1,6 +1,7 @@ import "@/styles/globals.css"; import { Toaster } from "@/components/ui/sonner"; +import { Languages } from "@/lib/languages"; import { api } from "@/utils/api"; import type { NextPage } from "next"; import { appWithTranslation } from "next-i18next"; @@ -10,7 +11,6 @@ import { Inter } from "next/font/google"; import Head from "next/head"; import Script from "next/script"; import type { ReactElement, ReactNode } from "react"; -import { Languages } from "@/lib/languages"; const inter = Inter({ subsets: ["latin"] }); diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 7f0e7f0b0..e5ed154f8 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -10,142 +10,141 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; export default async function handler( - req: NextApiRequest, - res: NextApiResponse + req: NextApiRequest, + res: NextApiResponse, ) { - const signature = req.headers["x-hub-signature-256"]; - const githubBody = req.body; - - if (!githubBody?.installation?.id) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - const githubResult = await db.query.github.findFirst({ - where: eq(github.githubInstallationId, githubBody.installation.id), - }); - - if (!githubResult) { - res.status(400).json({ message: "Github Installation not found" }); - return; - } - - if (!githubResult.githubWebhookSecret) { - res.status(400).json({ message: "Github Webhook Secret not set" }); - return; - } - const webhooks = new Webhooks({ - secret: githubResult.githubWebhookSecret, - }); - - const verified = await webhooks.verify( - JSON.stringify(githubBody), - signature as string - ); - - if (!verified) { - res.status(401).json({ message: "Unauthorized" }); - return; - } - - if (req.headers["x-github-event"] === "ping") { - res.status(200).json({ message: "Ping received, webhook is active" }); - return; - } - - if (req.headers["x-github-event"] !== "push") { - res.status(400).json({ message: "We only accept push events" }); - return; - } - - try { - const branchName = githubBody?.ref?.replace("refs/heads/", ""); - const repository = githubBody?.repository?.name; - const deploymentTitle = extractCommitMessage(req.headers, req.body); - const deploymentHash = extractHash(req.headers, req.body); - const owner = githubBody?.repository?.owner?.name; - - - const apps = await db.query.applications.findMany({ - where: and( - eq(applications.sourceType, "github"), - eq(applications.autoDeploy, true), - eq(applications.branch, branchName), - eq(applications.repository, repository), - eq(applications.owner, owner) - ), - }); - - for (const app of apps) { - const jobData: DeploymentJob = { - applicationId: app.applicationId as string, - titleLog: deploymentTitle, - descriptionLog: `Hash: ${deploymentHash}`, - type: "deploy", - applicationType: "application", - server: !!app.serverId, - }; - - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const composeApps = await db.query.compose.findMany({ - where: and( - eq(compose.sourceType, "github"), - eq(compose.autoDeploy, true), - eq(compose.branch, branchName), - eq(compose.repository, repository), - eq(compose.owner, owner) - ), - }); - - for (const composeApp of composeApps) { - const jobData: DeploymentJob = { - composeId: composeApp.composeId as string, - titleLog: deploymentTitle, - type: "deploy", - applicationType: "compose", - descriptionLog: `Hash: ${deploymentHash}`, - server: !!composeApp.serverId, - }; - - if (IS_CLOUD && composeApp.serverId) { - jobData.serverId = composeApp.serverId; - await deploy(jobData); - return true; - } - - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - } - ); - } - - const totalApps = apps.length + composeApps.length; - const emptyApps = totalApps === 0; - - if (emptyApps) { - res.status(200).json({ message: "No apps to deploy" }); - return; - } - res.status(200).json({ message: `Deployed ${totalApps} apps` }); - } catch (error) { - res.status(400).json({ message: "Error To Deploy Application", error }); - } + const signature = req.headers["x-hub-signature-256"]; + const githubBody = req.body; + + if (!githubBody?.installation?.id) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + const githubResult = await db.query.github.findFirst({ + where: eq(github.githubInstallationId, githubBody.installation.id), + }); + + if (!githubResult) { + res.status(400).json({ message: "Github Installation not found" }); + return; + } + + if (!githubResult.githubWebhookSecret) { + res.status(400).json({ message: "Github Webhook Secret not set" }); + return; + } + const webhooks = new Webhooks({ + secret: githubResult.githubWebhookSecret, + }); + + const verified = await webhooks.verify( + JSON.stringify(githubBody), + signature as string, + ); + + if (!verified) { + res.status(401).json({ message: "Unauthorized" }); + return; + } + + if (req.headers["x-github-event"] === "ping") { + res.status(200).json({ message: "Ping received, webhook is active" }); + return; + } + + if (req.headers["x-github-event"] !== "push") { + res.status(400).json({ message: "We only accept push events" }); + return; + } + + try { + const branchName = githubBody?.ref?.replace("refs/heads/", ""); + const repository = githubBody?.repository?.name; + const deploymentTitle = extractCommitMessage(req.headers, req.body); + const deploymentHash = extractHash(req.headers, req.body); + const owner = githubBody?.repository?.owner?.name; + + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.autoDeploy, true), + eq(applications.branch, branchName), + eq(applications.repository, repository), + eq(applications.owner, owner), + ), + }); + + for (const app of apps) { + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: deploymentTitle, + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application", + server: !!app.serverId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const composeApps = await db.query.compose.findMany({ + where: and( + eq(compose.sourceType, "github"), + eq(compose.autoDeploy, true), + eq(compose.branch, branchName), + eq(compose.repository, repository), + eq(compose.owner, owner), + ), + }); + + for (const composeApp of composeApps) { + const jobData: DeploymentJob = { + composeId: composeApp.composeId as string, + titleLog: deploymentTitle, + type: "deploy", + applicationType: "compose", + descriptionLog: `Hash: ${deploymentHash}`, + server: !!composeApp.serverId, + }; + + if (IS_CLOUD && composeApp.serverId) { + jobData.serverId = composeApp.serverId; + await deploy(jobData); + return true; + } + + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + + const totalApps = apps.length + composeApps.length; + const emptyApps = totalApps === 0; + + if (emptyApps) { + res.status(200).json({ message: "No apps to deploy" }); + return; + } + res.status(200).json({ message: `Deployed ${totalApps} apps` }); + } catch (error) { + res.status(400).json({ message: "Error To Deploy Application", error }); + } } diff --git a/apps/dokploy/public/locales/ko/settings.json b/apps/dokploy/public/locales/ko/settings.json index f0a99e4e7..db877ee6a 100644 --- a/apps/dokploy/public/locales/ko/settings.json +++ b/apps/dokploy/public/locales/ko/settings.json @@ -1,44 +1,44 @@ { - "settings.common.save": "저장", - "settings.server.domain.title": "서버 도메인", - "settings.server.domain.description": "서버 애플리케이션에 도메인을 추가합니다.", - "settings.server.domain.form.domain": "도메인", - "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 이메일", - "settings.server.domain.form.certificate.label": "인증서", - "settings.server.domain.form.certificate.placeholder": "인증서 선택", - "settings.server.domain.form.certificateOptions.none": "없음", - "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (기본)", + "settings.common.save": "저장", + "settings.server.domain.title": "서버 도메인", + "settings.server.domain.description": "서버 애플리케이션에 도메인을 추가합니다.", + "settings.server.domain.form.domain": "도메인", + "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 이메일", + "settings.server.domain.form.certificate.label": "인증서", + "settings.server.domain.form.certificate.placeholder": "인증서 선택", + "settings.server.domain.form.certificateOptions.none": "없음", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (기본)", - "settings.server.webServer.title": "웹 서버", - "settings.server.webServer.description": "웹 서버를 재시작하거나 정리합니다.", - "settings.server.webServer.actions": "작업", - "settings.server.webServer.reload": "재시작", - "settings.server.webServer.watchLogs": "로그 보기", - "settings.server.webServer.updateServerIp": "서버 IP 갱신", - "settings.server.webServer.server.label": "서버", - "settings.server.webServer.traefik.label": "Traefik", - "settings.server.webServer.traefik.modifyEnv": "환경 변수 수정", - "settings.server.webServer.storage.label": "저장 공간", - "settings.server.webServer.storage.cleanUnusedImages": "사용하지 않는 이미지 정리", - "settings.server.webServer.storage.cleanUnusedVolumes": "사용하지 않는 볼륨 정리", - "settings.server.webServer.storage.cleanStoppedContainers": "정지된 컨테이너 정리", - "settings.server.webServer.storage.cleanDockerBuilder": "도커 빌더 & 시스템 정리", - "settings.server.webServer.storage.cleanMonitoring": "모니터링 데이터 정리", - "settings.server.webServer.storage.cleanAll": "전체 정리", + "settings.server.webServer.title": "웹 서버", + "settings.server.webServer.description": "웹 서버를 재시작하거나 정리합니다.", + "settings.server.webServer.actions": "작업", + "settings.server.webServer.reload": "재시작", + "settings.server.webServer.watchLogs": "로그 보기", + "settings.server.webServer.updateServerIp": "서버 IP 갱신", + "settings.server.webServer.server.label": "서버", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "환경 변수 수정", + "settings.server.webServer.storage.label": "저장 공간", + "settings.server.webServer.storage.cleanUnusedImages": "사용하지 않는 이미지 정리", + "settings.server.webServer.storage.cleanUnusedVolumes": "사용하지 않는 볼륨 정리", + "settings.server.webServer.storage.cleanStoppedContainers": "정지된 컨테이너 정리", + "settings.server.webServer.storage.cleanDockerBuilder": "도커 빌더 & 시스템 정리", + "settings.server.webServer.storage.cleanMonitoring": "모니터링 데이터 정리", + "settings.server.webServer.storage.cleanAll": "전체 정리", - "settings.profile.title": "계정", - "settings.profile.description": "여기에서 프로필 세부 정보를 변경하세요.", - "settings.profile.email": "이메일", - "settings.profile.password": "비밀번호", - "settings.profile.avatar": "아바타", + "settings.profile.title": "계정", + "settings.profile.description": "여기에서 프로필 세부 정보를 변경하세요.", + "settings.profile.email": "이메일", + "settings.profile.password": "비밀번호", + "settings.profile.avatar": "아바타", - "settings.appearance.title": "외관", - "settings.appearance.description": "대시보드의 테마를 사용자 설정합니다.", - "settings.appearance.theme": "테마", - "settings.appearance.themeDescription": "대시보드 테마 선택", - "settings.appearance.themes.light": "라이트", - "settings.appearance.themes.dark": "다크", - "settings.appearance.themes.system": "시스템", - "settings.appearance.language": "언어", - "settings.appearance.languageDescription": "대시보드에서 사용할 언어 선택" + "settings.appearance.title": "외관", + "settings.appearance.description": "대시보드의 테마를 사용자 설정합니다.", + "settings.appearance.theme": "테마", + "settings.appearance.themeDescription": "대시보드 테마 선택", + "settings.appearance.themes.light": "라이트", + "settings.appearance.themes.dark": "다크", + "settings.appearance.themes.system": "시스템", + "settings.appearance.language": "언어", + "settings.appearance.languageDescription": "대시보드에서 사용할 언어 선택" } diff --git a/apps/dokploy/public/locales/kz/common.json b/apps/dokploy/public/locales/kz/common.json index 9e26dfeeb..0967ef424 100644 --- a/apps/dokploy/public/locales/kz/common.json +++ b/apps/dokploy/public/locales/kz/common.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/apps/dokploy/public/locales/kz/settings.json b/apps/dokploy/public/locales/kz/settings.json index 97403d0e2..bf8f41372 100644 --- a/apps/dokploy/public/locales/kz/settings.json +++ b/apps/dokploy/public/locales/kz/settings.json @@ -1,41 +1,41 @@ { - "settings.common.save": "Сақтау", - "settings.server.domain.title": "Сервер домені", - "settings.server.domain.description": "Dokploy сервер қолданбасына домен енгізіңіз.", - "settings.server.domain.form.domain": "Домен", - "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Эл. поштасы", - "settings.server.domain.form.certificate.label": "Сертификат", - "settings.server.domain.form.certificate.placeholder": "Сертификатты таңдаңыз", - "settings.server.domain.form.certificateOptions.none": "Жоқ", - "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Стандартты)", - "settings.server.webServer.title": "Веб-Сервер", - "settings.server.webServer.description": "Веб-серверді қайта жүктеу немесе тазалау.", - "settings.server.webServer.actions": "Әрекеттер", - "settings.server.webServer.reload": "Қайта жүктеу", - "settings.server.webServer.watchLogs": "Журналдарды қарау", - "settings.server.webServer.updateServerIp": "Сервердің IP жаңарту", - "settings.server.webServer.server.label": "Сервер", - "settings.server.webServer.traefik.label": "Traefik", - "settings.server.webServer.traefik.modifyEnv": "Env Өзгерту", - "settings.server.webServer.storage.label": "Диск кеңістігі", - "settings.server.webServer.storage.cleanUnusedImages": "Пайдаланылмаған образды тазалау", - "settings.server.webServer.storage.cleanUnusedVolumes": "Пайдаланылмаған томды тазалау", - "settings.server.webServer.storage.cleanStoppedContainers": "Тоқтатылған контейнерлерді тазалау", - "settings.server.webServer.storage.cleanDockerBuilder": "Docker Builder & Системаны тазалау", - "settings.server.webServer.storage.cleanMonitoring": "Мониторингті тазалау", - "settings.server.webServer.storage.cleanAll": "Барлығын тазалау", - "settings.profile.title": "Аккаунт", - "settings.profile.description": "Профиль мәліметтерін осы жерден өзгертіңіз.", - "settings.profile.email": "Эл. пошта", - "settings.profile.password": "Құпия сөз", - "settings.profile.avatar": "Аватар", - "settings.appearance.title": "Сыртқы түрі", - "settings.appearance.description": "Dokploy сыртқы келбетін өзгерту.", - "settings.appearance.theme": "Келбеті", - "settings.appearance.themeDescription": "Жүйе тақтасының келбетің таңдаңыз", - "settings.appearance.themes.light": "Жарық", - "settings.appearance.themes.dark": "Қараңғы", - "settings.appearance.themes.system": "Жүйелік", - "settings.appearance.language": "Тіл", - "settings.appearance.languageDescription": "Жүйе тақтасының тілің таңдаңыз" + "settings.common.save": "Сақтау", + "settings.server.domain.title": "Сервер домені", + "settings.server.domain.description": "Dokploy сервер қолданбасына домен енгізіңіз.", + "settings.server.domain.form.domain": "Домен", + "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Эл. поштасы", + "settings.server.domain.form.certificate.label": "Сертификат", + "settings.server.domain.form.certificate.placeholder": "Сертификатты таңдаңыз", + "settings.server.domain.form.certificateOptions.none": "Жоқ", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Стандартты)", + "settings.server.webServer.title": "Веб-Сервер", + "settings.server.webServer.description": "Веб-серверді қайта жүктеу немесе тазалау.", + "settings.server.webServer.actions": "Әрекеттер", + "settings.server.webServer.reload": "Қайта жүктеу", + "settings.server.webServer.watchLogs": "Журналдарды қарау", + "settings.server.webServer.updateServerIp": "Сервердің IP жаңарту", + "settings.server.webServer.server.label": "Сервер", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "Env Өзгерту", + "settings.server.webServer.storage.label": "Диск кеңістігі", + "settings.server.webServer.storage.cleanUnusedImages": "Пайдаланылмаған образды тазалау", + "settings.server.webServer.storage.cleanUnusedVolumes": "Пайдаланылмаған томды тазалау", + "settings.server.webServer.storage.cleanStoppedContainers": "Тоқтатылған контейнерлерді тазалау", + "settings.server.webServer.storage.cleanDockerBuilder": "Docker Builder & Системаны тазалау", + "settings.server.webServer.storage.cleanMonitoring": "Мониторингті тазалау", + "settings.server.webServer.storage.cleanAll": "Барлығын тазалау", + "settings.profile.title": "Аккаунт", + "settings.profile.description": "Профиль мәліметтерін осы жерден өзгертіңіз.", + "settings.profile.email": "Эл. пошта", + "settings.profile.password": "Құпия сөз", + "settings.profile.avatar": "Аватар", + "settings.appearance.title": "Сыртқы түрі", + "settings.appearance.description": "Dokploy сыртқы келбетін өзгерту.", + "settings.appearance.theme": "Келбеті", + "settings.appearance.themeDescription": "Жүйе тақтасының келбетің таңдаңыз", + "settings.appearance.themes.light": "Жарық", + "settings.appearance.themes.dark": "Қараңғы", + "settings.appearance.themes.system": "Жүйелік", + "settings.appearance.language": "Тіл", + "settings.appearance.languageDescription": "Жүйе тақтасының тілің таңдаңыз" } diff --git a/apps/dokploy/public/locales/pt-br/settings.json b/apps/dokploy/public/locales/pt-br/settings.json index 7a00fd388..f4d90a2f8 100644 --- a/apps/dokploy/public/locales/pt-br/settings.json +++ b/apps/dokploy/public/locales/pt-br/settings.json @@ -31,7 +31,7 @@ "settings.profile.email": "Email", "settings.profile.password": "Senha", "settings.profile.avatar": "Avatar", - + "settings.appearance.title": "Aparencia", "settings.appearance.description": "Personalize o tema do seu dashboard.", "settings.appearance.theme": "Tema", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 504e33209..951d2a10d 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -190,7 +190,7 @@ export const notificationRouter = createTRPCRouter({ await sendDiscordNotification(input, { title: "> `🤚` - Test Notification", description: "> Hi, From Dokploy 👋", - color: 0xf3f7f4 + color: 0xf3f7f4, }); return true; } catch (error) { diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index bbb907a32..ef418d2b6 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,990 +1,990 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.0", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.0", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.48.1", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "5.8.3", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.48.1", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "5.8.3", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file", "manager"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.3", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.dev/", - docs: "https://ryot.dev/docs/getting-started", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, - { - id: "triggerdotdev", - name: "Trigger.dev", - version: "v3", - description: - "Trigger is a platform for building event-driven applications.", - logo: "triggerdotdev.svg", - links: { - github: "https://github.com/triggerdotdev/trigger.dev", - website: "https://trigger.dev/", - docs: "https://trigger.dev/docs", - }, - tags: ["event-driven", "applications"], - load: () => import("./triggerdotdev/index").then((m) => m.generate), - }, + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file", "manager"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.3", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.dev/", + docs: "https://ryot.dev/docs/getting-started", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "triggerdotdev", + name: "Trigger.dev", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "triggerdotdev.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./triggerdotdev/index").then((m) => m.generate), + }, ]; diff --git a/apps/dokploy/templates/triggerdotdev/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts index 9a98b46cc..7b894acba 100644 --- a/apps/dokploy/templates/triggerdotdev/index.ts +++ b/apps/dokploy/templates/triggerdotdev/index.ts @@ -1,93 +1,93 @@ import { Secrets } from "@/components/ui/secrets"; import { - type DomainSchema, - type Schema, - type Template, - generateBase64, - generateRandomDomain, + type DomainSchema, + type Schema, + type Template, + generateBase64, + generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const triggerDomain = generateRandomDomain(schema); - - const magicLinkSecret = generateBase64(16); - const sessionSecret = generateBase64(16); - const encryptionKey = generateBase64(32); - const providerSecret = generateBase64(32); - const coordinatorSecret = generateBase64(32); - - const dbPassword = generateBase64(24); - const dbUser = "triggeruser"; - const dbName = "triggerdb"; - - const domains: DomainSchema[] = [ - { - host: triggerDomain, - port: 3000, - serviceName: "webapp", - }, - ]; - - const envs = [ - `NODE_ENV=production`, - `RUNTIME_PLATFORM=docker-compose`, - `V3_ENABLED=true`, - - `# Domain configuration`, - `TRIGGER_DOMAIN=${triggerDomain}`, - `TRIGGER_PROTOCOL=http`, - - `# Database configuration with secure credentials`, - `POSTGRES_USER=${dbUser}`, - `POSTGRES_PASSWORD=${dbPassword}`, - `POSTGRES_DB=${dbName}`, - `DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`, - - `# Secrets`, - `MAGIC_LINK_SECRET=${magicLinkSecret}`, - `SESSION_SECRET=${sessionSecret}`, - `ENCRYPTION_KEY=${encryptionKey}`, - `PROVIDER_SECRET=${providerSecret}`, - `COORDINATOR_SECRET=${coordinatorSecret}`, - - `# TRIGGER_TELEMETRY_DISABLED=1`, - `INTERNAL_OTEL_TRACE_DISABLED=1`, - `INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0`, - - `DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300`, - `DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100`, - - `DIRECT_URL=\${DATABASE_URL}`, - `REDIS_HOST=redis`, - `REDIS_PORT=6379`, - `REDIS_TLS_DISABLED=true`, - - `# If this is set, emails that are not specified won't be able to log in`, - `# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"`, - `# Accounts with these emails will become admins when signing up and get access to the admin panel`, - `# ADMIN_EMAILS="admin@example.com|another-admin@example.com"`, - - `# If this is set, your users will be able to log in via GitHub`, - `# AUTH_GITHUB_CLIENT_ID=`, - `# AUTH_GITHUB_CLIENT_SECRET=`, - - `# E-mail settings`, - `# Ensure the FROM_EMAIL matches what you setup with Resend.com`, - `# If these are not set, emails will be printed to the console`, - `# FROM_EMAIL=`, - `# REPLY_TO_EMAIL=`, - `# RESEND_API_KEY=`, - - `# Worker settings`, - `HTTP_SERVER_PORT=9020`, - `COORDINATOR_HOST=127.0.0.1`, - `COORDINATOR_PORT=\${HTTP_SERVER_PORT}`, - `# REGISTRY_HOST=\${DEPLOY_REGISTRY_HOST}`, - `# REGISTRY_NAMESPACE=\${DEPLOY_REGISTRY_NAMESPACE}`, - ]; - - return { - envs, - domains, - }; + const triggerDomain = generateRandomDomain(schema); + + const magicLinkSecret = generateBase64(16); + const sessionSecret = generateBase64(16); + const encryptionKey = generateBase64(32); + const providerSecret = generateBase64(32); + const coordinatorSecret = generateBase64(32); + + const dbPassword = generateBase64(24); + const dbUser = "triggeruser"; + const dbName = "triggerdb"; + + const domains: DomainSchema[] = [ + { + host: triggerDomain, + port: 3000, + serviceName: "webapp", + }, + ]; + + const envs = [ + "NODE_ENV=production", + "RUNTIME_PLATFORM=docker-compose", + "V3_ENABLED=true", + + "# Domain configuration", + `TRIGGER_DOMAIN=${triggerDomain}`, + "TRIGGER_PROTOCOL=http", + + "# Database configuration with secure credentials", + `POSTGRES_USER=${dbUser}`, + `POSTGRES_PASSWORD=${dbPassword}`, + `POSTGRES_DB=${dbName}`, + `DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`, + + "# Secrets", + `MAGIC_LINK_SECRET=${magicLinkSecret}`, + `SESSION_SECRET=${sessionSecret}`, + `ENCRYPTION_KEY=${encryptionKey}`, + `PROVIDER_SECRET=${providerSecret}`, + `COORDINATOR_SECRET=${coordinatorSecret}`, + + "# TRIGGER_TELEMETRY_DISABLED=1", + "INTERNAL_OTEL_TRACE_DISABLED=1", + "INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0", + + "DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300", + "DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100", + + "DIRECT_URL=${DATABASE_URL}", + "REDIS_HOST=redis", + "REDIS_PORT=6379", + "REDIS_TLS_DISABLED=true", + + "# If this is set, emails that are not specified won't be able to log in", + '# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"', + "# Accounts with these emails will become admins when signing up and get access to the admin panel", + '# ADMIN_EMAILS="admin@example.com|another-admin@example.com"', + + "# If this is set, your users will be able to log in via GitHub", + "# AUTH_GITHUB_CLIENT_ID=", + "# AUTH_GITHUB_CLIENT_SECRET=", + + "# E-mail settings", + "# Ensure the FROM_EMAIL matches what you setup with Resend.com", + "# If these are not set, emails will be printed to the console", + "# FROM_EMAIL=", + "# REPLY_TO_EMAIL=", + "# RESEND_API_KEY=", + + "# Worker settings", + "HTTP_SERVER_PORT=9020", + "COORDINATOR_HOST=127.0.0.1", + "COORDINATOR_PORT=${HTTP_SERVER_PORT}", + "# REGISTRY_HOST=${DEPLOY_REGISTRY_HOST}", + "# REGISTRY_NAMESPACE=${DEPLOY_REGISTRY_NAMESPACE}", + ]; + + return { + envs, + domains, + }; } diff --git a/apps/dokploy/utils/i18n.ts b/apps/dokploy/utils/i18n.ts index 08370766e..47466d1f5 100644 --- a/apps/dokploy/utils/i18n.ts +++ b/apps/dokploy/utils/i18n.ts @@ -5,8 +5,8 @@ export function getLocale(cookies: NextApiRequestCookies) { return locale; } -import { serverSideTranslations as originalServerSideTranslations } from "next-i18next/serverSideTranslations"; import { Languages } from "@/lib/languages"; +import { serverSideTranslations as originalServerSideTranslations } from "next-i18next/serverSideTranslations"; export const serverSideTranslations = ( locale: string, diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 115d8b206..717c965f6 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -95,7 +95,9 @@ export const sendDatabaseBackupNotifications = async ({ }, { name: "`❓`・Type", - value: type.replace("error", "Failed").replace("success", "Successful"), + value: type + .replace("error", "Failed") + .replace("success", "Successful"), inline: true, }, ...(type === "error" && errorMessage diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index 31c1c6c9d..e8bbc6dbe 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -48,7 +48,6 @@ export const sendDockerCleanupNotifications = async ( title: "> `✅` - Docker Cleanup", color: 0x57f287, fields: [ - { name: "`📅`・Date", value: date.toLocaleDateString(), From 2960d818295f4881b4f9b122111d054a52e9c150 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:55:00 -0600 Subject: [PATCH 28/52] fix: show mount path when is not compose --- .../dashboard/application/advanced/volumes/update-volume.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx index 91e9befed..590cd1141 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx @@ -119,7 +119,7 @@ export const UpdateVolume = ({ } else if (typeForm === "file") { form.reset({ content: data.content || "", - mountPath: "/", + mountPath: serviceType === "compose" ? "/" : data.mountPath, filePath: data.filePath || "", type: "file", }); From f4bd729f65494ffee48e97ce99f17c734ef98e8f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 14:01:48 -0600 Subject: [PATCH 29/52] test: add missing fields --- apps/dokploy/__test__/drop/drop.test.test.ts | 1 + apps/dokploy/__test__/traefik/traefik.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index 53ab02f2b..9a6473afd 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -26,6 +26,7 @@ if (typeof window === "undefined") { const baseApp: ApplicationNested = { applicationId: "", + herokuVersion: "", applicationStatus: "done", appName: "", autoDeploy: true, diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 7e11160bd..d7ad29ab0 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -6,6 +6,7 @@ import { expect, test } from "vitest"; const baseApp: ApplicationNested = { applicationId: "", + herokuVersion: "", applicationStatus: "done", appName: "", autoDeploy: true, From 32b19a0fb648628e305b58a181e3b0390298e16c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 7 Dec 2024 14:09:31 -0600 Subject: [PATCH 30/52] refactor: add code editor in volumes edit --- Dockerfile | 1 - Dockerfile.cloud | 1 - .../application/advanced/volumes/add-volumes.tsx | 13 ++++++++----- .../application/advanced/volumes/update-volume.tsx | 14 ++++++++------ 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index ebc61a2b7..74b70db0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,6 @@ RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var COPY --from=build /prod/dokploy/.next ./.next COPY --from=build /prod/dokploy/dist ./dist COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs -# COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs COPY --from=build /prod/dokploy/public ./public COPY --from=build /prod/dokploy/package.json ./package.json COPY --from=build /prod/dokploy/drizzle ./drizzle diff --git a/Dockerfile.cloud b/Dockerfile.cloud index 0f8427d46..020ea3d69 100644 --- a/Dockerfile.cloud +++ b/Dockerfile.cloud @@ -44,7 +44,6 @@ RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var COPY --from=build /prod/dokploy/.next ./.next COPY --from=build /prod/dokploy/dist ./dist COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs -COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs COPY --from=build /prod/dokploy/public ./public COPY --from=build /prod/dokploy/package.json ./package.json COPY --from=build /prod/dokploy/drizzle ./drizzle diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx index d9dc16e70..5c6b95ca1 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx @@ -1,3 +1,4 @@ +import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -18,7 +19,6 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -150,7 +150,7 @@ export const AddVolumes = ({ - + Volumes / Mounts @@ -303,9 +303,12 @@ export const AddVolumes = ({ Content -