From 126dad498c6db14078192ceeb05bdf756a0f8727 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 2 Mar 2025 21:59:31 -0600 Subject: [PATCH 01/64] chore(version): bump project version to v0.19.0 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 8b067cf8..5ea57461 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.18.4", + "version": "v0.19.0", "private": true, "license": "Apache-2.0", "type": "module", From 4da4e1c17dad96d7a3da9cfbaa7cbdbb67c7acdb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 2 Mar 2025 22:16:10 -0600 Subject: [PATCH 02/64] feat(side-menu): restrict AI settings to owner role --- apps/dokploy/components/layouts/side.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 77f3e84d..dea5e137 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -302,6 +302,7 @@ const MENU: Menu = { icon: BotIcon, url: "/dashboard/settings/ai", isSingle: true, + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, From 5db7508530f812e7e8f565d55bdca7b94b094253 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 2 Mar 2025 22:32:56 -0600 Subject: [PATCH 03/64] feat(organization): prevent deletion of last owned organization --- apps/dokploy/server/api/routers/organization.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index 6f7a9c67..3d7753de 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -133,6 +133,18 @@ export const organizationRouter = createTRPCRouter({ }); } + const ownerOrgs = await db.query.organization.findMany({ + where: eq(organization.ownerId, ctx.user.id), + }); + + if (ownerOrgs.length <= 1) { + throw new TRPCError({ + code: "FORBIDDEN", + message: + "You must maintain at least one organization where you are the owner", + }); + } + const result = await db .delete(organization) .where(eq(organization.id, input.organizationId)); From 49d4cea06f2b8f80fc0fb9145ea42d5987bc8ad0 Mon Sep 17 00:00:00 2001 From: Hexaa <128217934+hexaaagon@users.noreply.github.com> Date: Mon, 3 Mar 2025 12:17:42 +0700 Subject: [PATCH 04/64] feat(zipline): update zipline version --- apps/dokploy/templates/zipline/docker-compose.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/zipline/docker-compose.yml b/apps/dokploy/templates/zipline/docker-compose.yml index e29132df..6227164f 100644 --- a/apps/dokploy/templates/zipline/docker-compose.yml +++ b/apps/dokploy/templates/zipline/docker-compose.yml @@ -17,15 +17,17 @@ services: retries: 5 zipline: - image: ghcr.io/diced/zipline:3.7.9 + image: ghcr.io/diced/zipline:4 restart: unless-stopped environment: - CORE_RETURN_HTTPS=${ZIPLINE_RETURN_HTTPS} - CORE_SECRET=${ZIPLINE_SECRET} - - CORE_HOST=0.0.0.0 + - CORE_HOSTNAME=0.0.0.0 - CORE_PORT=${ZIPLINE_PORT} - - CORE_DATABASE_URL=postgres://postgres:postgres@postgres/postgres + - DATABASE_URL=postgres://postgres:postgres@postgres/postgres - CORE_LOGGER=${ZIPLINE_LOGGER} + - DATASOURCE_TYPE=local + - DATASOURCE_LOCAL_DIRECTORY=./uploads volumes: - "../files/uploads:/zipline/uploads" - "../files/public:/zipline/public" From 6846e0e5a33e0e32b248283f361993005e0f6fc6 Mon Sep 17 00:00:00 2001 From: Ali Issa Date: Mon, 3 Mar 2025 06:45:36 -0500 Subject: [PATCH 05/64] feat: reorganize project view tabs into logical workflow groups #1261 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the project view tabs to follow a more intuitive user journey: - Group tabs into "Initial Setup", "Deployment", and "Monitoring" sections - Maintain "Advanced" as a standalone option - Order tabs to match typical project workflow (configuration → deployment → monitoring) This reorganization reduces cognitive load by grouping related functions, minimizes tab switching during common tasks, and provides a clearer mental model of the platform's workflow for new users. --- .../services/application/[applicationId].tsx | 12 ++++++------ .../[projectId]/services/compose/[composeId].tsx | 6 +++--- .../[projectId]/services/mariadb/[mariadbId].tsx | 2 +- .../project/[projectId]/services/mongo/[mongoId].tsx | 2 +- .../project/[projectId]/services/mysql/[mysqlId].tsx | 2 +- .../[projectId]/services/postgres/[postgresId].tsx | 2 +- .../project/[projectId]/services/redis/[redisId].tsx | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) 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 94b8f5f5..cff3a8db 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -228,15 +228,15 @@ const Service = ( > General Environment - {((data?.serverId && isCloud) || !data?.server) && ( - Monitoring - )} - Logs - Deployments + Domains Preview Deployments - Domains + Deployments + Logs + {((data?.serverId && isCloud) || !data?.server) && ( + Monitoring + )} Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 46b727d2..86be791b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -224,12 +224,12 @@ const Service = ( > General Environment + Domains + Deployments + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} - Logs - Deployments - Domains Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index e91e0978..d3930bbc 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -197,11 +197,11 @@ const Mariadb = ( > General Environment + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} Backups - Logs Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index b10b7b93..4bc1d0ce 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -198,11 +198,11 @@ const Mongo = ( > General Environment + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} Backups - Logs Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 261a2762..5440fc5e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -200,13 +200,13 @@ const MySql = ( Environment + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} Backups - Logs Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 5d8fd3b1..635f8b4e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -197,11 +197,11 @@ const Postgresql = ( > General Environment + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} Backups - Logs Advanced diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index c4f40281..c7084d52 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -197,10 +197,10 @@ const Redis = ( > General Environment + Logs {((data?.serverId && isCloud) || !data?.server) && ( Monitoring )} - Logs Advanced From 62fae661a12a9fd66d2da833975805127380586f Mon Sep 17 00:00:00 2001 From: Shaun Janssens Date: Tue, 4 Mar 2025 10:50:42 +0100 Subject: [PATCH 06/64] feat: bitbucket branch length --- packages/server/src/utils/providers/bitbucket.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utils/providers/bitbucket.ts b/packages/server/src/utils/providers/bitbucket.ts index dd98a93b..11797a45 100644 --- a/packages/server/src/utils/providers/bitbucket.ts +++ b/packages/server/src/utils/providers/bitbucket.ts @@ -275,7 +275,7 @@ export const getBitbucketBranches = async ( } const bitbucketProvider = await findBitbucketById(input.bitbucketId); const { owner, repo } = input; - const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches`; + const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches?pagelen=100`; try { const response = await fetch(url, { From e43b907a8dfd720044ad2a4016268cc535d87b32 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Tue, 4 Mar 2025 22:42:04 +1100 Subject: [PATCH 07/64] feat(ui): move profile to first item in account dropdown --- apps/dokploy/components/layouts/user-nav.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 4a9624de..85cb96f3 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -72,6 +72,14 @@ export const UserNav = () => { + { + router.push("/dashboard/settings/profile"); + }} + > + Profile + { @@ -126,14 +134,6 @@ export const UserNav = () => { ) : ( <> - { - router.push("/dashboard/settings/profile"); - }} - > - Profile - {data?.role === "owner" && ( Date: Wed, 5 Mar 2025 00:18:10 -0600 Subject: [PATCH 08/64] feat(application): add Railpack as a new build type - Introduce Railpack as a new build method for applications - Update database schema to include 'railpack' in buildType enum - Add Railpack installation and validation scripts for servers - Implement Railpack build and command generation utilities - Update UI to include Railpack as a build option with a 'New' badge --- CONTRIBUTING.md | 7 + Dockerfile | 4 + .../dashboard/application/build/show.tsx | 14 + .../settings/servers/validate-server.tsx | 9 + .../drizzle/0069_legal_bill_hollister.sql | 1 + apps/dokploy/drizzle/meta/0069_snapshot.json | 5119 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/server/api/routers/server.ts | 4 + packages/server/src/db/schema/application.ts | 2 + packages/server/src/setup/server-setup.ts | 13 + packages/server/src/setup/server-validate.ts | 19 +- packages/server/src/utils/builders/index.ts | 6 + .../server/src/utils/builders/railpack.ts | 87 + 13 files changed, 5290 insertions(+), 2 deletions(-) create mode 100644 apps/dokploy/drizzle/0069_legal_bill_hollister.sql create mode 100644 apps/dokploy/drizzle/meta/0069_snapshot.json create mode 100644 packages/server/src/utils/builders/railpack.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19ee38dc..8584fdf6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,11 +138,18 @@ curl -sSL https://nixpacks.com/install.sh -o install.sh \ && ./install.sh ``` +```bash +# Install Railpack +curl -sSL https://railpack.com/install.sh | sh +``` + ```bash # Install Buildpacks curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.32.1/pack-v0.32.1-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack ``` + + ## Pull Request - The `main` branch is the source of truth and should always reflect the latest stable release. diff --git a/Dockerfile b/Dockerfile index 48bbb877..7f41dcd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,6 +55,10 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ && ./install.sh \ && pnpm install -g tsx +# Install Railpack +ARG RAILPACK_VERSION=0.0.37 +RUN curl -sSL https://railpack.com/install.sh | sh + # Install buildpacks COPY --from=buildpacksio/pack:0.35.0 /usr/local/bin/pack /usr/local/bin/pack diff --git a/apps/dokploy/components/dashboard/application/build/show.tsx b/apps/dokploy/components/dashboard/application/build/show.tsx index ad83f456..5c6e044c 100644 --- a/apps/dokploy/components/dashboard/application/build/show.tsx +++ b/apps/dokploy/components/dashboard/application/build/show.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { @@ -25,6 +26,7 @@ enum BuildType { paketo_buildpacks = "paketo_buildpacks", nixpacks = "nixpacks", static = "static", + railpack = "railpack", } const mySchema = z.discriminatedUnion("buildType", [ @@ -53,6 +55,9 @@ const mySchema = z.discriminatedUnion("buildType", [ z.object({ buildType: z.literal("static"), }), + z.object({ + buildType: z.literal("railpack"), + }), ]); type AddTemplate = z.infer; @@ -173,6 +178,15 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { Dockerfile + + + + + + Railpack{" "} + New + + diff --git a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx index 0632b97c..a489b390 100644 --- a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx @@ -139,6 +139,15 @@ export const ValidateServer = ({ serverId }: Props) => { : "Not Created" } /> + diff --git a/apps/dokploy/drizzle/0069_legal_bill_hollister.sql b/apps/dokploy/drizzle/0069_legal_bill_hollister.sql new file mode 100644 index 00000000..fb02a194 --- /dev/null +++ b/apps/dokploy/drizzle/0069_legal_bill_hollister.sql @@ -0,0 +1 @@ +ALTER TYPE "public"."buildType" ADD VALUE 'railpack'; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0069_snapshot.json b/apps/dokploy/drizzle/meta/0069_snapshot.json new file mode 100644 index 00000000..04712815 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0069_snapshot.json @@ -0,0 +1,5119 @@ +{ + "id": "6247f9c0-261c-497e-97f4-c02a80687211", + "prevId": "e1cfc9dc-d89b-4fe3-b29a-8dcea6fb959d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "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 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "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": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_temp_id_fk": { + "name": "apikey_user_id_user_temp_id_fk", + "tableFrom": "apikey", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_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 aa3c410b..844710e3 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -484,6 +484,13 @@ "when": 1740897756774, "tag": "0068_complex_rhino", "breakpoints": true + }, + { + "idx": 69, + "version": "7", + "when": 1741152916611, + "tag": "0069_legal_bill_hollister", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 1a9ebc0a..2c7af905 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -206,6 +206,10 @@ export const serverRouter = createTRPCRouter({ enabled: boolean; version: string; }; + railpack: { + enabled: boolean; + version: string; + }; isDokployNetworkInstalled: boolean; isSwarmInstalled: boolean; isMainDirectoryInstalled: boolean; diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index e670e2e2..379cfecf 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -42,6 +42,7 @@ export const buildType = pgEnum("buildType", [ "paketo_buildpacks", "nixpacks", "static", + "railpack", ]); export interface HealthCheckSwarm { @@ -383,6 +384,7 @@ const createSchema = createInsertSchema(applications, { "paketo_buildpacks", "nixpacks", "static", + "railpack", ]), herokuVersion: z.string().optional(), publishDirectory: z.string().optional(), diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts index dc760407..4f2335b2 100644 --- a/packages/server/src/setup/server-setup.ts +++ b/packages/server/src/setup/server-setup.ts @@ -170,6 +170,9 @@ ${installNixpacks()} echo -e "12. Installing Buildpacks" ${installBuildpacks()} + +echo -e "13. Installing Railpack" +${installRailpack()} `; return bashCommand; @@ -573,6 +576,16 @@ const installNixpacks = () => ` fi `; +const installRailpack = () => ` + if command_exists railpack; then + echo "Railpack already installed ✅" + else + export RAILPACK_VERSION=0.0.37 + bash -c "$(curl -fsSL https://railpack.com/install.sh)" + echo "Railpack version $RAILPACK_VERSION installed ✅" + fi +`; + const installBuildpacks = () => ` SUFFIX="" if [ "$SYS_ARCH" = "aarch64" ] || [ "$SYS_ARCH" = "arm64" ]; then diff --git a/packages/server/src/setup/server-validate.ts b/packages/server/src/setup/server-validate.ts index c86206b6..b190fde2 100644 --- a/packages/server/src/setup/server-validate.ts +++ b/packages/server/src/setup/server-validate.ts @@ -38,6 +38,18 @@ export const validateNixpacks = () => ` fi `; +export const validateRailpack = () => ` + if command_exists railpack; then + version=$(railpack --version | awk '{print $3}') + if [ -n "$version" ]; then + echo "$version true" + else + echo "0.0.0 false" + fi + else + echo "0.0.0 false" + fi +`; export const validateBuildpacks = () => ` if command_exists pack; then version=$(pack --version | awk '{print $1}') @@ -86,7 +98,7 @@ export const serverValidate = async (serverId: string) => { rcloneVersionEnabled=$(${validateRClone()}) nixpacksVersionEnabled=$(${validateNixpacks()}) buildpacksVersionEnabled=$(${validateBuildpacks()}) - + railpackVersionEnabled=$(${validateRailpack()}) dockerVersion=$(echo $dockerVersionEnabled | awk '{print $1}') dockerEnabled=$(echo $dockerVersionEnabled | awk '{print $2}') @@ -96,6 +108,9 @@ export const serverValidate = async (serverId: string) => { nixpacksVersion=$(echo $nixpacksVersionEnabled | awk '{print $1}') nixpacksEnabled=$(echo $nixpacksVersionEnabled | awk '{print $2}') + railpackVersion=$(echo $railpackVersionEnabled | awk '{print $1}') + railpackEnabled=$(echo $railpackVersionEnabled | awk '{print $2}') + buildpacksVersion=$(echo $buildpacksVersionEnabled | awk '{print $1}') buildpacksEnabled=$(echo $buildpacksVersionEnabled | awk '{print $2}') @@ -103,7 +118,7 @@ export const serverValidate = async (serverId: string) => { isSwarmInstalled=$(${validateSwarm()}) isMainDirectoryInstalled=$(${validateMainDirectory()}) - echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}" + echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"railpack\\": {\\"version\\": \\"$railpackVersion\\", \\"enabled\\": $railpackEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}" `; client.exec(bashCommand, (err, stream) => { if (err) { diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index d777b1a3..69419f0e 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -16,6 +16,7 @@ import { buildCustomDocker, getDockerCommand } from "./docker-file"; import { buildHeroku, getHerokuCommand } from "./heroku"; import { buildNixpacks, getNixpacksCommand } from "./nixpacks"; import { buildPaketo, getPaketoCommand } from "./paketo"; +import { buildRailpack, getRailpackCommand } from "./railpack"; import { buildStatic, getStaticCommand } from "./static"; // NIXPACKS codeDirectory = where is the path of the code directory @@ -55,6 +56,8 @@ export const buildApplication = async ( await buildCustomDocker(application, writeStream); } else if (buildType === "static") { await buildStatic(application, writeStream); + } else if (buildType === "railpack") { + await buildRailpack(application, writeStream); } if (application.registryId) { @@ -96,6 +99,9 @@ export const getBuildCommand = ( case "dockerfile": command = getDockerCommand(application, logPath); break; + case "railpack": + command = getRailpackCommand(application, logPath); + break; } if (registry) { command += uploadImageRemoteCommand(application, logPath); diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts new file mode 100644 index 00000000..ae55a638 --- /dev/null +++ b/packages/server/src/utils/builders/railpack.ts @@ -0,0 +1,87 @@ +import type { WriteStream } from "node:fs"; +import type { ApplicationNested } from "."; +import { prepareEnvironmentVariables } from "../docker/utils"; +import { getBuildAppDirectory } from "../filesystem/directory"; +import { spawnAsync } from "../process/spawnAsync"; +import { execAsync } from "../process/execAsync"; + +export const buildRailpack = async ( + application: ApplicationNested, + writeStream: WriteStream, +) => { + const { env, appName } = application; + const buildAppDirectory = getBuildAppDirectory(application); + const envVariables = prepareEnvironmentVariables( + env, + application.project.env, + ); + + try { + // Ensure buildkit container is running, create if it doesn't exist + await execAsync( + "docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit", + ); + + // Build the application using railpack + const args = ["build", buildAppDirectory, "--name", appName]; + + // Add environment variables + for (const env of envVariables) { + args.push("--env", env); + } + + await spawnAsync( + "railpack", + args, + (data) => { + if (writeStream.writable) { + writeStream.write(data); + } + }, + { + env: { + ...process.env, + BUILDKIT_HOST: "docker-container://buildkit", + }, + }, + ); + + return true; + } catch (e) { + throw e; + } +}; + +export const getRailpackCommand = ( + application: ApplicationNested, + logPath: string, +) => { + const { env, appName } = application; + const buildAppDirectory = getBuildAppDirectory(application); + const envVariables = prepareEnvironmentVariables( + env, + application.project.env, + ); + + // Build the application using railpack + const args = ["build", buildAppDirectory, "--name", appName]; + + // Add environment variables + for (const env of envVariables) { + args.push("--env", env); + } + + const command = `railpack ${args.join(" ")}`; + const bashCommand = ` + echo "Building with Railpack..." >> "${logPath}"; + docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit; + export BUILDKIT_HOST=docker-container://buildkit; + ${command} >> ${logPath} 2>> ${logPath} || { + echo "❌ Railpack build failed" >> ${logPath}; + exit 1; + } + echo "✅ Railpack build completed." >> ${logPath}; + `; + + return bashCommand; +}; From e039826d5011208163275c19d0f84783d8349df9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:21:36 -0600 Subject: [PATCH 09/64] chore(version): bump project version to v0.19.1 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 5ea57461..da354818 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.19.0", + "version": "v0.19.1", "private": true, "license": "Apache-2.0", "type": "module", From 6ff06576d00dad06890ff074fd39662f3964d464 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:36:20 -0600 Subject: [PATCH 10/64] fix(dockerfile): update Railpack installation script to use bash --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7f41dcd7..a5bd7e5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,7 +57,7 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ # Install Railpack ARG RAILPACK_VERSION=0.0.37 -RUN curl -sSL https://railpack.com/install.sh | sh +RUN curl -sSL https://railpack.com/install.sh | bash # Install buildpacks COPY --from=buildpacksio/pack:0.35.0 /usr/local/bin/pack /usr/local/bin/pack From 21c8b98f9c45908b63c11a0ae7b93f7ce34a181a Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 5 Mar 2025 16:12:46 +0800 Subject: [PATCH 11/64] feat: fallback to openai compatible provider if url host doesn't match --- packages/server/src/utils/ai/select-ai-provider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/ai/select-ai-provider.ts b/packages/server/src/utils/ai/select-ai-provider.ts index efbc5730..f56c5e8c 100644 --- a/packages/server/src/utils/ai/select-ai-provider.ts +++ b/packages/server/src/utils/ai/select-ai-provider.ts @@ -17,7 +17,7 @@ function getProviderName(apiUrl: string) { if (apiUrl.includes("localhost:11434") || apiUrl.includes("ollama")) return "ollama"; if (apiUrl.includes("api.deepinfra.com")) return "deepinfra"; - throw new Error(`Unsupported AI provider for URL: ${apiUrl}`); + return "custom"; } export function selectAIProvider(config: { apiUrl: string; apiKey: string }) { @@ -68,6 +68,12 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) { apiKey: config.apiKey, }); default: - throw new Error(`Unsupported AI provider: ${providerName}`); + return createOpenAICompatible({ + name: "custom", + baseURL: config.apiUrl, + headers: { + Authorization: `Bearer ${config.apiKey}`, + }, + )}; } } From a7fd64e019b44bff76d41f4049cc5254b0f0ff48 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 5 Mar 2025 16:17:50 +0800 Subject: [PATCH 12/64] fix: use a named case --- packages/server/src/utils/ai/select-ai-provider.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/src/utils/ai/select-ai-provider.ts b/packages/server/src/utils/ai/select-ai-provider.ts index f56c5e8c..92ace813 100644 --- a/packages/server/src/utils/ai/select-ai-provider.ts +++ b/packages/server/src/utils/ai/select-ai-provider.ts @@ -67,7 +67,7 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) { baseURL: config.apiUrl, apiKey: config.apiKey, }); - default: + case "custom": return createOpenAICompatible({ name: "custom", baseURL: config.apiUrl, @@ -75,5 +75,7 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) { Authorization: `Bearer ${config.apiKey}`, }, )}; + case default: + throw new Error(`Unsupported AI provider for URL: ${config.apiUrl}`); } } From efd176451f9495b32111a178e9dc7f1f2b606a39 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 5 Mar 2025 16:19:28 +0800 Subject: [PATCH 13/64] fix: default case correct --- packages/server/src/utils/ai/select-ai-provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/ai/select-ai-provider.ts b/packages/server/src/utils/ai/select-ai-provider.ts index 92ace813..1ebb91d6 100644 --- a/packages/server/src/utils/ai/select-ai-provider.ts +++ b/packages/server/src/utils/ai/select-ai-provider.ts @@ -75,7 +75,7 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) { Authorization: `Bearer ${config.apiKey}`, }, )}; - case default: - throw new Error(`Unsupported AI provider for URL: ${config.apiUrl}`); + default: + throw new Error(`Unsupported AI provider: ${providerName}`); } } From f342613503694006f38a896e547ccd604e6d7e3c Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 5 Mar 2025 20:27:16 +0700 Subject: [PATCH 14/64] Text format --- apps/dokploy/components/dashboard/project/add-template.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index 5363e6f3..8a5a4c98 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -383,9 +383,8 @@ export const AddTemplate = ({ projectId }: Props) => { side="top" > - If ot server is selected, the application - will be deployed on the server where the - user is logged in. + If no server is selected, the application will be + deployed on the server where the user is logged in. From 71ca5babfd49a2c143a45fd73f99281f3581aca9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 5 Mar 2025 20:51:09 +0700 Subject: [PATCH 15/64] Remove duplicate breadcrumb --- apps/dokploy/components/layouts/side.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index dea5e137..638f7f57 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1055,10 +1055,6 @@ export default function Page({ children }: Props) { - - - {activeItem?.title} - From c83d0a95b79a10650c1fbd1838a594b0c64ec4e9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 5 Mar 2025 20:53:38 +0700 Subject: [PATCH 16/64] Remove ending separator --- apps/dokploy/components/shared/breadcrumb-sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx index 74e9fdf6..ebc1b1f9 100644 --- a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx +++ b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx @@ -37,7 +37,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => { )} - + {_index + 1 < list.length && } ))} From b0c710aa9274331557c68777276bd040e822ed25 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 5 Mar 2025 21:25:11 +0700 Subject: [PATCH 17/64] Tab instead space --- apps/dokploy/components/dashboard/project/add-template.tsx | 2 +- apps/dokploy/components/shared/breadcrumb-sidebar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index 8a5a4c98..3a97b097 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -384,7 +384,7 @@ export const AddTemplate = ({ projectId }: Props) => { > If no server is selected, the application will be - deployed on the server where the user is logged in. + deployed on the server where the user is logged in. diff --git a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx index ebc1b1f9..5f6add3e 100644 --- a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx +++ b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx @@ -37,7 +37,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => { )} - {_index + 1 < list.length && } + {_index + 1 < list.length && } ))} From 3bdd5e4dd01d27aac18535f07d25cc83bef16016 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Thu, 6 Mar 2025 10:34:13 +0700 Subject: [PATCH 18/64] Template DataLens --- apps/dokploy/public/templates/datalens.svg | 28 +++++ .../templates/datalens/docker-compose.yml | 106 ++++++++++++++++++ apps/dokploy/templates/datalens/index.ts | 23 ++++ apps/dokploy/templates/templates.ts | 14 +++ 4 files changed, 171 insertions(+) create mode 100644 apps/dokploy/public/templates/datalens.svg create mode 100644 apps/dokploy/templates/datalens/docker-compose.yml create mode 100644 apps/dokploy/templates/datalens/index.ts diff --git a/apps/dokploy/public/templates/datalens.svg b/apps/dokploy/public/templates/datalens.svg new file mode 100644 index 00000000..64954e31 --- /dev/null +++ b/apps/dokploy/public/templates/datalens.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/dokploy/templates/datalens/docker-compose.yml b/apps/dokploy/templates/datalens/docker-compose.yml new file mode 100644 index 00000000..94da15fc --- /dev/null +++ b/apps/dokploy/templates/datalens/docker-compose.yml @@ -0,0 +1,106 @@ +services: + pg-compeng: + container_name: datalens-pg-compeng + image: postgres:16-alpine + environment: + POSTGRES_PASSWORD: "postgres" + POSTGRES_DB: postgres + POSTGRES_USER: postgres + + control-api: + container_name: datalens-control-api + image: ghcr.io/datalens-tech/datalens-control-api:0.2192.0 + environment: + BI_API_UWSGI_WORKERS_COUNT: 4 + CONNECTOR_AVAILABILITY_VISIBLE: "clickhouse,postgres,chyt,ydb,mysql,greenplum,mssql,appmetrica_api,metrika_api" + RQE_FORCE_OFF: 1 + DL_CRY_ACTUAL_KEY_ID: key_1 + DL_CRY_KEY_VAL_ID_key_1: "h1ZpilcYLYRdWp7Nk8X1M1kBPiUi8rdjz9oBfHyUKIk=" + RQE_SECRET_KEY: "" + US_HOST: "http://us:8083" + US_MASTER_TOKEN: "fake-us-master-token" + depends_on: + - us + + data-api: + container_name: datalens-data-api + image: ghcr.io/datalens-tech/datalens-data-api:0.2192.0 + environment: + GUNICORN_WORKERS_COUNT: 5 + RQE_FORCE_OFF: 1 + CACHES_ON: 0 + MUTATIONS_CACHES_ON: 0 + RQE_SECRET_KEY: "" + DL_CRY_ACTUAL_KEY_ID: key_1 + DL_CRY_KEY_VAL_ID_key_1: "h1ZpilcYLYRdWp7Nk8X1M1kBPiUi8rdjz9oBfHyUKIk=" + BI_COMPENG_PG_ON: 1 + BI_COMPENG_PG_URL: "postgresql://postgres:postgres@pg-compeng:5432/postgres" + US_HOST: "http://us:8083" + US_MASTER_TOKEN: "fake-us-master-token" + depends_on: + - us + - pg-compeng + + pg-demo-connection: + container_name: datalens-pg-demo-connection + image: postgres:16-alpine + environment: + POSTGRES_DB: demo + POSTGRES_USER: demo + POSTGRES_PASSWORD: demo + volumes: + - ${VOLUME_DEMO:-./pg-demo-connection/data}:/var/lib/postgresql/data + - ./pg-demo-connection/init:/docker-entrypoint-initdb.d + + pg-us: + container_name: datalens-pg-us + image: postgres:16-alpine + environment: + POSTGRES_DB: us-db-ci_purgeable + POSTGRES_USER: us + POSTGRES_PASSWORD: us + volumes: + - ${VOLUME_US:-./metadata}:/var/lib/postgresql/data + + us: + container_name: datalens-us + image: ghcr.io/datalens-tech/datalens-us:0.310.0 + depends_on: + - pg-us + environment: + APP_INSTALLATION: "opensource" + APP_ENV: "prod" + MASTER_TOKEN: "fake-us-master-token" + POSTGRES_DSN_LIST: ${METADATA_POSTGRES_DSN_LIST:-postgres://us:us@pg-us:5432/us-db-ci_purgeable} + SKIP_INSTALL_DB_EXTENSIONS: ${METADATA_SKIP_INSTALL_DB_EXTENSIONS:-0} + USE_DEMO_DATA: ${USE_DEMO_DATA:-1} + HC: ${HC:-0} + NODE_EXTRA_CA_CERTS: /certs/root.crt + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./certs:/certs + + datalens: + container_name: datalens-ui + image: ghcr.io/datalens-tech/datalens-ui:0.2601.0 + ports: + - ${UI_PORT:-8080}:8080 + depends_on: + - us + - control-api + - data-api + - pg-demo-connection + environment: + APP_MODE: "full" + APP_ENV: "production" + APP_INSTALLATION: "opensource" + AUTH_POLICY: "disabled" + US_ENDPOINT: "http://us:8083" + BI_API_ENDPOINT: "http://control-api:8080" + BI_DATA_ENDPOINT: "http://data-api:8080" + US_MASTER_TOKEN: "fake-us-master-token" + NODE_EXTRA_CA_CERTS: "/usr/local/share/ca-certificates/cert.pem" + HC: ${HC:-0} + YANDEX_MAP_ENABLED: ${YANDEX_MAP_ENABLED:-0} + YANDEX_MAP_TOKEN: ${YANDEX_MAP_TOKEN:-0} diff --git a/apps/dokploy/templates/datalens/index.ts b/apps/dokploy/templates/datalens/index.ts new file mode 100644 index 00000000..f2efb76b --- /dev/null +++ b/apps/dokploy/templates/datalens/index.ts @@ -0,0 +1,23 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 8080, + serviceName: "datalens", + }, + ]; + + const envs = ["HC=1"]; + + return { + envs, + domains, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index d39465a8..f0944c01 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -108,6 +108,20 @@ export const templates: TemplateData[] = [ tags: ["monitoring"], load: () => import("./grafana/index").then((m) => m.generate), }, + { + id: "datalens", + name: "DataLens", + version: "1.23.0", + description: "A modern, scalable business intelligence and data visualization system.", + logo: "datalens.svg", + links: { + github: "https://github.com/datalens-tech/datalens", + website: "https://datalens.tech/", + docs: "https://datalens.tech/docs/", + }, + tags: ["analytics", "self-hosted", "bi", "monitoring"], + load: () => import("./datalens/index").then((m) => m.generate), + }, { id: "directus", name: "Directus", From 8859cc97b44d28f7d5ac63576da9b989f4d43fdc Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Thu, 6 Mar 2025 10:46:10 +0700 Subject: [PATCH 19/64] fix: superset docker-compose --- apps/dokploy/templates/superset/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/superset/docker-compose.yml b/apps/dokploy/templates/superset/docker-compose.yml index b73bf55e..e786a934 100644 --- a/apps/dokploy/templates/superset/docker-compose.yml +++ b/apps/dokploy/templates/superset/docker-compose.yml @@ -1,7 +1,7 @@ # This is an UNOFFICIAL production docker image build for Superset: # - https://github.com/amancevice/docker-superset - + # ## SETUP INSTRUCTIONS # # After deploying this image, you will need to run one of the two @@ -10,7 +10,7 @@ # $ superset-init # Initialise database only # # You will be prompted to enter the credentials for the admin user. - + # ## NETWORK INSTRUCTIONS # @@ -66,7 +66,7 @@ services: timeout: 10s retries: 3 -superset_redis: + superset_redis: image: redis restart: always volumes: From f5cd0fbdd80ec97987fdb91f73a28df6a16a8f68 Mon Sep 17 00:00:00 2001 From: Vyacheslav Shcherbinin Date: Thu, 6 Mar 2025 11:32:13 +0700 Subject: [PATCH 20/64] Restart policy --- apps/dokploy/templates/datalens/docker-compose.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/dokploy/templates/datalens/docker-compose.yml b/apps/dokploy/templates/datalens/docker-compose.yml index 94da15fc..e4edc6ed 100644 --- a/apps/dokploy/templates/datalens/docker-compose.yml +++ b/apps/dokploy/templates/datalens/docker-compose.yml @@ -2,6 +2,7 @@ services: pg-compeng: container_name: datalens-pg-compeng image: postgres:16-alpine + restart: always environment: POSTGRES_PASSWORD: "postgres" POSTGRES_DB: postgres @@ -10,6 +11,7 @@ services: control-api: container_name: datalens-control-api image: ghcr.io/datalens-tech/datalens-control-api:0.2192.0 + restart: always environment: BI_API_UWSGI_WORKERS_COUNT: 4 CONNECTOR_AVAILABILITY_VISIBLE: "clickhouse,postgres,chyt,ydb,mysql,greenplum,mssql,appmetrica_api,metrika_api" @@ -25,6 +27,7 @@ services: data-api: container_name: datalens-data-api image: ghcr.io/datalens-tech/datalens-data-api:0.2192.0 + restart: always environment: GUNICORN_WORKERS_COUNT: 5 RQE_FORCE_OFF: 1 @@ -44,6 +47,7 @@ services: pg-demo-connection: container_name: datalens-pg-demo-connection image: postgres:16-alpine + restart: unless-stopped environment: POSTGRES_DB: demo POSTGRES_USER: demo @@ -55,6 +59,7 @@ services: pg-us: container_name: datalens-pg-us image: postgres:16-alpine + restart: always environment: POSTGRES_DB: us-db-ci_purgeable POSTGRES_USER: us @@ -65,6 +70,7 @@ services: us: container_name: datalens-us image: ghcr.io/datalens-tech/datalens-us:0.310.0 + restart: always depends_on: - pg-us environment: @@ -84,6 +90,7 @@ services: datalens: container_name: datalens-ui image: ghcr.io/datalens-tech/datalens-ui:0.2601.0 + restart: always ports: - ${UI_PORT:-8080}:8080 depends_on: From bf9e886b9aead461e380829b6f34e4704d0beb02 Mon Sep 17 00:00:00 2001 From: Vyacheslav Shcherbinin Date: Thu, 6 Mar 2025 14:05:39 +0700 Subject: [PATCH 21/64] Disable demo --- .../dokploy/templates/datalens/docker-compose.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/dokploy/templates/datalens/docker-compose.yml b/apps/dokploy/templates/datalens/docker-compose.yml index e4edc6ed..57dd0026 100644 --- a/apps/dokploy/templates/datalens/docker-compose.yml +++ b/apps/dokploy/templates/datalens/docker-compose.yml @@ -44,18 +44,6 @@ services: - us - pg-compeng - pg-demo-connection: - container_name: datalens-pg-demo-connection - image: postgres:16-alpine - restart: unless-stopped - environment: - POSTGRES_DB: demo - POSTGRES_USER: demo - POSTGRES_PASSWORD: demo - volumes: - - ${VOLUME_DEMO:-./pg-demo-connection/data}:/var/lib/postgresql/data - - ./pg-demo-connection/init:/docker-entrypoint-initdb.d - pg-us: container_name: datalens-pg-us image: postgres:16-alpine @@ -79,7 +67,7 @@ services: MASTER_TOKEN: "fake-us-master-token" POSTGRES_DSN_LIST: ${METADATA_POSTGRES_DSN_LIST:-postgres://us:us@pg-us:5432/us-db-ci_purgeable} SKIP_INSTALL_DB_EXTENSIONS: ${METADATA_SKIP_INSTALL_DB_EXTENSIONS:-0} - USE_DEMO_DATA: ${USE_DEMO_DATA:-1} + USE_DEMO_DATA: ${USE_DEMO_DATA:-0} HC: ${HC:-0} NODE_EXTRA_CA_CERTS: /certs/root.crt extra_hosts: @@ -97,7 +85,6 @@ services: - us - control-api - data-api - - pg-demo-connection environment: APP_MODE: "full" APP_ENV: "production" From ab5f62604ca6a6e362d6dd6b9d3d6b4309d5bf4d Mon Sep 17 00:00:00 2001 From: Peter Vinum Date: Thu, 6 Mar 2025 14:08:23 +0100 Subject: [PATCH 22/64] refactor: remove unnecessary extra shadow from monitoring page --- .../free/container/show-free-container-monitoring.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx index 278e0936..968cf9c2 100644 --- a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx @@ -200,7 +200,7 @@ export const ContainerFreeMonitoring = ({ }, [appName]); return ( -
+

Monitoring

From ad3a0198e95c33bfc38de45fd6c8d4585e17fb85 Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sun, 23 Feb 2025 11:23:58 -0500 Subject: [PATCH 23/64] feat: add Hoarder template --- apps/dokploy/public/templates/hoarder.svg | 7 +++ .../templates/hoarder/docker-compose.yml | 45 +++++++++++++++++++ apps/dokploy/templates/hoarder/index.ts | 34 ++++++++++++++ apps/dokploy/templates/templates.ts | 15 +++++++ 4 files changed, 101 insertions(+) create mode 100644 apps/dokploy/public/templates/hoarder.svg create mode 100644 apps/dokploy/templates/hoarder/docker-compose.yml create mode 100644 apps/dokploy/templates/hoarder/index.ts diff --git a/apps/dokploy/public/templates/hoarder.svg b/apps/dokploy/public/templates/hoarder.svg new file mode 100644 index 00000000..dc8f9f4b --- /dev/null +++ b/apps/dokploy/public/templates/hoarder.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/dokploy/templates/hoarder/docker-compose.yml b/apps/dokploy/templates/hoarder/docker-compose.yml new file mode 100644 index 00000000..93e59469 --- /dev/null +++ b/apps/dokploy/templates/hoarder/docker-compose.yml @@ -0,0 +1,45 @@ +services: + web: + image: ghcr.io/hoarder-app/hoarder:0.22.0 + restart: unless-stopped + volumes: + - hoarder-data:/data + ports: + - 3000 + environment: + - DISABLE_SIGNUPS + - NEXTAUTH_URL + - NEXTAUTH_SECRET + - MEILI_ADDR=http://meilisearch:7700 + - BROWSER_WEB_URL=http://chrome:9222 + - DATA_DIR=/data + chrome: + image: gcr.io/zenika-hub/alpine-chrome:124 + restart: unless-stopped + command: + - --no-sandbox + - --disable-gpu + - --disable-dev-shm-usage + - --remote-debugging-address=0.0.0.0 + - --remote-debugging-port=9222 + - --hide-scrollbars + meilisearch: + image: getmeili/meilisearch:v1.6 + restart: unless-stopped + environment: + - MEILI_MASTER_KEY + - MEILI_NO_ANALYTICS="true" + volumes: + - meilisearch-data:/meili_data + healthcheck: + test: + - CMD + - curl + - '-f' + - 'http://127.0.0.1:7700/health' + interval: 2s + timeout: 10s + retries: 15 +volumes: + meilisearch-data: + hoarder-data: \ No newline at end of file diff --git a/apps/dokploy/templates/hoarder/index.ts b/apps/dokploy/templates/hoarder/index.ts new file mode 100644 index 00000000..d1c656e1 --- /dev/null +++ b/apps/dokploy/templates/hoarder/index.ts @@ -0,0 +1,34 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const postgresPassword = generatePassword(); + const nextSecret = generateBase64(32); + const meiliMasterKey = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "web", + }, + ]; + + const envs = [ + `NEXTAUTH_SECRET=${nextSecret}`, + `MEILI_MASTER_KEY=${meiliMasterKey}`, + `NEXTAUTH_URL=http://${mainDomain}`, + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f..b9d78d8b 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -677,6 +677,21 @@ export const templates: TemplateData[] = [ tags: ["self-hosted", "open-source", "manager"], load: () => import("./hi-events/index").then((m) => m.generate), }, + { + id: "hoarder", + name: "Hoarder", + version: "0.22.0", + description: + 'Hoarder is an open source "Bookmark Everything" app that uses AI for automatically tagging the content you throw at it.', + logo: "hoarder.svg", + links: { + github: "https://github.com/hoarder/hoarder", + website: "https://hoarder.app/", + docs: "https://docs.hoarder.app/", + }, + tags: ["self-hosted", "bookmarks", "link-sharing"], + load: () => import("./hoarder/index").then((m) => m.generate), + }, { id: "windows", name: "Windows (dockerized)", From 3ca057c44a9ca8030e25334bdd37949e8864e3ee Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:04:46 -0600 Subject: [PATCH 24/64] chore(issue-template): update bug report label and add cloud version option --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7746a40c..6c055d0c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug Report description: Create a bug report -labels: ["bug"] +labels: ["needs-triage"] body: - type: markdown attributes: @@ -62,6 +62,7 @@ body: - "Docker" - "Remote server" - "Local Development" + - "Cloud Version" validations: required: true - type: dropdown From 457a8e05fdbc8a816f2d5d5efe5db27b11721f72 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:05:31 -0600 Subject: [PATCH 25/64] chore(issue-template): update bug report label emoji --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 6c055d0c..23e8debb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug Report description: Create a bug report -labels: ["needs-triage"] +labels: ["needs-triage🔍"] body: - type: markdown attributes: From 598d0952412e614862e55bdc3d0f34ef5357e1af Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:17:20 -0600 Subject: [PATCH 26/64] fix(gitlab): update repository filtering and connection testing - Change repository filtering to use 'user' kind instead of 'member' - Add console logging for debugging GitLab provider and repository connection - Ensure consistent filtering logic in both getGitlabRepositories and testGitlabConnection --- packages/server/src/utils/providers/gitlab.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index c380a920..bcfa657e 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -266,7 +266,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => { if (groupName) { return full_path.toLowerCase().includes(groupName) && kind === "group"; } - return kind === "member"; + return kind === "user"; }); const mappedRepositories = filteredRepos.map((repo: any) => { return { @@ -409,6 +409,8 @@ export const testGitlabConnection = async ( const gitlabProvider = await findGitlabById(gitlabId); + console.log(gitlabProvider); + const response = await fetch( `${gitlabProvider.gitlabUrl}/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`, { @@ -427,13 +429,15 @@ export const testGitlabConnection = async ( const repositories = await response.json(); + console.log(repositories); + const filteredRepos = repositories.filter((repo: any) => { const { full_path, kind } = repo.namespace; if (groupName) { return full_path.toLowerCase().includes(groupName) && kind === "group"; } - return kind === "member"; + return kind === "user"; }); return filteredRepos.length; From 6166963b00f514b7ebc8bfd728f154deb398fece Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:27:30 -0600 Subject: [PATCH 27/64] fix(gitlab): remove debug console logs from connection testing --- packages/server/src/utils/providers/gitlab.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index bcfa657e..facdeb59 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -409,8 +409,6 @@ export const testGitlabConnection = async ( const gitlabProvider = await findGitlabById(gitlabId); - console.log(gitlabProvider); - const response = await fetch( `${gitlabProvider.gitlabUrl}/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`, { @@ -429,8 +427,6 @@ export const testGitlabConnection = async ( const repositories = await response.json(); - console.log(repositories); - const filteredRepos = repositories.filter((repo: any) => { const { full_path, kind } = repo.namespace; From 29eb490e2defb32555ea25fb2a7948825e19ea02 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:46:21 -0600 Subject: [PATCH 28/64] feat(destinations): add createdAt timestamp and display creation date --- .../destination/handle-destinations.tsx | 99 +- .../destination/show-destinations.tsx | 14 +- .../drizzle/0070_useful_serpent_society.sql | 1 + apps/dokploy/drizzle/meta/0070_snapshot.json | 5126 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + .../dokploy/server/api/routers/destination.ts | 3 +- packages/server/src/db/schema/destination.ts | 3 +- 7 files changed, 5209 insertions(+), 44 deletions(-) create mode 100644 apps/dokploy/drizzle/0070_useful_serpent_society.sql create mode 100644 apps/dokploy/drizzle/meta/0070_snapshot.json diff --git a/apps/dokploy/components/dashboard/settings/destination/handle-destinations.tsx b/apps/dokploy/components/dashboard/settings/destination/handle-destinations.tsx index 4bd78c45..958802e3 100644 --- a/apps/dokploy/components/dashboard/settings/destination/handle-destinations.tsx +++ b/apps/dokploy/components/dashboard/settings/destination/handle-destinations.tsx @@ -39,12 +39,12 @@ import { S3_PROVIDERS } from "./constants"; const addDestination = z.object({ name: z.string().min(1, "Name is required"), - provider: z.string().optional(), - accessKeyId: z.string(), - secretAccessKey: z.string(), - bucket: z.string(), + provider: z.string().min(1, "Provider is required"), + accessKeyId: z.string().min(1, "Access Key Id is required"), + secretAccessKey: z.string().min(1, "Secret Access Key is required"), + bucket: z.string().min(1, "Bucket is required"), region: z.string(), - endpoint: z.string(), + endpoint: z.string().min(1, "Endpoint is required"), serverId: z.string().optional(), }); @@ -129,6 +129,58 @@ export const HandleDestinations = ({ destinationId }: Props) => { ); }); }; + + const handleTestConnection = async (serverId?: string) => { + const result = await form.trigger([ + "provider", + "accessKeyId", + "secretAccessKey", + "bucket", + "endpoint", + ]); + + if (!result) { + const errors = form.formState.errors; + const errorFields = Object.entries(errors) + .map(([field, error]) => `${field}: ${error?.message}`) + .filter(Boolean) + .join("\n"); + + toast.error("Please fill all required fields", { + description: errorFields, + }); + return; + } + + const provider = form.getValues("provider"); + const accessKey = form.getValues("accessKeyId"); + const secretKey = form.getValues("secretAccessKey"); + const bucket = form.getValues("bucket"); + const endpoint = form.getValues("endpoint"); + const region = form.getValues("region"); + + const connectionString = `:s3,provider=${provider},access_key_id=${accessKey},secret_access_key=${secretKey},endpoint=${endpoint}${region ? `,region=${region}` : ""}:${bucket}`; + + await testConnection({ + provider, + accessKey, + bucket, + endpoint, + name: "Test", + region, + secretAccessKey: secretKey, + serverId, + }) + .then(() => { + toast.success("Connection Success"); + }) + .catch((e) => { + toast.error("Error connecting to provider", { + description: `${e.message}\n\nTry manually: rclone ls ${connectionString}`, + }); + }); + }; + return ( @@ -349,26 +401,9 @@ export const HandleDestinations = ({ destinationId }: Props) => { +
+
{ > - - { - toggleLogRotate({ - enable: !isLogRotateActive, - }) - .then(() => { - toast.success( - `Log rotate ${isLogRotateActive ? "activated" : "deactivated"}`, - ); - refetchLogRotate(); - }) - .catch((err) => { - toast.error(err.message); - }); - }} - > - -
-
- {isActive ? ( - - ) : ( -
- - You need to activate requests - + {isActive ? ( + <> +
+ {(dateRange.from || dateRange.to) && ( + + )} + + + + + + { + setDateRange({ + from: range?.from, + to: range?.to, + }); + }} + numberOfMonths={2} + /> + +
- )} - {isActive && } -
+ + + + ) : ( +
+ +
+

+ Requests are not activated +

+

+ Activate requests to see incoming traffic statistics and + monitor your application's usage. After activation, you'll + need to reload Traefik for the changes to take effect. +

+
+
+ )}
diff --git a/apps/dokploy/components/ui/calendar.tsx b/apps/dokploy/components/ui/calendar.tsx new file mode 100644 index 00000000..5141bae9 --- /dev/null +++ b/apps/dokploy/components/ui/calendar.tsx @@ -0,0 +1,68 @@ +import type * as React from "react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { DayPicker } from "react-day-picker"; + +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; + +export type CalendarProps = React.ComponentProps; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + ( + + ), + IconRight: ({ className, ...props }) => ( + + ), + }} + {...props} + /> + ); +} +Calendar.displayName = "Calendar"; + +export { Calendar }; diff --git a/apps/dokploy/drizzle/0071_flaky_black_queen.sql b/apps/dokploy/drizzle/0071_flaky_black_queen.sql new file mode 100644 index 00000000..8f04063b --- /dev/null +++ b/apps/dokploy/drizzle/0071_flaky_black_queen.sql @@ -0,0 +1 @@ +ALTER TABLE "user_temp" ADD COLUMN "logCleanupCron" text; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0071_snapshot.json b/apps/dokploy/drizzle/meta/0071_snapshot.json new file mode 100644 index 00000000..deb61325 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0071_snapshot.json @@ -0,0 +1,5132 @@ +{ + "id": "44cb886c-d31a-4b3d-b70e-da306c74dcf5", + "prevId": "53fc370e-731b-4bfe-874f-9ce725650082", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "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 + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "messageThreadId": { + "name": "messageThreadId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai": { + "name": "ai", + "schema": "", + "columns": { + "aiId": { + "name": "aiId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiUrl": { + "name": "apiUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isEnabled": { + "name": "isEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ai_organizationId_organization_id_fk": { + "name": "ai_organizationId_organization_id_fk", + "tableFrom": "ai", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "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": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_temp_id_fk": { + "name": "apikey_user_id_user_temp_id_fk", + "tableFrom": "apikey", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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[]" + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + "railpack" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_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 3f65ce22..11b2935f 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -498,6 +498,13 @@ "when": 1741322697251, "tag": "0070_useful_serpent_society", "breakpoints": true + }, + { + "idx": 71, + "version": "7", + "when": 1741460060541, + "tag": "0071_flaky_black_queen", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index fc1255fc..24007599 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -28,7 +28,6 @@ import { getDokployImageTag, getUpdateData, initializeTraefik, - logRotationManager, parseRawConfig, paths, prepareEnvironmentVariables, @@ -53,6 +52,9 @@ import { writeConfig, writeMainConfig, writeTraefikConfigInPath, + startLogCleanup, + stopLogCleanup, + getLogCleanupStatus, } from "@dokploy/server"; import { checkGPUStatus, setupGPUSupport } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; @@ -577,48 +579,43 @@ export const settingsRouter = createTRPCRouter({ totalCount: 0, }; } - const rawConfig = readMonitoringConfig(); + const rawConfig = readMonitoringConfig( + !!input.dateRange?.start && !!input.dateRange?.end, + ); + const parsedConfig = parseRawConfig( rawConfig as string, input.page, input.sort, input.search, input.status, + input.dateRange, ); return parsedConfig; }), - readStats: adminProcedure.query(() => { - if (IS_CLOUD) { - return []; - } - const rawConfig = readMonitoringConfig(); - const processedLogs = processLogs(rawConfig as string); - return processedLogs || []; - }), - getLogRotateStatus: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - return await logRotationManager.getStatus(); - }), - toggleLogRotate: adminProcedure + readStats: adminProcedure .input( - z.object({ - enable: z.boolean(), - }), + z + .object({ + dateRange: z + .object({ + start: z.string().optional(), + end: z.string().optional(), + }) + .optional(), + }) + .optional(), ) - .mutation(async ({ input }) => { + .query(({ input }) => { if (IS_CLOUD) { - return true; + return []; } - if (input.enable) { - await logRotationManager.activate(); - } else { - await logRotationManager.deactivate(); - } - - return true; + const rawConfig = readMonitoringConfig( + !!input?.dateRange?.start || !!input?.dateRange?.end, + ); + const processedLogs = processLogs(rawConfig as string, input?.dateRange); + return processedLogs || []; }), haveActivateRequests: adminProcedure.query(async () => { if (IS_CLOUD) { @@ -820,10 +817,20 @@ export const settingsRouter = createTRPCRouter({ }); } }), + updateLogCleanup: adminProcedure + .input( + z.object({ + cronExpression: z.string().nullable(), + }), + ) + .mutation(async ({ input }) => { + if (input.cronExpression) { + return startLogCleanup(input.cronExpression); + } + return stopLogCleanup(); + }), + + getLogCleanupStatus: adminProcedure.query(async () => { + return getLogCleanupStatus(); + }), }); -// { -// "Parallelism": 1, -// "Delay": 10000000000, -// "FailureAction": "rollback", -// "Order": "start-first" -// } diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 9307127a..b1303285 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -53,7 +53,7 @@ export const users_temp = pgTable("user_temp", { letsEncryptEmail: text("letsEncryptEmail"), sshPrivateKey: text("sshPrivateKey"), enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), - enableLogRotation: boolean("enableLogRotation").notNull().default(false), + logCleanupCron: text("logCleanupCron"), // Metrics enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), metricsConfig: jsonb("metricsConfig") @@ -250,6 +250,12 @@ export const apiReadStatsLogs = z.object({ status: z.string().array().optional(), search: z.string().optional(), sort: z.object({ id: z.string(), desc: z.boolean() }).optional(), + dateRange: z + .object({ + start: z.string().optional(), + end: z.string().optional(), + }) + .optional(), }); export const apiUpdateWebServerMonitoring = z.object({ @@ -305,4 +311,5 @@ export const apiUpdateUser = createSchema.partial().extend({ }), }) .optional(), + logCleanupCron: z.string().optional().nullable(), }); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index f74b8d9d..c8337dc2 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -116,3 +116,9 @@ export * from "./db/validations/index"; export * from "./utils/gpu-setup"; export * from "./lib/auth"; + +export { + startLogCleanup, + stopLogCleanup, + getLogCleanupStatus, +} from "./utils/access-log/handler"; diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 69d0cc68..fac18794 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -1,121 +1,77 @@ -import { IS_CLOUD, paths } from "@dokploy/server/constants"; -import { type RotatingFileStream, createStream } from "rotating-file-stream"; +import { paths } from "@dokploy/server/constants"; import { execAsync } from "../process/execAsync"; import { findAdmin } from "@dokploy/server/services/admin"; import { updateUser } from "@dokploy/server/services/user"; +import { scheduleJob, scheduledJobs } from "node-schedule"; -class LogRotationManager { - private static instance: LogRotationManager; - private stream: RotatingFileStream | null = null; +const LOG_CLEANUP_JOB_NAME = "access-log-cleanup"; - private constructor() { - if (IS_CLOUD) { - return; - } - this.initialize().catch(console.error); - } - - public static getInstance(): LogRotationManager { - if (!LogRotationManager.instance) { - LogRotationManager.instance = new LogRotationManager(); - } - return LogRotationManager.instance; - } - - private async initialize(): Promise { - const isActive = await this.getStateFromDB(); - if (isActive) { - await this.activateStream(); - } - } - - private async getStateFromDB(): Promise { - const admin = await findAdmin(); - return admin?.user.enableLogRotation ?? false; - } - - private async setStateInDB(active: boolean): Promise { - const admin = await findAdmin(); - if (!admin) { - return; - } - await updateUser(admin.user.id, { - enableLogRotation: active, - }); - } - - private async activateStream(): Promise { +export const startLogCleanup = async ( + cronExpression = "0 0 * * *", +): Promise => { + try { const { DYNAMIC_TRAEFIK_PATH } = paths(); - if (this.stream) { - await this.deactivateStream(); + + const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME]; + if (existingJob) { + existingJob.cancel(); } - this.stream = createStream("access.log", { - size: "100M", - interval: "1d", - path: DYNAMIC_TRAEFIK_PATH, - rotate: 6, - compress: "gzip", - }); + scheduleJob(LOG_CLEANUP_JOB_NAME, cronExpression, async () => { + try { + await execAsync( + `tail -n 1000 ${DYNAMIC_TRAEFIK_PATH}/access.log > ${DYNAMIC_TRAEFIK_PATH}/access.log.tmp && mv ${DYNAMIC_TRAEFIK_PATH}/access.log.tmp ${DYNAMIC_TRAEFIK_PATH}/access.log`, + ); - this.stream.on("rotation", this.handleRotation.bind(this)); - } - - private async deactivateStream(): Promise { - return new Promise((resolve) => { - if (this.stream) { - this.stream.end(() => { - this.stream = null; - resolve(); - }); - } else { - resolve(); + await execAsync("docker exec dokploy-traefik kill -USR1 1"); + } catch (error) { + console.error("Error during log cleanup:", error); } }); - } - public async activate(): Promise { - const currentState = await this.getStateFromDB(); - if (currentState) { - return true; + const admin = await findAdmin(); + if (admin) { + await updateUser(admin.user.id, { + logCleanupCron: cronExpression, + }); } - await this.setStateInDB(true); - await this.activateStream(); return true; + } catch (_) { + return false; } +}; - public async deactivate(): Promise { - console.log("Deactivating log rotation..."); - const currentState = await this.getStateFromDB(); - if (!currentState) { - console.log("Log rotation is already inactive in DB"); - return true; +export const stopLogCleanup = async (): Promise => { + try { + const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME]; + if (existingJob) { + existingJob.cancel(); + } + + // Update database + const admin = await findAdmin(); + if (admin) { + await updateUser(admin.user.id, { + logCleanupCron: null, + }); } - await this.setStateInDB(false); - await this.deactivateStream(); - console.log("Log rotation deactivated successfully"); return true; + } catch (error) { + console.error("Error stopping log cleanup:", error); + return false; } +}; - private async handleRotation() { - try { - const status = await this.getStatus(); - if (!status) { - await this.deactivateStream(); - } - await execAsync( - "docker kill -s USR1 $(docker ps -q --filter name=dokploy-traefik)", - ); - console.log("USR1 Signal send to Traefik"); - } catch (error) { - console.error("Error sending USR1 Signal to Traefik:", error); - } - } - public async getStatus(): Promise { - const dbState = await this.getStateFromDB(); - return dbState; - } -} -export const logRotationManager = LogRotationManager.getInstance(); +export const getLogCleanupStatus = async (): Promise<{ + enabled: boolean; + cronExpression: string | null; +}> => { + const admin = await findAdmin(); + const cronExpression = admin?.user.logCleanupCron ?? null; + return { + enabled: cronExpression !== null, + cronExpression, + }; +}; diff --git a/packages/server/src/utils/access-log/utils.ts b/packages/server/src/utils/access-log/utils.ts index 0a322f95..9e9070b6 100644 --- a/packages/server/src/utils/access-log/utils.ts +++ b/packages/server/src/utils/access-log/utils.ts @@ -6,14 +6,21 @@ interface HourlyData { count: number; } -export function processLogs(logString: string): HourlyData[] { +export function processLogs( + logString: string, + dateRange?: { start?: string; end?: string }, +): HourlyData[] { if (_.isEmpty(logString)) { return []; } const hourlyData = _(logString) .split("\n") - .compact() + .filter((line) => { + const trimmed = line.trim(); + // Check if the line starts with { and ends with } to ensure it's a potential JSON object + return trimmed !== "" && trimmed.startsWith("{") && trimmed.endsWith("}"); + }) .map((entry) => { try { const log: LogEntry = JSON.parse(entry); @@ -21,6 +28,20 @@ export function processLogs(logString: string): HourlyData[] { return null; } const date = new Date(log.StartUTC); + + if (dateRange?.start || dateRange?.end) { + const logDate = date.getTime(); + const start = dateRange?.start + ? new Date(dateRange.start).getTime() + : 0; + const end = dateRange?.end + ? new Date(dateRange.end).getTime() + : Number.POSITIVE_INFINITY; + if (logDate < start || logDate > end) { + return null; + } + } + return `${date.toISOString().slice(0, 13)}:00:00Z`; } catch (error) { console.error("Error parsing log entry:", error); @@ -51,21 +72,46 @@ export function parseRawConfig( sort?: SortInfo, search?: string, status?: string[], + dateRange?: { start?: string; end?: string }, ): { data: LogEntry[]; totalCount: number } { try { if (_.isEmpty(rawConfig)) { return { data: [], totalCount: 0 }; } + // Split logs into chunks to avoid memory issues let parsedLogs = _(rawConfig) .split("\n") + .filter((line) => { + const trimmed = line.trim(); + return ( + trimmed !== "" && trimmed.startsWith("{") && trimmed.endsWith("}") + ); + }) + .map((line) => { + try { + return JSON.parse(line) as LogEntry; + } catch (error) { + console.error("Error parsing log line:", error); + return null; + } + }) .compact() - .map((line) => JSON.parse(line) as LogEntry) .value(); - parsedLogs = parsedLogs.filter( - (log) => log.ServiceName !== "dokploy-service-app@file", - ); + // Apply date range filter if provided + if (dateRange?.start || dateRange?.end) { + parsedLogs = parsedLogs.filter((log) => { + const logDate = new Date(log.StartUTC).getTime(); + const start = dateRange?.start + ? new Date(dateRange.start).getTime() + : 0; + const end = dateRange?.end + ? new Date(dateRange.end).getTime() + : Number.POSITIVE_INFINITY; + return logDate >= start && logDate <= end; + }); + } if (search) { parsedLogs = parsedLogs.filter((log) => @@ -78,6 +124,7 @@ export function parseRawConfig( status.some((range) => isStatusInRange(log.DownstreamStatus, range)), ); } + const totalCount = parsedLogs.length; if (sort) { @@ -101,6 +148,7 @@ export function parseRawConfig( throw new Error("Failed to parse rawConfig"); } } + const isStatusInRange = (status: number, range: string) => { switch (range) { case "info": diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index 7699a42e..49bf4f3b 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -12,6 +12,7 @@ import { runMongoBackup } from "./mongo"; import { runMySqlBackup } from "./mysql"; import { runPostgresBackup } from "./postgres"; import { findAdmin } from "../../services/admin"; +import { startLogCleanup } from "../access-log/handler"; export const initCronJobs = async () => { console.log("Setting up cron jobs...."); @@ -168,4 +169,8 @@ export const initCronJobs = async () => { } } } + + if (admin?.user.logCleanupCron) { + await startLogCleanup(admin.user.logCleanupCron); + } }; diff --git a/packages/server/src/utils/traefik/application.ts b/packages/server/src/utils/traefik/application.ts index 61150abf..6220d774 100644 --- a/packages/server/src/utils/traefik/application.ts +++ b/packages/server/src/utils/traefik/application.ts @@ -137,12 +137,44 @@ export const readRemoteConfig = async (serverId: string, appName: string) => { } }; -export const readMonitoringConfig = () => { +export const readMonitoringConfig = (readAll = false) => { const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log"); if (fs.existsSync(configPath)) { - const yamlStr = fs.readFileSync(configPath, "utf8"); - return yamlStr; + if (!readAll) { + // Read first 500 lines + let content = ""; + let chunk = ""; + let validCount = 0; + + for (const char of fs.readFileSync(configPath, "utf8")) { + chunk += char; + if (char === "\n") { + try { + const trimmed = chunk.trim(); + if ( + trimmed !== "" && + trimmed.startsWith("{") && + trimmed.endsWith("}") + ) { + const log = JSON.parse(trimmed); + if (log.ServiceName !== "dokploy-service-app@file") { + content += chunk; + validCount++; + if (validCount >= 500) { + break; + } + } + } + } catch { + // Ignore invalid JSON + } + chunk = ""; + } + } + return content; + } + return fs.readFileSync(configPath, "utf8"); } return null; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cef1fa0d..54d8577c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6282,6 +6282,7 @@ packages: oslo@1.2.0: resolution: {integrity: sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==} + deprecated: Package is no longer supported. Please see https://oslojs.dev for the successor project. otpauth@9.3.4: resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==} From 2c3ff5794dfc45bf96ba0687226d9ed9880279e9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 14:23:52 -0600 Subject: [PATCH 37/64] refactor(user): update log cleanup configuration - Replace enableLogRotation boolean with logCleanupCron configuration - Align with recent log scheduling and monitoring improvements --- .../__test__/traefik/server/update-server-config.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index c72d7254..f33b37fd 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -47,7 +47,7 @@ const baseAdmin: User = { letsEncryptEmail: null, sshPrivateKey: null, enableDockerCleanup: false, - enableLogRotation: false, + logCleanupCron: null, serversQuantity: 0, stripeCustomerId: "", stripeSubscriptionId: "", From a96af6536bbbde6160098a3142d89a23854e7d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Gonz=C3=A1lez=20Tar=C3=ADn?= Date: Sat, 8 Mar 2025 21:42:59 +0100 Subject: [PATCH 38/64] fix: database empty backups fix --- packages/server/src/utils/databases/mariadb.ts | 2 +- packages/server/src/utils/databases/mongo.ts | 2 +- packages/server/src/utils/databases/mysql.ts | 4 ++-- packages/server/src/utils/databases/postgres.ts | 2 +- packages/server/src/utils/databases/redis.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/src/utils/databases/mariadb.ts b/packages/server/src/utils/databases/mariadb.ts index ead5a618..5f9befe6 100644 --- a/packages/server/src/utils/databases/mariadb.ts +++ b/packages/server/src/utils/databases/mariadb.ts @@ -31,7 +31,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => { mounts, } = mariadb; - const defaultMariadbEnv = `MARIADB_DATABASE=${databaseName}\nMARIADB_USER=${databaseUser}\nMARIADB_PASSWORD=${databasePassword}\nMARIADB_ROOT_PASSWORD=${databaseRootPassword}${ + const defaultMariadbEnv = `MARIADB_DATABASE="${databaseName}"\nMARIADB_USER="${databaseUser}"\nMARIADB_PASSWORD="${databasePassword}"\nMARIADB_ROOT_PASSWORD="${databaseRootPassword}"${ env ? `\n${env}` : "" }`; const resources = calculateResources({ diff --git a/packages/server/src/utils/databases/mongo.ts b/packages/server/src/utils/databases/mongo.ts index ace9c972..58f8624a 100644 --- a/packages/server/src/utils/databases/mongo.ts +++ b/packages/server/src/utils/databases/mongo.ts @@ -77,7 +77,7 @@ fi ${command ?? "wait $MONGOD_PID"}`; - const defaultMongoEnv = `MONGO_INITDB_ROOT_USERNAME=${databaseUser}\nMONGO_INITDB_ROOT_PASSWORD=${databasePassword}${replicaSets ? "\nMONGO_INITDB_DATABASE=admin" : ""}${ + const defaultMongoEnv = `MONGO_INITDB_ROOT_USERNAME="${databaseUser}"\nMONGO_INITDB_ROOT_PASSWORD="${databasePassword}"${replicaSets ? "\nMONGO_INITDB_DATABASE=admin" : ""}${ env ? `\n${env}` : "" }`; diff --git a/packages/server/src/utils/databases/mysql.ts b/packages/server/src/utils/databases/mysql.ts index de28cfe6..76f01e5e 100644 --- a/packages/server/src/utils/databases/mysql.ts +++ b/packages/server/src/utils/databases/mysql.ts @@ -34,10 +34,10 @@ export const buildMysql = async (mysql: MysqlNested) => { const defaultMysqlEnv = databaseUser !== "root" - ? `MYSQL_USER=${databaseUser}\nMYSQL_DATABASE=${databaseName}\nMYSQL_PASSWORD=${databasePassword}\nMYSQL_ROOT_PASSWORD=${databaseRootPassword}${ + ? `MYSQL_USER="${databaseUser}"\nMYSQL_DATABASE="${databaseName}"\nMYSQL_PASSWORD="${databasePassword}"\nMYSQL_ROOT_PASSWORD="${databaseRootPassword}"${ env ? `\n${env}` : "" }` - : `MYSQL_DATABASE=${databaseName}\nMYSQL_ROOT_PASSWORD=${databaseRootPassword}${ + : `MYSQL_DATABASE="${databaseName}"\nMYSQL_ROOT_PASSWORD="${databaseRootPassword}"${ env ? `\n${env}` : "" }`; const resources = calculateResources({ diff --git a/packages/server/src/utils/databases/postgres.ts b/packages/server/src/utils/databases/postgres.ts index a8930a1c..36ac09ae 100644 --- a/packages/server/src/utils/databases/postgres.ts +++ b/packages/server/src/utils/databases/postgres.ts @@ -30,7 +30,7 @@ export const buildPostgres = async (postgres: PostgresNested) => { mounts, } = postgres; - const defaultPostgresEnv = `POSTGRES_DB=${databaseName}\nPOSTGRES_USER=${databaseUser}\nPOSTGRES_PASSWORD=${databasePassword}${ + const defaultPostgresEnv = `POSTGRES_DB="${databaseName}"\nPOSTGRES_USER="${databaseUser}"\nPOSTGRES_PASSWORD="${databasePassword}"${ env ? `\n${env}` : "" }`; const resources = calculateResources({ diff --git a/packages/server/src/utils/databases/redis.ts b/packages/server/src/utils/databases/redis.ts index aef86280..3a8ad086 100644 --- a/packages/server/src/utils/databases/redis.ts +++ b/packages/server/src/utils/databases/redis.ts @@ -28,7 +28,7 @@ export const buildRedis = async (redis: RedisNested) => { mounts, } = redis; - const defaultRedisEnv = `REDIS_PASSWORD=${databasePassword}${ + const defaultRedisEnv = `REDIS_PASSWORD="${databasePassword}"${ env ? `\n${env}` : "" }`; const resources = calculateResources({ From c89f957133665a449b7bfd270648563c6b3ca67f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 15:31:08 -0600 Subject: [PATCH 39/64] refactor(ui): enhance update server button and sidebar layout - Improve UpdateServer component with flexible rendering and tooltip support - Modify sidebar layout to integrate update server button more cleanly - Add conditional rendering and styling for update availability - Introduce more consistent button and tooltip interactions --- .../settings/web-server/update-server.tsx | 84 ++++++++++++++----- apps/dokploy/components/layouts/side.tsx | 16 ++-- .../components/layouts/update-server.tsx | 60 +++++++++++-- 3 files changed, 119 insertions(+), 41 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx index 2cfc459b..1b7eb91a 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx @@ -5,6 +5,12 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import type { IUpdateData } from "@dokploy/server/index"; import { @@ -24,9 +30,17 @@ import { UpdateWebServer } from "./update-webserver"; interface Props { updateData?: IUpdateData; + children?: React.ReactNode; + isOpen?: boolean; + onOpenChange?: (open: boolean) => void; } -export const UpdateServer = ({ updateData }: Props) => { +export const UpdateServer = ({ + updateData, + children, + isOpen: isOpenProp, + onOpenChange: onOpenChangeProp, +}: Props) => { const [hasCheckedUpdate, setHasCheckedUpdate] = useState(!!updateData); const [isUpdateAvailable, setIsUpdateAvailable] = useState( !!updateData?.updateAvailable, @@ -35,10 +49,10 @@ export const UpdateServer = ({ updateData }: Props) => { api.settings.getUpdateData.useMutation(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); const { data: releaseTag } = api.settings.getReleaseTag.useQuery(); - const [isOpen, setIsOpen] = useState(false); const [latestVersion, setLatestVersion] = useState( updateData?.latestVersion ?? "", ); + const [isOpenInternal, setIsOpenInternal] = useState(false); const handleCheckUpdates = async () => { try { @@ -65,28 +79,52 @@ export const UpdateServer = ({ updateData }: Props) => { } }; + const isOpen = isOpenInternal || isOpenProp; + const onOpenChange = (open: boolean) => { + setIsOpenInternal(open); + onOpenChangeProp?.(open); + }; + return ( - + - + {children ? ( + children + ) : ( + + + + + + {updateData && ( + +

Update Available

+
+ )} +
+
+ )}
@@ -217,7 +255,7 @@ export const UpdateServer = ({ updateData }: Props) => {
- {isUpdateAvailable ? ( diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 638f7f57..dd70a1c2 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -37,8 +37,6 @@ import { BreadcrumbItem, BreadcrumbLink, BreadcrumbList, - BreadcrumbPage, - BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import { Collapsible, @@ -1017,18 +1015,16 @@ export default function Page({ children }: Props) { ))} - {!isCloud && auth?.role === "owner" && ( - - - - - - )} - + + {!isCloud && auth?.role === "owner" && ( + + + + )} diff --git a/apps/dokploy/components/layouts/update-server.tsx b/apps/dokploy/components/layouts/update-server.tsx index 5d797885..9e37c80b 100644 --- a/apps/dokploy/components/layouts/update-server.tsx +++ b/apps/dokploy/components/layouts/update-server.tsx @@ -3,7 +3,14 @@ import type { IUpdateData } from "@dokploy/server/index"; import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; import UpdateServer from "../dashboard/settings/web-server/update-server"; - +import { Button } from "../ui/button"; +import { Download } from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../ui/tooltip"; const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; export const UpdateServerButton = () => { @@ -15,6 +22,7 @@ export const UpdateServerButton = () => { const { data: isCloud } = api.settings.isCloud.useQuery(); const { mutateAsync: getUpdateData } = api.settings.getUpdateData.useMutation(); + const [isOpen, setIsOpen] = useState(false); const checkUpdatesIntervalRef = useRef(null); @@ -69,11 +77,47 @@ export const UpdateServerButton = () => { }; }, []); - return ( - updateData.updateAvailable && ( -
- -
- ) - ); + return updateData.updateAvailable ? ( +
+ + + + + + + {updateData && ( + +

Update Available

+
+ )} +
+
+
+
+ ) : null; }; From 08d7c4e1c3ad5df3fd473399de981a92d8a86282 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 17:56:20 -0600 Subject: [PATCH 40/64] refactor(docker): sort container lists by name - Add sorting to container retrieval methods in docker service - Ensure consistent container list ordering across different container fetching functions - Improve readability and predictability of container list results --- packages/server/src/services/docker.ts | 160 +++++++++++++------------ 1 file changed, 84 insertions(+), 76 deletions(-) diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index a4a3b0b5..d2f4de53 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -136,24 +136,26 @@ export const getContainersByAppNameMatch = async ( result = stdout.trim().split("\n"); } - const containers = result.map((line) => { - const parts = line.split(" | "); - const containerId = parts[0] - ? parts[0].replace("CONTAINER ID : ", "").trim() - : "No container id"; - const name = parts[1] - ? parts[1].replace("Name: ", "").trim() - : "No container name"; + const containers = result + .map((line) => { + const parts = line.split(" | "); + const containerId = parts[0] + ? parts[0].replace("CONTAINER ID : ", "").trim() + : "No container id"; + const name = parts[1] + ? parts[1].replace("Name: ", "").trim() + : "No container name"; - const state = parts[2] - ? parts[2].replace("State: ", "").trim() - : "No state"; - return { - containerId, - name, - state, - }; - }); + const state = parts[2] + ? parts[2].replace("State: ", "").trim() + : "No state"; + return { + containerId, + name, + state, + }; + }) + .sort((a, b) => a.name.localeCompare(b.name)); return containers || []; } catch (_error) {} @@ -190,28 +192,30 @@ export const getStackContainersByAppName = async ( result = stdout.trim().split("\n"); } - const containers = result.map((line) => { - const parts = line.split(" | "); - const containerId = parts[0] - ? parts[0].replace("CONTAINER ID : ", "").trim() - : "No container id"; - const name = parts[1] - ? parts[1].replace("Name: ", "").trim() - : "No container name"; + const containers = result + .map((line) => { + const parts = line.split(" | "); + const containerId = parts[0] + ? parts[0].replace("CONTAINER ID : ", "").trim() + : "No container id"; + const name = parts[1] + ? parts[1].replace("Name: ", "").trim() + : "No container name"; - const state = parts[2] - ? parts[2].replace("State: ", "").trim().toLowerCase() - : "No state"; - const node = parts[3] - ? parts[3].replace("Node: ", "").trim() - : "No specific node"; - return { - containerId, - name, - state, - node, - }; - }); + const state = parts[2] + ? parts[2].replace("State: ", "").trim().toLowerCase() + : "No state"; + const node = parts[3] + ? parts[3].replace("Node: ", "").trim() + : "No specific node"; + return { + containerId, + name, + state, + node, + }; + }) + .sort((a, b) => a.name.localeCompare(b.name)); return containers || []; } catch (_error) {} @@ -249,29 +253,31 @@ export const getServiceContainersByAppName = async ( result = stdout.trim().split("\n"); } - const containers = result.map((line) => { - const parts = line.split(" | "); - const containerId = parts[0] - ? parts[0].replace("CONTAINER ID : ", "").trim() - : "No container id"; - const name = parts[1] - ? parts[1].replace("Name: ", "").trim() - : "No container name"; + const containers = result + .map((line) => { + const parts = line.split(" | "); + const containerId = parts[0] + ? parts[0].replace("CONTAINER ID : ", "").trim() + : "No container id"; + const name = parts[1] + ? parts[1].replace("Name: ", "").trim() + : "No container name"; - const state = parts[2] - ? parts[2].replace("State: ", "").trim().toLowerCase() - : "No state"; + const state = parts[2] + ? parts[2].replace("State: ", "").trim().toLowerCase() + : "No state"; - const node = parts[3] - ? parts[3].replace("Node: ", "").trim() - : "No specific node"; - return { - containerId, - name, - state, - node, - }; - }); + const node = parts[3] + ? parts[3].replace("Node: ", "").trim() + : "No specific node"; + return { + containerId, + name, + state, + node, + }; + }) + .sort((a, b) => a.name.localeCompare(b.name)); return containers || []; } catch (_error) {} @@ -306,23 +312,25 @@ export const getContainersByAppLabel = async ( const lines = stdout.trim().split("\n"); - const containers = lines.map((line) => { - const parts = line.split(" | "); - const containerId = parts[0] - ? parts[0].replace("CONTAINER ID : ", "").trim() - : "No container id"; - const name = parts[1] - ? parts[1].replace("Name: ", "").trim() - : "No container name"; - const state = parts[2] - ? parts[2].replace("State: ", "").trim() - : "No state"; - return { - containerId, - name, - state, - }; - }); + const containers = lines + .map((line) => { + const parts = line.split(" | "); + const containerId = parts[0] + ? parts[0].replace("CONTAINER ID : ", "").trim() + : "No container id"; + const name = parts[1] + ? parts[1].replace("Name: ", "").trim() + : "No container name"; + const state = parts[2] + ? parts[2].replace("State: ", "").trim() + : "No state"; + return { + containerId, + name, + state, + }; + }) + .sort((a, b) => a.name.localeCompare(b.name)); return containers || []; } catch (_error) {} From 832fa526ddeb5b609b56ec66a8bd78ae466a8d53 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:08:49 -0600 Subject: [PATCH 41/64] refactor(ui): improve environment code editor styling and layout - Adjust CodeEditor component wrapper and class names - Enhance font and styling for environment configuration - Optimize form item and control rendering --- .../application/environment/show-enviroment.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx index ba20db31..cc208d9b 100644 --- a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx @@ -132,8 +132,8 @@ export const ShowEnvironment = ({ id, type }: Props) => { control={form.control} name="environment" render={({ field }) => ( - - + + { } language="properties" disabled={isEnvVisible} + className="font-mono" + wrapperClassName="compose-file-editor" placeholder={`NODE_ENV=production PORT=3000 -`} - className="h-96 font-mono" + `} {...field} /> - )} From 01c33ad98b5f826b2a126515ab597018315db6c4 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:26:39 -0600 Subject: [PATCH 42/64] feat(ui): add tooltips to service action buttons for improved user guidance - Integrate tooltips for Deploy, Rebuild, Start, and Stop buttons across various service components - Provide context-specific explanations for each action button - Enhance user understanding of service management actions - Consistent tooltip styling and implementation using TooltipProvider --- .../dashboard/application/general/show.tsx | 266 ++++++++++------ .../dashboard/compose/general/actions.tsx | 205 +++++++----- .../mariadb/general/show-general-mariadb.tsx | 205 ++++++++---- .../mongo/general/show-general-mongo.tsx | 216 ++++++++----- .../mysql/general/show-general-mysql.tsx | 206 ++++++++---- .../general/show-general-postgres.tsx | 299 +++++++++++------- .../redis/general/show-general-redis.tsx | 208 ++++++++---- 7 files changed, 1047 insertions(+), 558 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 8989ca19..28a50a01 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -4,8 +4,22 @@ import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Switch } from "@/components/ui/switch"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { Ban, CheckCircle2, Hammer, RefreshCcw, Terminal } from "lucide-react"; +import { + Ban, + CheckCircle2, + Hammer, + HelpCircle, + RefreshCcw, + Terminal, +} from "lucide-react"; import { useRouter } from "next/router"; import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; @@ -41,128 +55,188 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { Deploy Settings - { - await deploy({ - applicationId: applicationId, - }) - .then(() => { - toast.success("Application deployed successfully"); - refetch(); - router.push( - `/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments`, - ); - }) - .catch(() => { - toast.error("Error deploying application"); - }); - }} - > - - - { - await reload({ - applicationId: applicationId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Application reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading application"); - }); - }} - > - - - { - await redeploy({ - applicationId: applicationId, - }) - .then(() => { - toast.success("Application rebuilt successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error rebuilding application"); - }); - }} - > - - - - {data?.applicationStatus === "idle" ? ( + { - await start({ + await deploy({ applicationId: applicationId, }) .then(() => { - toast.success("Application started successfully"); + toast.success("Application deployed successfully"); refetch(); + router.push( + `/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments`, + ); }) .catch(() => { - toast.error("Error starting application"); + toast.error("Error deploying application"); }); }} > - - ) : ( { - await stop({ + await reload({ applicationId: applicationId, + appName: data?.appName || "", }) .then(() => { - toast.success("Application stopped successfully"); + toast.success("Application reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error stopping application"); + toast.error("Error reloading application"); }); }} > - - )} + { + await redeploy({ + applicationId: applicationId, + }) + .then(() => { + toast.success("Application rebuilt successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error rebuilding application"); + }); + }} + > + + + + {data?.applicationStatus === "idle" ? ( + { + await start({ + applicationId: applicationId, + }) + .then(() => { + toast.success("Application started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting application"); + }); + }} + > + + + ) : ( + { + await stop({ + applicationId: applicationId, + }) + .then(() => { + toast.success("Application stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping application"); + }); + }} + > + + + )} + { api.compose.stop.useMutation(); return (
- { - await deploy({ - composeId: composeId, - }) - .then(() => { - toast.success("Compose deployed successfully"); - refetch(); - router.push( - `/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`, - ); - }) - .catch(() => { - toast.error("Error deploying compose"); - }); - }} - > - - - { - await redeploy({ - composeId: composeId, - }) - .then(() => { - toast.success("Compose rebuilt successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error rebuilding compose"); - }); - }} - > - - - {data?.composeType === "docker-compose" && - data?.composeStatus === "idle" ? ( + { - await start({ + await deploy({ composeId: composeId, }) .then(() => { - toast.success("Compose started successfully"); + toast.success("Compose deployed successfully"); refetch(); + router.push( + `/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`, + ); }) .catch(() => { - toast.error("Error starting compose"); + toast.error("Error deploying compose"); }); }} > - - ) : ( { - await stop({ + await redeploy({ composeId: composeId, }) .then(() => { - toast.success("Compose stopped successfully"); + toast.success("Compose rebuilt successfully"); refetch(); }) .catch(() => { - toast.error("Error stopping compose"); + toast.error("Error rebuilding compose"); }); }} > - - )} - + {data?.composeType === "docker-compose" && + data?.composeStatus === "idle" ? ( + { + await start({ + composeId: composeId, + }) + .then(() => { + toast.success("Compose started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting compose"); + }); + }} + > + + + ) : ( + { + await stop({ + composeId: composeId, + }) + .then(() => { + toast.success("Compose stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping compose"); + }); + }} + > + + + )} + { Deploy Settings - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mariadbId: mariadbId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mariadb reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mariadb"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( + { - await start({ - mariadbId: mariadbId, - }) - .then(() => { - toast.success("Mariadb started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Mariadb"); - }); + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); }} > - - ) : ( { - await stop({ + await reload({ mariadbId: mariadbId, + appName: data?.appName || "", }) .then(() => { - toast.success("Mariadb stopped successfully"); + toast.success("Mariadb reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error stopping Mariadb"); + toast.error("Error reloading Mariadb"); }); }} > - - )} + {data?.applicationStatus === "idle" ? ( + { + await start({ + mariadbId: mariadbId, + }) + .then(() => { + toast.success("Mariadb started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mariadb"); + }); + }} + > + + + ) : ( + { + await stop({ + mariadbId: mariadbId, + }) + .then(() => { + toast.success("Mariadb stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mariadb"); + }); + }} + > + + + )} + { Deploy Settings - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mongoId: mongoId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mongo reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mongo"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( + { - await start({ + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); + }} + > + + + { + await reload({ mongoId: mongoId, + appName: data?.appName || "", }) .then(() => { - toast.success("Mongo started successfully"); + toast.success("Mongo reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error starting Mongo"); + toast.error("Error reloading Mongo"); }); }} > - - ) : ( - { - await stop({ - mongoId: mongoId, - }) - .then(() => { - toast.success("Mongo stopped successfully"); - refetch(); + {data?.applicationStatus === "idle" ? ( + { + await start({ + mongoId: mongoId, }) - .catch(() => { - toast.error("Error stopping Mongo"); - }); - }} - > - - - )} + .then(() => { + toast.success("Mongo started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mongo"); + }); + }} + > + + + ) : ( + { + await stop({ + mongoId: mongoId, + }) + .then(() => { + toast.success("Mongo stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mongo"); + }); + }} + > + + + )} + { Deploy Settings - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mysqlId: mysqlId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mysql reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mysql"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( + { - await start({ - mysqlId: mysqlId, - }) - .then(() => { - toast.success("Mysql started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Mysql"); - }); + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); }} > - - ) : ( { - await stop({ + await reload({ mysqlId: mysqlId, + appName: data?.appName || "", }) .then(() => { - toast.success("Mysql stopped successfully"); + toast.success("Mysql reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error stopping Mysql"); + toast.error("Error reloading Mysql"); }); }} > - - )} - + {data?.applicationStatus === "idle" ? ( + { + await start({ + mysqlId: mysqlId, + }) + .then(() => { + toast.success("Mysql started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mysql"); + }); + }} + > + + + ) : ( + { + await stop({ + mysqlId: mysqlId, + }) + .then(() => { + toast.success("Mysql stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mysql"); + }); + }} + > + + + )} + { ); return ( -
- - - General - - - { - setIsDeploying(true); - - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - - { - await reload({ - postgresId: postgresId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Postgres reloaded successfully"); + <> +
+ + + Deploy Settings + + + + { + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); refetch(); - }) - .catch(() => { - toast.error("Error reloading Postgres"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( - { - await start({ - postgresId: postgresId, - }) - .then(() => { - toast.success("Postgres started successfully"); - refetch(); + }} + > + + + { + await reload({ + postgresId: postgresId, + appName: data?.appName || "", }) - .catch(() => { - toast.error("Error starting Postgres"); - }); - }} + .then(() => { + toast.success("Postgres reloaded successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error reloading Postgres"); + }); + }} + > + + + {data?.applicationStatus === "idle" ? ( + { + await start({ + postgresId: postgresId, + }) + .then(() => { + toast.success("Postgres started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Postgres"); + }); + }} + > + + + ) : ( + { + await stop({ + postgresId: postgresId, + }) + .then(() => { + toast.success("Postgres stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Postgres"); + }); + }} + > + + + )} + + - - - ) : ( - { - await stop({ - postgresId: postgresId, - }) - .then(() => { - toast.success("Postgres stopped successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error stopping Postgres"); - }); - }} - > - - - )} - - - - - - - { - setIsDrawerOpen(false); - setFilteredLogs([]); - setIsDeploying(false); - refetch(); - }} - filteredLogs={filteredLogs} - /> -
+ +
+
+ { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + refetch(); + }} + filteredLogs={filteredLogs} + /> +
+ ); }; diff --git a/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx b/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx index e309ef49..d9c2e10a 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx @@ -2,12 +2,26 @@ import { DialogAction } from "@/components/shared/dialog-action"; import { DrawerLogs } from "@/components/shared/drawer-logs"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; +import { + Ban, + CheckCircle2, + HelpCircle, + RefreshCcw, + Terminal, +} from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; + interface Props { redisId: string; } @@ -63,94 +77,150 @@ export const ShowGeneralRedis = ({ redisId }: Props) => { Deploy Settings - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - redisId: redisId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Redis reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Redis"); - }); - }} - > - - - {/* */} - {data?.applicationStatus === "idle" ? ( + { - await start({ - redisId: redisId, - }) - .then(() => { - toast.success("Redis started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Redis"); - }); + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); }} > - - ) : ( { - await stop({ + await reload({ redisId: redisId, + appName: data?.appName || "", }) .then(() => { - toast.success("Redis stopped successfully"); + toast.success("Redis reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error stopping Redis"); + toast.error("Error reloading Redis"); }); }} > - - )} - + {data?.applicationStatus === "idle" ? ( + { + await start({ + redisId: redisId, + }) + .then(() => { + toast.success("Redis started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Redis"); + }); + }} + > + + + ) : ( + { + await stop({ + redisId: redisId, + }) + .then(() => { + toast.success("Redis stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Redis"); + }); + }} + > + + + )} + Date: Sat, 8 Mar 2025 18:39:02 -0600 Subject: [PATCH 43/64] feat(services): add bulk service move functionality across projects - Implement service move feature for applications, compose, databases, and other services - Add move dialog with project selection for bulk service transfer - Create move mutation endpoints for each service type - Enhance project management with cross-project service relocation - Improve user experience with error handling and success notifications --- .../pages/dashboard/project/[projectId].tsx | 179 ++++++++++++++++++ .../dokploy/server/api/routers/application.ts | 45 +++++ apps/dokploy/server/api/routers/compose.ts | 51 ++++- apps/dokploy/server/api/routers/mariadb.ts | 47 +++++ apps/dokploy/server/api/routers/mongo.ts | 47 +++++ apps/dokploy/server/api/routers/mysql.ts | 47 +++++ apps/dokploy/server/api/routers/postgres.ts | 49 +++++ apps/dokploy/server/api/routers/redis.ts | 47 +++++ 8 files changed, 509 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 605fe6e5..37a6b344 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -75,6 +75,22 @@ import { useRouter } from "next/router"; import { type ReactElement, useMemo, useState } from "react"; import { toast } from "sonner"; import superjson from "superjson"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; export type Services = { appName: string; @@ -205,8 +221,13 @@ const Project = ( const { data: auth } = api.user.get.useQuery(); const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); + const { data: allProjects } = api.project.all.useQuery(); const router = useRouter(); + const [isMoveDialogOpen, setIsMoveDialogOpen] = useState(false); + const [selectedTargetProject, setSelectedTargetProject] = + useState(""); + const emptyServices = data?.mariadb?.length === 0 && data?.mongo?.length === 0 && @@ -254,6 +275,31 @@ const Project = ( const composeActions = { start: api.compose.start.useMutation(), stop: api.compose.stop.useMutation(), + move: api.compose.move.useMutation(), + }; + + const applicationActions = { + move: api.application.move.useMutation(), + }; + + const postgresActions = { + move: api.postgres.move.useMutation(), + }; + + const mysqlActions = { + move: api.mysql.move.useMutation(), + }; + + const mariadbActions = { + move: api.mariadb.move.useMutation(), + }; + + const redisActions = { + move: api.redis.move.useMutation(), + }; + + const mongoActions = { + move: api.mongo.move.useMutation(), }; const handleBulkStart = async () => { @@ -296,6 +342,80 @@ const Project = ( setIsBulkActionLoading(false); }; + const handleBulkMove = async () => { + if (!selectedTargetProject) { + toast.error("Please select a target project"); + return; + } + + let success = 0; + setIsBulkActionLoading(true); + for (const serviceId of selectedServices) { + try { + const service = filteredServices.find((s) => s.id === serviceId); + if (!service) continue; + + switch (service.type) { + case "application": + await applicationActions.move.mutateAsync({ + applicationId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "compose": + await composeActions.move.mutateAsync({ + composeId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "postgres": + await postgresActions.move.mutateAsync({ + postgresId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "mysql": + await mysqlActions.move.mutateAsync({ + mysqlId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "mariadb": + await mariadbActions.move.mutateAsync({ + mariadbId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "redis": + await redisActions.move.mutateAsync({ + redisId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + case "mongo": + await mongoActions.move.mutateAsync({ + mongoId: serviceId, + targetProjectId: selectedTargetProject, + }); + break; + } + success++; + } catch (error) { + toast.error( + `Error moving service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } + if (success > 0) { + toast.success(`${success} services moved successfully`); + refetch(); + } + setSelectedServices([]); + setIsDropdownOpen(false); + setIsMoveDialogOpen(false); + setIsBulkActionLoading(false); + }; + const filteredServices = useMemo(() => { if (!applications) return []; return applications.filter( @@ -445,6 +565,65 @@ const Project = ( Stop + + + + + + + Move Services + + Select the target project to move{" "} + {selectedServices.length} services + + +
+ +
+ + + + +
+
diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index e1629b4c..1909a02f 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -668,4 +668,49 @@ export const applicationRouter = createTRPCRouter({ return stats; }), + move: protectedProcedure + .input( + z.object({ + applicationId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this application", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the application's projectId + const updatedApplication = await db + .update(applications) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(applications.applicationId, input.applicationId)) + .returning() + .then((res) => res[0]); + + if (!updatedApplication) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move application", + }); + } + + return updatedApplication; + }), }); diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index bae926d0..22692d50 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -8,7 +8,7 @@ import { apiFindCompose, apiRandomizeCompose, apiUpdateCompose, - compose, + compose as composeTable, } from "@/server/db/schema"; import { cleanQueuesByCompose, myQueue } from "@/server/queues/queueSetup"; import { templates } from "@/templates/templates"; @@ -24,6 +24,7 @@ import { dump } from "js-yaml"; import _ from "lodash"; import { nanoid } from "nanoid"; import { createTRPCRouter, protectedProcedure } from "../trpc"; +import { z } from "zod"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { deploy } from "@/server/utils/deploy"; @@ -157,8 +158,8 @@ export const composeRouter = createTRPCRouter({ 4; const result = await db - .delete(compose) - .where(eq(compose.composeId, input.composeId)) + .delete(composeTable) + .where(eq(composeTable.composeId, input.composeId)) .returning(); const cleanupOperations = [ @@ -501,4 +502,48 @@ export const composeRouter = createTRPCRouter({ const uniqueTags = _.uniq(allTags); return uniqueTags; }), + + move: protectedProcedure + .input( + z.object({ + composeId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const compose = await findComposeById(input.composeId); + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this compose", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the compose's projectId + const updatedCompose = await db + .update(composeTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(composeTable.composeId, input.composeId)) + .returning() + .then((res) => res[0]); + + if (!updatedCompose) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move compose", + }); + } + + return updatedCompose; + }), }); diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index be0ffd39..21794bc7 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesMariaDB, apiSaveExternalPortMariaDB, apiUpdateMariaDB, + mariadb as mariadbTable, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; import { @@ -30,6 +31,9 @@ import { } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; +import { z } from "zod"; +import { eq } from "drizzle-orm"; +import { db } from "@/server/db"; export const mariadbRouter = createTRPCRouter({ create: protectedProcedure @@ -322,4 +326,47 @@ export const mariadbRouter = createTRPCRouter({ return true; }), + move: protectedProcedure + .input( + z.object({ + mariadbId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const mariadb = await findMariadbById(input.mariadbId); + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this mariadb", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the mariadb's projectId + const updatedMariadb = await db + .update(mariadbTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(mariadbTable.mariadbId, input.mariadbId)) + .returning() + .then((res) => res[0]); + + if (!updatedMariadb) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move mariadb", + }); + } + + return updatedMariadb; + }), }); diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 1c3ba6bb..4301e407 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesMongo, apiSaveExternalPortMongo, apiUpdateMongo, + mongo as mongoTable, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; import { @@ -30,6 +31,9 @@ import { } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; +import { z } from "zod"; +import { eq } from "drizzle-orm"; +import { db } from "@/server/db"; export const mongoRouter = createTRPCRouter({ create: protectedProcedure @@ -336,4 +340,47 @@ export const mongoRouter = createTRPCRouter({ return true; }), + move: protectedProcedure + .input( + z.object({ + mongoId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const mongo = await findMongoById(input.mongoId); + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this mongo", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the mongo's projectId + const updatedMongo = await db + .update(mongoTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(mongoTable.mongoId, input.mongoId)) + .returning() + .then((res) => res[0]); + + if (!updatedMongo) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move mongo", + }); + } + + return updatedMongo; + }), }); diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 594403f2..3fa1eb92 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesMySql, apiSaveExternalPortMySql, apiUpdateMySql, + mysql as mysqlTable, } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -32,6 +33,9 @@ import { updateMySqlById, } from "@dokploy/server"; import { observable } from "@trpc/server/observable"; +import { eq } from "drizzle-orm"; +import { db } from "@/server/db"; +import { z } from "zod"; export const mysqlRouter = createTRPCRouter({ create: protectedProcedure @@ -332,4 +336,47 @@ export const mysqlRouter = createTRPCRouter({ return true; }), + move: protectedProcedure + .input( + z.object({ + mysqlId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const mysql = await findMySqlById(input.mysqlId); + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this mysql", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the mysql's projectId + const updatedMysql = await db + .update(mysqlTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(mysqlTable.mysqlId, input.mysqlId)) + .returning() + .then((res) => res[0]); + + if (!updatedMysql) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move mysql", + }); + } + + return updatedMysql; + }), }); diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index cf3221b4..b3cd5b82 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesPostgres, apiSaveExternalPortPostgres, apiUpdatePostgres, + postgres as postgresTable, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; import { @@ -30,6 +31,9 @@ import { } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; +import { z } from "zod"; +import { eq } from "drizzle-orm"; +import { db } from "@/server/db"; export const postgresRouter = createTRPCRouter({ create: protectedProcedure @@ -352,4 +356,49 @@ export const postgresRouter = createTRPCRouter({ return true; }), + move: protectedProcedure + .input( + z.object({ + postgresId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const postgres = await findPostgresById(input.postgresId); + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this postgres", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the postgres's projectId + const updatedPostgres = await db + .update(postgresTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(postgresTable.postgresId, input.postgresId)) + .returning() + .then((res) => res[0]); + + if (!updatedPostgres) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move postgres", + }); + } + + return updatedPostgres; + }), }); diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index a80660bf..d7d9d78d 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesRedis, apiSaveExternalPortRedis, apiUpdateRedis, + redis as redisTable, } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -30,6 +31,9 @@ import { updateRedisById, } from "@dokploy/server"; import { observable } from "@trpc/server/observable"; +import { eq } from "drizzle-orm"; +import { db } from "@/server/db"; +import { z } from "zod"; export const redisRouter = createTRPCRouter({ create: protectedProcedure @@ -316,4 +320,47 @@ export const redisRouter = createTRPCRouter({ return true; }), + move: protectedProcedure + .input( + z.object({ + redisId: z.string(), + targetProjectId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + const redis = await findRedisById(input.redisId); + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move this redis", + }); + } + + const targetProject = await findProjectById(input.targetProjectId); + if (targetProject.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to move to this project", + }); + } + + // Update the redis's projectId + const updatedRedis = await db + .update(redisTable) + .set({ + projectId: input.targetProjectId, + }) + .where(eq(redisTable.redisId, input.redisId)) + .returning() + .then((res) => res[0]); + + if (!updatedRedis) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to move redis", + }); + } + + return updatedRedis; + }), }); From fc1dbcf51af3653bcce664f544f4c70ab09b53cc Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:40:23 -0600 Subject: [PATCH 44/64] feat(services): improve bulk move project selection UX - Add empty state handling when no other projects are available - Disable move button when no target projects exist - Provide clear guidance for users to create a new project before moving services --- .../pages/dashboard/project/[projectId].tsx | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 37a6b344..2f550dba 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -587,26 +587,40 @@ const Project = (
- + {allProjects?.filter( + (p) => p.projectId !== projectId, + ).length === 0 ? ( +
+ +

+ No other projects available. Create a new + project first to move services. +

+
+ ) : ( + + )}
From 7dda252b7cfc97b8885864056e9417ce99aa142e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:43:37 -0600 Subject: [PATCH 45/64] feat(services): add bulk delete functionality for services - Implement bulk delete feature for applications, compose, and various database services - Add delete mutation endpoints for each service type - Provide user-friendly bulk delete action with error handling and success notifications - Integrate Trash2 icon for delete action in bulk service management --- .../pages/dashboard/project/[projectId].tsx | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 2f550dba..cf0a5f99 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -65,6 +65,7 @@ import { PlusIcon, Search, X, + Trash2, } from "lucide-react"; import type { GetServerSidePropsContext, @@ -276,30 +277,37 @@ const Project = ( start: api.compose.start.useMutation(), stop: api.compose.stop.useMutation(), move: api.compose.move.useMutation(), + delete: api.compose.delete.useMutation(), }; const applicationActions = { move: api.application.move.useMutation(), + delete: api.application.delete.useMutation(), }; const postgresActions = { move: api.postgres.move.useMutation(), + delete: api.postgres.remove.useMutation(), }; const mysqlActions = { move: api.mysql.move.useMutation(), + delete: api.mysql.remove.useMutation(), }; const mariadbActions = { move: api.mariadb.move.useMutation(), + delete: api.mariadb.remove.useMutation(), }; const redisActions = { move: api.redis.move.useMutation(), + delete: api.redis.remove.useMutation(), }; const mongoActions = { move: api.mongo.move.useMutation(), + delete: api.mongo.remove.useMutation(), }; const handleBulkStart = async () => { @@ -416,6 +424,68 @@ const Project = ( setIsBulkActionLoading(false); }; + const handleBulkDelete = async () => { + let success = 0; + setIsBulkActionLoading(true); + for (const serviceId of selectedServices) { + try { + const service = filteredServices.find((s) => s.id === serviceId); + if (!service) continue; + + switch (service.type) { + case "application": + await applicationActions.delete.mutateAsync({ + applicationId: serviceId, + }); + break; + case "compose": + await composeActions.delete.mutateAsync({ + composeId: serviceId, + deleteVolumes: false, + }); + break; + case "postgres": + await postgresActions.delete.mutateAsync({ + postgresId: serviceId, + }); + break; + case "mysql": + await mysqlActions.delete.mutateAsync({ + mysqlId: serviceId, + }); + break; + case "mariadb": + await mariadbActions.delete.mutateAsync({ + mariadbId: serviceId, + }); + break; + case "redis": + await redisActions.delete.mutateAsync({ + redisId: serviceId, + }); + break; + case "mongo": + await mongoActions.delete.mutateAsync({ + mongoId: serviceId, + }); + break; + } + success++; + } catch (error) { + toast.error( + `Error deleting service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } + if (success > 0) { + toast.success(`${success} services deleted successfully`); + refetch(); + } + setSelectedServices([]); + setIsDropdownOpen(false); + setIsBulkActionLoading(false); + }; + const filteredServices = useMemo(() => { if (!applications) return []; return applications.filter( @@ -565,6 +635,20 @@ const Project = ( Stop + + + Date: Sat, 8 Mar 2025 18:48:34 -0600 Subject: [PATCH 46/64] feat(services): add sorting functionality for services - Implement local storage-based sorting for services - Add sorting options by name, type, and creation date - Provide ascending and descending sort order selection - Enhance service list usability with dynamic sorting --- .../pages/dashboard/project/[projectId].tsx | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index cf0a5f99..e1eccbdc 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -73,7 +73,7 @@ import type { } from "next"; import Head from "next/head"; import { useRouter } from "next/router"; -import { type ReactElement, useMemo, useState } from "react"; +import { type ReactElement, useMemo, useState, useEffect } from "react"; import { toast } from "sonner"; import superjson from "superjson"; import { @@ -220,6 +220,38 @@ const Project = ( const [isBulkActionLoading, setIsBulkActionLoading] = useState(false); const { projectId } = props; const { data: auth } = api.user.get.useQuery(); + const [sortBy, setSortBy] = useState(() => { + if (typeof window !== "undefined") { + return localStorage.getItem("servicesSort") || "createdAt-desc"; + } + return "createdAt-desc"; + }); + + useEffect(() => { + localStorage.setItem("servicesSort", sortBy); + }, [sortBy]); + + const sortServices = (services: Services[]) => { + const [field, direction] = sortBy.split("-"); + return [...services].sort((a, b) => { + let comparison = 0; + switch (field) { + case "name": + comparison = a.name.localeCompare(b.name); + break; + case "type": + comparison = a.type.localeCompare(b.type); + break; + case "createdAt": + comparison = + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); + break; + default: + comparison = 0; + } + return direction === "asc" ? comparison : -comparison; + }); + }; const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); const { data: allProjects } = api.project.all.useQuery(); @@ -488,7 +520,7 @@ const Project = ( const filteredServices = useMemo(() => { if (!applications) return []; - return applications.filter( + const filtered = applications.filter( (service) => (service.name.toLowerCase().includes(searchQuery.toLowerCase()) || service.description @@ -496,7 +528,8 @@ const Project = ( .includes(searchQuery.toLowerCase())) && (selectedTypes.length === 0 || selectedTypes.includes(service.type)), ); - }, [applications, searchQuery, selectedTypes]); + return sortServices(filtered); + }, [applications, searchQuery, selectedTypes, sortBy]); return (
@@ -741,6 +774,23 @@ const Project = ( />
+ Date: Sat, 8 Mar 2025 18:50:09 -0600 Subject: [PATCH 47/64] refactor(ui): improve responsive layout for project services view - Update responsive breakpoints for service list layout - Use more semantic breakpoint classes (xl, lg) for better responsiveness - Adjust flex direction and alignment for improved mobile and desktop views --- apps/dokploy/pages/dashboard/project/[projectId].tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index e1eccbdc..0bbca7a0 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -603,7 +603,7 @@ const Project = (
) : ( <> -
+
-
+
Date: Sat, 8 Mar 2025 18:51:59 -0600 Subject: [PATCH 48/64] feat(services): add role-based delete service permissions - Restrict bulk delete action to owners and users with delete service permissions - Conditionally render delete button based on user role and authorization - Improve service management security by implementing fine-grained access control --- .../pages/dashboard/project/[projectId].tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 0bbca7a0..d1dd33ea 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -668,20 +668,24 @@ const Project = ( Stop - - - + + + )} + Date: Sat, 8 Mar 2025 19:17:59 -0600 Subject: [PATCH 49/64] feat(environment): add unsaved changes handling for environment settings - Implement form change tracking for environment variables - Add cancel and save buttons with conditional rendering - Disable save button when no changes are detected - Show unsaved changes warning in description - Improve user experience with form state management --- .../environment/show-enviroment.tsx | 39 ++++++++++-- .../application/environment/show.tsx | 59 ++++++++++++++++--- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx index cc208d9b..8a78c274 100644 --- a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx @@ -71,15 +71,19 @@ export const ShowEnvironment = ({ id, type }: Props) => { resolver: zodResolver(addEnvironmentSchema), }); + // Watch form value + const currentEnvironment = form.watch("environment"); + const hasChanges = currentEnvironment !== (data?.env || ""); + useEffect(() => { if (data) { form.reset({ environment: data.env || "", }); } - }, [form.reset, data, form]); + }, [data, form]); - const onSubmit = async (data: EnvironmentSchema) => { + const onSubmit = async (formData: EnvironmentSchema) => { mutateAsync({ mongoId: id || "", postgresId: id || "", @@ -87,7 +91,7 @@ export const ShowEnvironment = ({ id, type }: Props) => { mysqlId: id || "", mariadbId: id || "", composeId: id || "", - env: data.environment, + env: formData.environment, }) .then(async () => { toast.success("Environments Added"); @@ -98,6 +102,12 @@ export const ShowEnvironment = ({ id, type }: Props) => { }); }; + const handleCancel = () => { + form.reset({ + environment: data?.env || "", + }); + }; + return (
@@ -106,6 +116,11 @@ export const ShowEnvironment = ({ id, type }: Props) => { Environment Settings You can add environment variables to your resource. + {hasChanges && ( + + (You have unsaved changes) + + )}
@@ -155,8 +170,22 @@ PORT=3000 )} /> -
- + )} +
diff --git a/apps/dokploy/components/dashboard/application/environment/show.tsx b/apps/dokploy/components/dashboard/application/environment/show.tsx index d97c39e2..b574ce09 100644 --- a/apps/dokploy/components/dashboard/application/environment/show.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show.tsx @@ -7,6 +7,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { useEffect } from "react"; const addEnvironmentSchema = z.object({ env: z.string(), @@ -34,16 +35,32 @@ export const ShowEnvironment = ({ applicationId }: Props) => { const form = useForm({ defaultValues: { - env: data?.env || "", - buildArgs: data?.buildArgs || "", + env: "", + buildArgs: "", }, resolver: zodResolver(addEnvironmentSchema), }); - const onSubmit = async (data: EnvironmentSchema) => { + // Watch form values + const currentEnv = form.watch("env"); + const currentBuildArgs = form.watch("buildArgs"); + const hasChanges = + currentEnv !== (data?.env || "") || + currentBuildArgs !== (data?.buildArgs || ""); + + useEffect(() => { + if (data) { + form.reset({ + env: data.env || "", + buildArgs: data.buildArgs || "", + }); + } + }, [data, form]); + + const onSubmit = async (formData: EnvironmentSchema) => { mutateAsync({ - env: data.env, - buildArgs: data.buildArgs, + env: formData.env, + buildArgs: formData.buildArgs, applicationId, }) .then(async () => { @@ -55,6 +72,13 @@ export const ShowEnvironment = ({ applicationId }: Props) => { }); }; + const handleCancel = () => { + form.reset({ + env: data?.env || "", + buildArgs: data?.buildArgs || "", + }); + }; + return (
@@ -65,7 +89,16 @@ export const ShowEnvironment = ({ applicationId }: Props) => { + You can add environment variables to your resource. + {hasChanges && ( + + (You have unsaved changes) + + )} + + } placeholder={["NODE_ENV=production", "PORT=3000"].join("\n")} /> {data?.buildType === "dockerfile" && ( @@ -89,8 +122,18 @@ export const ShowEnvironment = ({ applicationId }: Props) => { placeholder="NPM_TOKEN=xyz" /> )} -
- + )} +
From 777aa3e4be941d18b5403040c4b941eb0adc846e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 19:26:18 -0600 Subject: [PATCH 50/64] feat(api): enhance API key display with code editor and clipboard copy --- .../dashboard/settings/api/add-api-key.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx index a82a9b35..131d7ddf 100644 --- a/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx +++ b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx @@ -31,6 +31,8 @@ import { FormDescription, } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; +import copy from "copy-to-clipboard"; +import { CodeEditor } from "@/components/shared/code-editor"; const formSchema = z.object({ name: z.string().min(1, "Name is required"), @@ -441,13 +443,16 @@ export const AddApiKey = () => {
-
- {newApiKey} -
+
+ + + + + + Are you absolutely sure? + + +

This action will:

+
    +
  • Stop the current database service
  • +
  • Delete all existing data and volumes
  • +
  • Reset to the default configuration
  • +
  • Restart the service with a clean state
  • +
+

+ This action cannot be undone. +

+
+
+ + Cancel + + {isLoading ? "Rebuilding..." : "Yes, rebuild database"} + + +
+ +
+ + + ); +}; diff --git a/apps/dokploy/components/dashboard/shared/show-database-advanced-settings.tsx b/apps/dokploy/components/dashboard/shared/show-database-advanced-settings.tsx new file mode 100644 index 00000000..c6c4c414 --- /dev/null +++ b/apps/dokploy/components/dashboard/shared/show-database-advanced-settings.tsx @@ -0,0 +1,20 @@ +import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; +import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; +import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; +import { RebuildDatabase } from "./rebuild-database"; + +interface Props { + id: string; + type: "postgres" | "mysql" | "mariadb" | "mongo" | "redis"; +} + +export const ShowDatabaseAdvancedSettings = ({ id, type }: Props) => { + return ( +
+ + + + +
+ ); +}; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index d3930bbc..43803f1d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -1,5 +1,3 @@ -import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; -import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -10,7 +8,7 @@ import { ShowInternalMariadbCredentials } from "@/components/dashboard/mariadb/g import { UpdateMariadb } from "@/components/dashboard/mariadb/update-mariadb"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; -import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; +import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings"; import { MariadbIcon } from "@/components/icons/data-tools-icons"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; @@ -278,11 +276,10 @@ const Mariadb = (
-
- - - -
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 4bc1d0ce..1df3c390 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -1,5 +1,3 @@ -import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; -import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -10,7 +8,7 @@ import { ShowInternalMongoCredentials } from "@/components/dashboard/mongo/gener import { UpdateMongo } from "@/components/dashboard/mongo/update-mongo"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; -import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; +import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings"; import { MongodbIcon } from "@/components/icons/data-tools-icons"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; @@ -279,11 +277,7 @@ const Mongo = (
-
- - - -
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 5440fc5e..7bed196d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -1,5 +1,3 @@ -import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; -import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -10,7 +8,7 @@ import { ShowExternalMysqlCredentials } from "@/components/dashboard/mysql/gener import { ShowGeneralMysql } from "@/components/dashboard/mysql/general/show-general-mysql"; import { ShowInternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-internal-mysql-credentials"; import { UpdateMysql } from "@/components/dashboard/mysql/update-mysql"; -import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; +import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings"; import { MysqlIcon } from "@/components/icons/data-tools-icons"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; @@ -236,33 +234,9 @@ const MySql = ( /> ) : ( <> - {/* {monitoring?.enabledFeatures && ( -
- - -
- )} - - {toggleMonitoring ? ( - - ) : ( -
*/} - {/*
*/} - {/* )} */} )}
@@ -283,11 +257,10 @@ const MySql = (
-
- - - -
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 635f8b4e..75337464 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -1,12 +1,9 @@ -import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; -import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; -import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; import { ShowExternalPostgresCredentials } from "@/components/dashboard/postgres/general/show-external-postgres-credentials"; import { ShowGeneralPostgres } from "@/components/dashboard/postgres/general/show-general-postgres"; import { ShowInternalPostgresCredentials } from "@/components/dashboard/postgres/general/show-internal-postgres-credentials"; @@ -15,6 +12,7 @@ import { PostgresqlIcon } from "@/components/icons/data-tools-icons"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; import { StatusTooltip } from "@/components/shared/status-tooltip"; +import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings"; import { Badge } from "@/components/ui/badge"; import { Card, @@ -235,33 +233,9 @@ const Postgresql = ( /> ) : ( <> - {/* {monitoring?.enabledFeatures && ( -
- - -
- )} - - {toggleMonitoring ? ( - - ) : ( -
*/} - {/*
*/} - {/* )} */} )}
@@ -282,11 +256,10 @@ const Postgresql = (
-
- - - -
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index c7084d52..7c624ef0 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -1,15 +1,13 @@ -import { ShowResources } from "@/components/dashboard/application/advanced/show-resources"; -import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring"; -import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; import { ShowExternalRedisCredentials } from "@/components/dashboard/redis/general/show-external-redis-credentials"; import { ShowGeneralRedis } from "@/components/dashboard/redis/general/show-general-redis"; import { ShowInternalRedisCredentials } from "@/components/dashboard/redis/general/show-internal-redis-credentials"; import { UpdateRedis } from "@/components/dashboard/redis/update-redis"; +import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings"; import { RedisIcon } from "@/components/icons/data-tools-icons"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; @@ -272,11 +270,7 @@ const Redis = (
-
- - - -
+
diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 21794bc7..c3ba8c13 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -8,6 +8,7 @@ import { apiSaveEnvironmentVariablesMariaDB, apiSaveExternalPortMariaDB, apiUpdateMariaDB, + apiRebuildMariadb, mariadb as mariadbTable, } from "@/server/db/schema"; import { cancelJobs } from "@/server/utils/backup"; @@ -34,7 +35,7 @@ import { observable } from "@trpc/server/observable"; import { z } from "zod"; import { eq } from "drizzle-orm"; import { db } from "@/server/db"; - +import { rebuildDatabase } from "@dokploy/server"; export const mariadbRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateMariaDB) @@ -369,4 +370,18 @@ export const mariadbRouter = createTRPCRouter({ return updatedMariadb; }), + rebuild: protectedProcedure + .input(apiRebuildMariadb) + .mutation(async ({ input, ctx }) => { + const mariadb = await findMariadbById(input.mariadbId); + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to rebuild this MariaDB database", + }); + } + + await rebuildDatabase(mariadb.mariadbId, "mariadb"); + return true; + }), }); diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 4301e407..de6c03e5 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -4,6 +4,7 @@ import { apiCreateMongo, apiDeployMongo, apiFindOneMongo, + apiRebuildMongo, apiResetMongo, apiSaveEnvironmentVariablesMongo, apiSaveExternalPortMongo, @@ -34,7 +35,7 @@ import { observable } from "@trpc/server/observable"; import { z } from "zod"; import { eq } from "drizzle-orm"; import { db } from "@/server/db"; - +import { rebuildDatabase } from "@dokploy/server"; export const mongoRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateMongo) @@ -383,4 +384,19 @@ export const mongoRouter = createTRPCRouter({ return updatedMongo; }), + rebuild: protectedProcedure + .input(apiRebuildMongo) + .mutation(async ({ input, ctx }) => { + const mongo = await findMongoById(input.mongoId); + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to rebuild this MongoDB database", + }); + } + + await rebuildDatabase(mongo.mongoId, "mongo"); + + return true; + }), }); diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 3fa1eb92..0c66b71c 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -4,6 +4,7 @@ import { apiCreateMySql, apiDeployMySql, apiFindOneMySql, + apiRebuildMysql, apiResetMysql, apiSaveEnvironmentVariablesMySql, apiSaveExternalPortMySql, @@ -24,6 +25,7 @@ import { findBackupsByDbId, findMySqlById, findProjectById, + rebuildDatabase, removeMySqlById, removeService, startService, @@ -379,4 +381,19 @@ export const mysqlRouter = createTRPCRouter({ return updatedMysql; }), + rebuild: protectedProcedure + .input(apiRebuildMysql) + .mutation(async ({ input, ctx }) => { + const mysql = await findMySqlById(input.mysqlId); + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to rebuild this MySQL database", + }); + } + + await rebuildDatabase(mysql.mysqlId, "mysql"); + + return true; + }), }); diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index b3cd5b82..8159447f 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -4,6 +4,7 @@ import { apiCreatePostgres, apiDeployPostgres, apiFindOnePostgres, + apiRebuildPostgres, apiResetPostgres, apiSaveEnvironmentVariablesPostgres, apiSaveExternalPortPostgres, @@ -21,6 +22,7 @@ import { findBackupsByDbId, findPostgresById, findProjectById, + rebuildDatabase, removePostgresById, removeService, startService, @@ -34,7 +36,6 @@ import { observable } from "@trpc/server/observable"; import { z } from "zod"; import { eq } from "drizzle-orm"; import { db } from "@/server/db"; - export const postgresRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreatePostgres) @@ -401,4 +402,21 @@ export const postgresRouter = createTRPCRouter({ return updatedPostgres; }), + rebuild: protectedProcedure + .input(apiRebuildPostgres) + .mutation(async ({ input, ctx }) => { + const postgres = await findPostgresById(input.postgresId); + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to rebuild this Postgres database", + }); + } + + await rebuildDatabase(postgres.postgresId, "postgres"); + + return true; + }), }); diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index d7d9d78d..c310a6dd 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -9,6 +9,7 @@ import { apiSaveExternalPortRedis, apiUpdateRedis, redis as redisTable, + apiRebuildRedis, } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -34,7 +35,7 @@ import { observable } from "@trpc/server/observable"; import { eq } from "drizzle-orm"; import { db } from "@/server/db"; import { z } from "zod"; - +import { rebuildDatabase } from "@dokploy/server"; export const redisRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateRedis) @@ -363,4 +364,18 @@ export const redisRouter = createTRPCRouter({ return updatedRedis; }), + rebuild: protectedProcedure + .input(apiRebuildRedis) + .mutation(async ({ input, ctx }) => { + const redis = await findRedisById(input.redisId); + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to rebuild this Redis database", + }); + } + + await rebuildDatabase(redis.redisId, "redis"); + return true; + }), }); diff --git a/packages/server/src/db/schema/mariadb.ts b/packages/server/src/db/schema/mariadb.ts index 46357faf..ce10f0f9 100644 --- a/packages/server/src/db/schema/mariadb.ts +++ b/packages/server/src/db/schema/mariadb.ts @@ -146,3 +146,9 @@ export const apiUpdateMariaDB = createSchema mariadbId: z.string().min(1), }) .omit({ serverId: true }); + +export const apiRebuildMariadb = createSchema + .pick({ + mariadbId: true, + }) + .required(); diff --git a/packages/server/src/db/schema/mongo.ts b/packages/server/src/db/schema/mongo.ts index 8515dbff..4eefc022 100644 --- a/packages/server/src/db/schema/mongo.ts +++ b/packages/server/src/db/schema/mongo.ts @@ -141,3 +141,9 @@ export const apiResetMongo = createSchema appName: true, }) .required(); + +export const apiRebuildMongo = createSchema + .pick({ + mongoId: true, + }) + .required(); diff --git a/packages/server/src/db/schema/mysql.ts b/packages/server/src/db/schema/mysql.ts index 2c5cabe8..1610b7f0 100644 --- a/packages/server/src/db/schema/mysql.ts +++ b/packages/server/src/db/schema/mysql.ts @@ -144,3 +144,9 @@ export const apiUpdateMySql = createSchema mysqlId: z.string().min(1), }) .omit({ serverId: true }); + +export const apiRebuildMysql = createSchema + .pick({ + mysqlId: true, + }) + .required(); diff --git a/packages/server/src/db/schema/postgres.ts b/packages/server/src/db/schema/postgres.ts index 213dc2b5..0ea7dcc1 100644 --- a/packages/server/src/db/schema/postgres.ts +++ b/packages/server/src/db/schema/postgres.ts @@ -140,3 +140,9 @@ export const apiUpdatePostgres = createSchema postgresId: z.string().min(1), }) .omit({ serverId: true }); + +export const apiRebuildPostgres = createSchema + .pick({ + postgresId: true, + }) + .required(); diff --git a/packages/server/src/db/schema/redis.ts b/packages/server/src/db/schema/redis.ts index ed8276a5..85c27609 100644 --- a/packages/server/src/db/schema/redis.ts +++ b/packages/server/src/db/schema/redis.ts @@ -133,3 +133,9 @@ export const apiUpdateRedis = createSchema redisId: z.string().min(1), }) .omit({ serverId: true }); + +export const apiRebuildRedis = createSchema + .pick({ + redisId: true, + }) + .required(); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c8337dc2..a4c86ca6 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -30,7 +30,7 @@ export * from "./services/github"; export * from "./services/gitlab"; export * from "./services/server"; export * from "./services/application"; - +export * from "./utils/databases/rebuild"; export * from "./setup/config-paths"; export * from "./setup/postgres-setup"; export * from "./setup/redis-setup"; diff --git a/packages/server/src/utils/databases/rebuild.ts b/packages/server/src/utils/databases/rebuild.ts new file mode 100644 index 00000000..12c907b7 --- /dev/null +++ b/packages/server/src/utils/databases/rebuild.ts @@ -0,0 +1,99 @@ +import { deployPostgres } from "@dokploy/server/services/postgres"; +import { execAsyncRemote } from "../process/execAsync"; +import { execAsync } from "../process/execAsync"; +import { deployMySql } from "@dokploy/server/services/mysql"; +import { deployMariadb } from "@dokploy/server/services/mariadb"; +import { deployMongo } from "@dokploy/server/services/mongo"; +import { deployRedis } from "@dokploy/server/services/redis"; +import { removeService } from "../docker/utils"; +import { db } from "@dokploy/server/db"; +import { + postgres, + mysql, + mariadb, + mongo, + redis, +} from "@dokploy/server/db/schema"; +import { eq } from "drizzle-orm"; + +type DatabaseType = "postgres" | "mysql" | "mariadb" | "mongo" | "redis"; + +export const rebuildDatabase = async ( + databaseId: string, + type: DatabaseType, +) => { + const database = await findDatabaseById(databaseId, type); + + if (!database) { + throw new Error("Database not found"); + } + + await removeService(database.appName, database.serverId); + await new Promise((resolve) => setTimeout(resolve, 6000)); + + for (const mount of database.mounts) { + if (mount.type === "volume") { + const command = `docker volume rm ${mount?.volumeName} --force`; + if (database.serverId) { + await execAsyncRemote(database.serverId, command); + } else { + await execAsync(command); + } + } + } + + if (type === "postgres") { + await deployPostgres(databaseId); + } else if (type === "mysql") { + await deployMySql(databaseId); + } else if (type === "mariadb") { + await deployMariadb(databaseId); + } else if (type === "mongo") { + await deployMongo(databaseId); + } else if (type === "redis") { + await deployRedis(databaseId); + } +}; + +const findDatabaseById = async (databaseId: string, type: DatabaseType) => { + if (type === "postgres") { + return await db.query.postgres.findFirst({ + where: eq(postgres.postgresId, databaseId), + with: { + mounts: true, + }, + }); + } + if (type === "mysql") { + return await db.query.mysql.findFirst({ + where: eq(mysql.mysqlId, databaseId), + with: { + mounts: true, + }, + }); + } + if (type === "mariadb") { + return await db.query.mariadb.findFirst({ + where: eq(mariadb.mariadbId, databaseId), + with: { + mounts: true, + }, + }); + } + if (type === "mongo") { + return await db.query.mongo.findFirst({ + where: eq(mongo.mongoId, databaseId), + with: { + mounts: true, + }, + }); + } + if (type === "redis") { + return await db.query.redis.findFirst({ + where: eq(redis.redisId, databaseId), + with: { + mounts: true, + }, + }); + } +}; From 4730845a40ee3abd08b5d6e0b28ed54f235fcca3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:17:46 -0600 Subject: [PATCH 53/64] fix(databases): improve rebuild database button loading state --- .../components/dashboard/shared/rebuild-database.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/shared/rebuild-database.tsx b/apps/dokploy/components/dashboard/shared/rebuild-database.tsx index 15f08cdd..e3628130 100644 --- a/apps/dokploy/components/dashboard/shared/rebuild-database.tsx +++ b/apps/dokploy/components/dashboard/shared/rebuild-database.tsx @@ -71,6 +71,7 @@ export const RebuildDatabase = ({ id, type }: Props) => { From c0b5f9e51a76ad2b4da438019ab488a3fea41106 Mon Sep 17 00:00:00 2001 From: Eniola Osabiya Date: Sat, 8 Mar 2025 20:40:14 -0600 Subject: [PATCH 54/64] fix: update isolated deployment label for clarity --- .../dashboard/compose/general/isolated-deployment.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx b/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx index 3ae2e9fe..1eb13cad 100644 --- a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx +++ b/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx @@ -147,9 +147,9 @@ export const IsolatedDeployment = ({ composeId }: Props) => { render={({ field }) => (
- Enable Randomize ({data?.appName}) + Enable Isolated Deployment ({data?.appName}) - Enable randomize to the compose file. + Enable isolated deployment to the compose file.
From cc8ffca4d48f9ef422a79a7bdf96e01b9a6850ec Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:46:31 -0600 Subject: [PATCH 55/64] feat(domains): add custom certificate resolver support - Extend domain configuration to support custom certificate resolvers - Add new "custom" certificate type option in domain forms - Update database schema and validation to include custom certificate resolver - Implement custom certificate resolver handling in Traefik and Docker domain configurations - Enhance domain management with more flexible SSL/TLS certificate options --- .../application/domains/add-domain.tsx | 123 +- .../dashboard/compose/domains/add-domain.tsx | 93 +- .../dashboard/settings/web-domain.tsx | 3 +- .../drizzle/0072_green_susan_delgado.sql | 2 + apps/dokploy/drizzle/meta/0072_snapshot.json | 5132 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/server/db/validations/domain.ts | 22 +- packages/server/src/db/schema/domain.ts | 3 + packages/server/src/db/schema/shared.ts | 1 + packages/server/src/db/validations/domain.ts | 22 +- packages/server/src/utils/docker/domain.ts | 24 +- packages/server/src/utils/traefik/domain.ts | 2 + 12 files changed, 5367 insertions(+), 67 deletions(-) create mode 100644 apps/dokploy/drizzle/0072_green_susan_delgado.sql create mode 100644 apps/dokploy/drizzle/meta/0072_snapshot.json diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 61168943..f91218ce 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -85,8 +85,20 @@ export const AddDomain = ({ const form = useForm({ resolver: zodResolver(domain), + defaultValues: { + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + }, + mode: "onChange", }); + const certificateType = form.watch("certificateType"); + const https = form.watch("https"); + useEffect(() => { if (data) { form.reset({ @@ -94,13 +106,29 @@ export const AddDomain = ({ /* Convert null to undefined */ path: data?.path || undefined, port: data?.port || undefined, + certificateType: data?.certificateType || undefined, + customCertResolver: data?.customCertResolver || undefined, }); } if (!domainId) { - form.reset({}); + form.reset({ + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + }); } - }, [form, form.reset, data, isLoading]); + }, [form, data, isLoading, domainId]); + + // Separate effect for handling custom cert resolver validation + useEffect(() => { + if (certificateType === "custom") { + form.trigger("customCertResolver"); + } + }, [certificateType, form]); const dictionary = { success: domainId ? "Domain Updated" : "Domain Created", @@ -256,34 +284,73 @@ export const AddDomain = ({ )} /> - {form.getValues().https && ( - ( - - Certificate Provider - { + field.onChange(value); + if (value !== "custom") { + form.setValue( + "customCertResolver", + undefined, + ); + } + }} + value={field.value} + > + + + + + + + None + + Let's Encrypt + + Custom + + + + + ); + }} + /> - - None - - Let's Encrypt - - - - -
+ {certificateType === "custom" && ( + { + return ( + + Custom Certificate Resolver + + { + field.onChange(e); + form.trigger("customCertResolver"); + }} + /> + + + + ); + }} + /> )} - /> + )}
diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index e18d40d7..9b412c83 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -104,6 +104,15 @@ export const AddDomainCompose = ({ const form = useForm({ resolver: zodResolver(domainCompose), + defaultValues: { + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + serviceName: "", + }, }); const https = form.watch("https"); @@ -116,11 +125,21 @@ export const AddDomainCompose = ({ path: data?.path || undefined, port: data?.port || undefined, serviceName: data?.serviceName || undefined, + certificateType: data?.certificateType || undefined, + customCertResolver: data?.customCertResolver || undefined, }); } if (!domainId) { - form.reset({}); + form.reset({ + host: "", + path: undefined, + port: undefined, + https: false, + certificateType: undefined, + customCertResolver: undefined, + serviceName: "", + }); } }, [form, form.reset, data, isLoading]); @@ -393,33 +412,55 @@ export const AddDomainCompose = ({ /> {https && ( - ( - - Certificate Provider - + + + + + - - None - - Let's Encrypt - - - - - + + None + + Let's Encrypt + + Custom + + + + + )} + /> + + {form.getValues().certificateType === "custom" && ( + ( + + Custom Certificate Resolver + + + + + + )} + /> )} - /> + )}
diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 3b3f70ba..a579df39 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -35,7 +35,7 @@ const addServerDomain = z .object({ domain: z.string().min(1, { message: "URL is required" }), letsEncryptEmail: z.string(), - certificateType: z.enum(["letsencrypt", "none"]), + certificateType: z.enum(["letsencrypt", "none", "custom"]), }) .superRefine((data, ctx) => { if (data.certificateType === "letsencrypt" && !data.letsEncryptEmail) { @@ -193,6 +193,7 @@ export const WebDomain = () => { ); }} /> +
+
+ + + + )} + />
)}
- ( - - Branch - - - - - - )} - /> +
+ ( + + Branch + + + + + + )} + /> +
+ { )} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. This + will work only when manual webhook is setup. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx index adb44575..d8b6922e 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx @@ -28,14 +28,22 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import Link from "next/link"; const GithubProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), @@ -47,6 +55,7 @@ const GithubProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), githubId: z.string().min(1, "Github Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GithubProvider = z.infer; @@ -113,6 +122,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { }, buildPath: data.buildPath || "/", githubId: data.githubId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -125,6 +135,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { owner: data.repository.owner, buildPath: data.buildPath, githubId: data.githubId, + watchPaths: data.watchPaths || [], }) .then(async () => { toast.success("Service Provided Saved"); @@ -350,7 +361,85 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { - + + + )} + /> + ( + +
+ Watch Paths + + + + + + +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + field.onChange(newPaths); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const path = input.value.trim(); + if (path) { + field.onChange([...(field.value || []), path]); + input.value = ""; + } + } + }} + /> + + +
)} @@ -365,6 +454,16 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { Save
+ {/* create github link */} +
+ + Repository + +
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx index 298a9114..2073f1a6 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx @@ -29,10 +29,17 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -50,6 +57,7 @@ const GitlabProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), gitlabId: z.string().min(1, "Gitlab Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GitlabProvider = z.infer; @@ -124,6 +132,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { }, buildPath: data.gitlabBuildPath || "/", gitlabId: data.gitlabId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -138,6 +147,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { applicationId, gitlabProjectId: data.repository.id, gitlabPathNamespace: data.repository.gitlabPathNamespace, + watchPaths: data.watchPaths || [], }) .then(async () => { toast.success("Service Provided Saved"); @@ -375,7 +385,85 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { - + +
+ )} + /> + ( + +
+ Watch Paths + + + + + + +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + field.onChange(newPaths); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const path = input.value.trim(); + if (path) { + field.onChange([...(field.value || []), path]); + input.value = ""; + } + } + }} + /> + + +
)} diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx index 87584133..079eeb1b 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx @@ -29,14 +29,21 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Badge } from "@/components/ui/badge"; const BitbucketProviderSchema = z.object({ composePath: z.string().min(1), @@ -48,6 +55,7 @@ const BitbucketProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), bitbucketId: z.string().min(1, "Bitbucket Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type BitbucketProvider = z.infer; @@ -73,6 +81,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { }, bitbucketId: "", branch: "", + watchPaths: [], }, resolver: zodResolver(BitbucketProviderSchema), }); @@ -118,6 +127,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { }, composePath: data.composePath, bitbucketId: data.bitbucketId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -132,6 +142,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { composeId, sourceType: "bitbucket", composeStatus: "idle", + watchPaths: data.watchPaths, }) .then(async () => { toast.success("Service Provided Saved"); @@ -365,6 +376,84 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { )} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
+
+ + + + )} + />
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx index 7787cb3c..6328386a 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Command, @@ -28,10 +29,16 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -47,6 +54,7 @@ const GithubProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), githubId: z.string().min(1, "Github Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GithubProvider = z.infer; @@ -71,6 +79,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { }, githubId: "", branch: "", + watchPaths: [], }, resolver: zodResolver(GithubProviderSchema), }); @@ -113,6 +122,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { }, composePath: data.composePath, githubId: data.githubId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -127,6 +137,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { githubId: data.githubId, sourceType: "github", composeStatus: "idle", + watchPaths: data.watchPaths, }) .then(async () => { toast.success("Service Provided Saved"); @@ -183,7 +194,6 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { )} /> - { )} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
+
+ + + + )} + />