From f2ae39aa8645fb89b115323e82dd10edd58f532d Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:39:54 +0200 Subject: [PATCH 001/144] feat: preview deployments for pull requests with specific labels --- .../show-preview-settings.tsx | 17 +++++++++++++++++ apps/dokploy/pages/api/deploy/github.ts | 13 +++++++++++++ packages/server/src/db/schema/application.ts | 1 + 3 files changed, 31 insertions(+) diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index a0f6ae0e4..7da10ea37 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -42,6 +42,7 @@ const schema = z wildcardDomain: z.string(), port: z.number(), previewLimit: z.number(), + previewLabels: z.string(), previewHttps: z.boolean(), previewPath: z.string(), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), @@ -81,6 +82,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { wildcardDomain: "*.traefik.me", port: 3000, previewLimit: 3, + previewLabels: "", previewHttps: false, previewPath: "/", previewCertificateType: "none", @@ -102,6 +104,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { buildArgs: data.previewBuildArgs || "", wildcardDomain: data.previewWildcard || "*.traefik.me", port: data.previewPort || 3000, + previewLabels: data.previewLabels || "", previewLimit: data.previewLimit || 3, previewHttps: data.previewHttps || false, previewPath: data.previewPath || "/", @@ -119,6 +122,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewBuildArgs: formData.buildArgs, previewWildcard: formData.wildcardDomain, previewPort: formData.port, + previewLabels: formData.previewLabels, applicationId, previewLimit: formData.previewLimit, previewHttps: formData.previewHttps, @@ -200,6 +204,19 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { )} /> + ( + + Labels + + + + + + )} + /> previewLimit) { continue; diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 40793fc88..7ccd44b36 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -119,6 +119,7 @@ export const applications = pgTable("application", { previewEnv: text("previewEnv"), watchPaths: text("watchPaths").array(), previewBuildArgs: text("previewBuildArgs"), + previewLabels: text("previewLabels"), previewWildcard: text("previewWildcard"), previewPort: integer("previewPort").default(3000), previewHttps: boolean("previewHttps").notNull().default(false), From a5bc384d77df6e461c0e44be4c34438373302b59 Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:02:50 +0200 Subject: [PATCH 002/144] run database migration --- .../dokploy/drizzle/0104_yummy_silver_fox.sql | 1 + apps/dokploy/drizzle/meta/0104_snapshot.json | 6142 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + 3 files changed, 6150 insertions(+) create mode 100644 apps/dokploy/drizzle/0104_yummy_silver_fox.sql create mode 100644 apps/dokploy/drizzle/meta/0104_snapshot.json diff --git a/apps/dokploy/drizzle/0104_yummy_silver_fox.sql b/apps/dokploy/drizzle/0104_yummy_silver_fox.sql new file mode 100644 index 000000000..4f3dd4654 --- /dev/null +++ b/apps/dokploy/drizzle/0104_yummy_silver_fox.sql @@ -0,0 +1 @@ +ALTER TABLE "application" ADD COLUMN "previewLabels" text; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0104_snapshot.json b/apps/dokploy/drizzle/meta/0104_snapshot.json new file mode 100644 index 000000000..b01c25118 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0104_snapshot.json @@ -0,0 +1,6142 @@ +{ + "id": "55b7955d-30af-4d68-b9df-6e39cc6a5137", + "prevId": "8bf085dd-e054-4ae6-811b-1d1a68dab752", + "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 + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLabels": { + "name": "previewLabels", + "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'" + }, + "previewCustomCertResolver": { + "name": "previewCustomCertResolver", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewRequireCollaboratorPermissions": { + "name": "previewRequireCollaboratorPermissions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rollbackActive": { + "name": "rollbackActive", + "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'" + }, + "cleanCache": { + "name": "cleanCache", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "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": "'/'" + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "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 + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBuildPath": { + "name": "giteaBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "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 + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": 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 + }, + "isStaticSpa": { + "name": "isStaticSpa", + "type": "boolean", + "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 + }, + "giteaId": { + "name": "giteaId", + "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_giteaId_gitea_giteaId_fk": { + "name": "application_giteaId_gitea_giteaId_fk", + "tableFrom": "application", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "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'" + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "logCleanupCron": { + "name": "logCleanupCron", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0 0 * * *'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allowImpersonation": { + "name": "allowImpersonation", + "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 + }, + "customCertResolver": { + "name": "customCertResolver", + "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'" + }, + "internalPath": { + "name": "internalPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "stripPath": { + "name": "stripPath", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "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 + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "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 + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backupType": { + "name": "backupType", + "type": "backupType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'database'" + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "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_composeId_compose_composeId_fk": { + "name": "backup_composeId_compose_composeId_fk", + "tableFrom": "backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "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" + }, + "backup_userId_user_temp_id_fk": { + "name": "backup_userId_user_temp_id_fk", + "tableFrom": "backup", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "backup_appName_unique": { + "name": "backup_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "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 + }, + "pid": { + "name": "pid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "startedAt": { + "name": "startedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finishedAt": { + "name": "finishedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeBackupId": { + "name": "volumeBackupId", + "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" + }, + "deployment_scheduleId_schedule_scheduleId_fk": { + "name": "deployment_scheduleId_schedule_scheduleId_fk", + "tableFrom": "deployment", + "tableTo": "schedule", + "columnsFrom": [ + "scheduleId" + ], + "columnsTo": [ + "scheduleId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_backupId_backup_backupId_fk": { + "name": "deployment_backupId_backup_backupId_fk", + "tableFrom": "deployment", + "tableTo": "backup", + "columnsFrom": [ + "backupId" + ], + "columnsTo": [ + "backupId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_rollbackId_rollback_rollbackId_fk": { + "name": "deployment_rollbackId_rollback_rollbackId_fk", + "tableFrom": "deployment", + "tableTo": "rollback", + "columnsFrom": [ + "rollbackId" + ], + "columnsTo": [ + "rollbackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_volumeBackupId_volume_backup_volumeBackupId_fk": { + "name": "deployment_volumeBackupId_volume_backup_volumeBackupId_fk", + "tableFrom": "deployment", + "tableTo": "volume_backup", + "columnsFrom": [ + "volumeBackupId" + ], + "columnsTo": [ + "volumeBackupId" + ], + "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 + }, + "publishMode": { + "name": "publishMode", + "type": "publishModeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'host'" + }, + "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 + }, + "giteaRepository": { + "name": "giteaRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaOwner": { + "name": "giteaOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "giteaBranch": { + "name": "giteaBranch", + "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": "''" + }, + "enableSubmodules": { + "name": "enableSubmodules", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "isolatedDeploymentsVolume": { + "name": "isolatedDeploymentsVolume", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "triggerType": { + "name": "triggerType", + "type": "triggerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'push'" + }, + "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 + }, + "watchPaths": { + "name": "watchPaths", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "giteaId": { + "name": "giteaId", + "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_giteaId_gitea_giteaId_fk": { + "name": "compose_giteaId_gitea_giteaId_fk", + "tableFrom": "compose", + "tableTo": "gitea", + "columnsFrom": [ + "giteaId" + ], + "columnsTo": [ + "giteaId" + ], + "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 + }, + "userId": { + "name": "userId", + "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" + }, + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "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.gitea": { + "name": "gitea", + "schema": "", + "columns": { + "giteaId": { + "name": "giteaId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "giteaUrl": { + "name": "giteaUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitea.com'" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "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 + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'repo,repo:status,read:user,read:org'" + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "gitea_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitea_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitea", + "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 + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "scheduleId": { + "name": "scheduleId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shellType": { + "name": "shellType", + "type": "shellType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'bash'" + }, + "scheduleType": { + "name": "scheduleType", + "type": "scheduleType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "script": { + "name": "script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_applicationId_application_applicationId_fk": { + "name": "schedule_applicationId_application_applicationId_fk", + "tableFrom": "schedule", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_composeId_compose_composeId_fk": { + "name": "schedule_composeId_compose_composeId_fk", + "tableFrom": "schedule", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_serverId_server_serverId_fk": { + "name": "schedule_serverId_server_serverId_fk", + "tableFrom": "schedule", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schedule_userId_user_temp_id_fk": { + "name": "schedule_userId_user_temp_id_fk", + "tableFrom": "schedule", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rollback": { + "name": "rollback", + "schema": "", + "columns": { + "rollbackId": { + "name": "rollbackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fullContext": { + "name": "fullContext", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rollback_deploymentId_deployment_deploymentId_fk": { + "name": "rollback_deploymentId_deployment_deploymentId_fk", + "tableFrom": "rollback", + "tableTo": "deployment", + "columnsFrom": [ + "deploymentId" + ], + "columnsTo": [ + "deploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.volume_backup": { + "name": "volume_backup", + "schema": "", + "columns": { + "volumeBackupId": { + "name": "volumeBackupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turnOff": { + "name": "turnOff", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cronExpression": { + "name": "cronExpression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keepLatestCount": { + "name": "keepLatestCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "volume_backup_applicationId_application_applicationId_fk": { + "name": "volume_backup_applicationId_application_applicationId_fk", + "tableFrom": "volume_backup", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_postgresId_postgres_postgresId_fk": { + "name": "volume_backup_postgresId_postgres_postgresId_fk", + "tableFrom": "volume_backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mariadbId_mariadb_mariadbId_fk": { + "name": "volume_backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "volume_backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mongoId_mongo_mongoId_fk": { + "name": "volume_backup_mongoId_mongo_mongoId_fk", + "tableFrom": "volume_backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_mysqlId_mysql_mysqlId_fk": { + "name": "volume_backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "volume_backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_redisId_redis_redisId_fk": { + "name": "volume_backup_redisId_redis_redisId_fk", + "tableFrom": "volume_backup", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_composeId_compose_composeId_fk": { + "name": "volume_backup_composeId_compose_composeId_fk", + "tableFrom": "volume_backup", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "volume_backup_destinationId_destination_destinationId_fk": { + "name": "volume_backup_destinationId_destination_destinationId_fk", + "tableFrom": "volume_backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "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", + "gitea", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.backupType": { + "name": "backupType", + "schema": "public", + "values": [ + "database", + "compose" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo", + "web-server" + ] + }, + "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.publishModeType": { + "name": "publishModeType", + "schema": "public", + "values": [ + "ingress", + "host" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none", + "custom" + ] + }, + "public.triggerType": { + "name": "triggerType", + "schema": "public", + "values": [ + "push", + "tag" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "gitea", + "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", + "gitea" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + }, + "public.scheduleType": { + "name": "scheduleType", + "schema": "public", + "values": [ + "application", + "compose", + "server", + "dokploy-server" + ] + }, + "public.shellType": { + "name": "shellType", + "schema": "public", + "values": [ + "bash", + "sh" + ] + } + }, + "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 d998792f5..0b7c8bbdf 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -729,6 +729,13 @@ "when": 1752465764072, "tag": "0103_cultured_pestilence", "breakpoints": true + }, + { + "idx": 104, + "version": "7", + "when": 1753376537774, + "tag": "0104_yummy_silver_fox", + "breakpoints": true } ] } \ No newline at end of file From a0bbf7be23ffaf661e25d28d2bab86dcecc8afc3 Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:35:33 +0200 Subject: [PATCH 003/144] add check for presence of labels --- apps/dokploy/pages/api/deploy/github.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 6430255f1..5ea99989e 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -443,17 +443,19 @@ export default async function handler( for (const app of secureApps) { // check for labels - let hasLabel: boolean = false; - const labels = githubBody?.pull_request?.labels; - const previewLabels = app?.previewLabels?.split(","); - for (const label of labels) { - if (previewLabels.contains(label.name)) { - hasLabel = true; - break; + if (app?.previewLabels != "") { + const previewLabels = app?.previewLabels?.split(","); + let hasLabel: boolean = false; + const labels = githubBody?.pull_request?.labels; + for (const label of labels) { + if (previewLabels.contains(label.name)) { + hasLabel = true; + break; + } } - } - if (hasLabel) - continue; + if (hasLabel) + continue; + } const previewLimit = app?.previewLimit || 0; if (app?.previewDeployments?.length > previewLimit) { From 1f9ef473f16dfd931d7952ef2bedf1e3762e8d4d Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:45:43 +0200 Subject: [PATCH 004/144] format some files --- .../preview-deployments/show-preview-settings.tsx | 8 ++++---- apps/dokploy/pages/api/deploy/github.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 7da10ea37..569f75257 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -42,7 +42,7 @@ const schema = z wildcardDomain: z.string(), port: z.number(), previewLimit: z.number(), - previewLabels: z.string(), + previewLabels: z.string(), previewHttps: z.boolean(), previewPath: z.string(), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), @@ -82,7 +82,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { wildcardDomain: "*.traefik.me", port: 3000, previewLimit: 3, - previewLabels: "", + previewLabels: "", previewHttps: false, previewPath: "/", previewCertificateType: "none", @@ -104,7 +104,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { buildArgs: data.previewBuildArgs || "", wildcardDomain: data.previewWildcard || "*.traefik.me", port: data.previewPort || 3000, - previewLabels: data.previewLabels || "", + previewLabels: data.previewLabels || "", previewLimit: data.previewLimit || 3, previewHttps: data.previewHttps || false, previewPath: data.previewPath || "/", @@ -122,7 +122,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewBuildArgs: formData.buildArgs, previewWildcard: formData.wildcardDomain, previewPort: formData.port, - previewLabels: formData.previewLabels, + previewLabels: formData.previewLabels, applicationId, previewLimit: formData.previewLimit, previewHttps: formData.previewHttps, diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 5ea99989e..22fc5446d 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -443,8 +443,8 @@ export default async function handler( for (const app of secureApps) { // check for labels - if (app?.previewLabels != "") { - const previewLabels = app?.previewLabels?.split(","); + if (app?.previewLabels != "") { + const previewLabels = app?.previewLabels?.split(","); let hasLabel: boolean = false; const labels = githubBody?.pull_request?.labels; for (const label of labels) { @@ -454,8 +454,8 @@ export default async function handler( } } if (hasLabel) - continue; - } + continue; + } const previewLimit = app?.previewLimit || 0; if (app?.previewDeployments?.length > previewLimit) { From 9baafb83ff5d19224bc55298340894d3779382a4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 07:38:28 +0000 Subject: [PATCH 005/144] [autofix.ci] apply automated fixes --- .../preview-deployments/show-preview-settings.tsx | 5 ++++- apps/dokploy/pages/api/deploy/github.ts | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 569f75257..7e59482d4 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -211,7 +211,10 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { Labels - + diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 22fc5446d..3ef822c18 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -453,8 +453,7 @@ export default async function handler( break; } } - if (hasLabel) - continue; + if (hasLabel) continue; } const previewLimit = app?.previewLimit || 0; From 22c7c6e6fb0d86082973dcd57680abe83d2f34f5 Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sun, 3 Aug 2025 18:25:51 +0900 Subject: [PATCH 006/144] ci(pull-request): use strategy matrix --- .github/workflows/pull-request.yml | 39 +++++++----------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e9591f3cc..6c74dbc02 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -4,9 +4,15 @@ on: pull_request: branches: [main, canary] +permissions: + contents: read + jobs: - lint-and-typecheck: + pr-check: runs-on: ubuntu-latest + strategy: + matrix: + job: [build, test, typecheck] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 @@ -15,32 +21,5 @@ jobs: node-version: 20.16.0 cache: "pnpm" - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm typecheck - - build-and-test: - needs: lint-and-typecheck - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.16.0 - cache: "pnpm" - - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm build - - parallel-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.16.0 - cache: "pnpm" - - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm test + - run: pnpm server:build + - run: pnpm ${{ matrix.job }} From 1d94c85c2b8e4f21ebef824165c79ac1289e8137 Mon Sep 17 00:00:00 2001 From: depado Date: Tue, 5 Aug 2025 14:53:35 +0200 Subject: [PATCH 007/144] fix(setup): properly handle dokploy-traefik container absence --- packages/server/src/setup/traefik-setup.ts | 23 ++++++++-------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index cf10d7fa1..ccdfa30f8 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -89,21 +89,14 @@ export const initializeStandaloneTraefik = async ({ const docker = await getRemoteDocker(serverId); try { const container = docker.getContainer(containerName); - try { - await container.remove({ force: true }); - await new Promise((resolve) => setTimeout(resolve, 5000)); - await docker.createContainer(settings); - const newContainer = docker.getContainer(containerName); - await newContainer.start(); - console.log("Traefik Started ✅"); - } catch (error) { - console.error("Error in initializeStandaloneTraefik", error); - } - } catch (error) { - await docker.createContainer(settings); - console.error("Error in initializeStandaloneTraefik", error); - throw error; - } + await container.remove({ force: true }); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } catch {} + + await docker.createContainer(settings); + const newContainer = docker.getContainer(containerName); + await newContainer.start(); + console.log("Traefik Started ✅"); }; export const initializeTraefikService = async ({ From 33873ce1e9389f9ee65ddac461e61ac9cdc195cf Mon Sep 17 00:00:00 2001 From: A-D-E Date: Tue, 5 Aug 2025 21:31:15 +0200 Subject: [PATCH 008/144] fix: use configured GitLab URL instead of hardcoded gitlab.com The previous pagination implementation accidentally hardcoded the GitLab URL to gitlab.com, breaking the integration for self-hosted GitLab instances. Changes: - Replace hardcoded 'https://gitlab.com' with gitlabProvider.gitlabUrl - Maintains the pagination functionality added in previous commit Fixes: GitLab branches API calls failing for self-hosted instances --- packages/server/src/utils/providers/gitlab.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 774cd818f..1971308b2 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -316,7 +316,7 @@ export const getGitlabBranches = async (input: { while (true) { const branchesResponse = await fetch( - `https://gitlab.com/api/v4/projects/${input.id}/repository/branches?page=${page}&per_page=${perPage}`, + `${gitlabProvider.gitlabUrl}/api/v4/projects/${input.id}/repository/branches?page=${page}&per_page=${perPage}`, { headers: { Authorization: `Bearer ${gitlabProvider.accessToken}`, From 883e1d1bfe823f8d1b85beb6c6b3a8ea2cc70148 Mon Sep 17 00:00:00 2001 From: Aeriit Date: Sat, 9 Aug 2025 11:05:37 -0400 Subject: [PATCH 009/144] fix(traefik): on setup support serverId as parameter and input --- packages/server/src/services/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index 613a97b0d..fc8e57e43 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -393,7 +393,7 @@ export const readPorts = async ( export const writeTraefikSetup = async ( input: TraefikOptions, - serverId?: string, + serverId = input.serverId, ) => { const resourceType = await getDockerResourceType("dokploy-traefik", serverId); if (resourceType === "service") { From edbdc01a1e81741eb0b51335b340da1ed036cebb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 06:12:28 -0600 Subject: [PATCH 010/144] chore(package): bump version to v0.24.9 --- 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 7af18336b..481c4956c 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.8", + "version": "v0.24.9", "private": true, "license": "Apache-2.0", "type": "module", From 112a1dedec3806cd07968c943a8b30fe87a3e990 Mon Sep 17 00:00:00 2001 From: Bob Mannino Date: Sun, 10 Aug 2025 14:34:06 +0100 Subject: [PATCH 011/144] feat: add keyboard shortcuts to application page --- apps/dokploy/hooks/use-keyboard-nav.tsx | 77 +++++++++++++++++++ .../services/application/[applicationId].tsx | 2 + 2 files changed, 79 insertions(+) create mode 100644 apps/dokploy/hooks/use-keyboard-nav.tsx diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx new file mode 100644 index 000000000..318f06914 --- /dev/null +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; + +const SHORTCUTS = { + g: "general", + e: "environment", + u: "domains", + p: "preview-deployments", + s: "schedules", + v: "volume-backups", + d: "deployments", + l: "logs", + m: "monitoring", + a: "advanced", +}; + +/** + * Use this to register keyboard shortcuts for the application page. Each + * shortcut must be prefixed with `g` (like GitHub). + * + * - `g g` "General", + * - `g e` "Environment", + * - `g u` "Domains", + * - `g p` "Preview Deployments", + * - `g s` "Schedules", + * - `g v` "Volume Backups", + * - `g d` "Deployments", + * - `g l` "Logs", + * - `g m` "Monitoring", + * - `g a` "Advanced" + */ +export function UseKeyboardNavForApplications() { + const [isModPressed, setModPressed] = useState(false); + const [timer, setTimer] = useState(null); + + const sp = useSearchParams(); + const router = useRouter(); + const pathname = usePathname(); + + const updateSearchParam = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(sp.toString()); + params.set(name, value); + + return params.toString(); + }, + [sp], + ); + + useEffect(() => { + const handleKeyDown = ({ key }: KeyboardEvent) => { + if (isModPressed) { + if (timer) clearTimeout(timer); + setModPressed(false); + + if (key in SHORTCUTS) { + const tab = SHORTCUTS[key]; + router.push( + `${pathname}?${updateSearchParam("tab", tab.toLowerCase())}`, + ); + } + } else { + if (key === "g") { + setModPressed(true); + setTimer(setTimeout(() => setModPressed(false), 5000)); + } + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isModPressed, timer, updateSearchParam, router, pathname]); + + return null; +} 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 209f6f65f..104b1ff7b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -51,6 +51,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; +import { UseKeyboardNavForApplications } from "@/hooks/use-keyboard-nav"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; @@ -91,6 +92,7 @@ const Service = ( return (
+ Date: Sun, 10 Aug 2025 15:03:41 -0600 Subject: [PATCH 012/144] fix(traefik): streamline serverId handling in writeTraefikSetup function --- packages/server/src/services/settings.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index fc8e57e43..e4402892f 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -391,22 +391,22 @@ export const readPorts = async ( ); }; -export const writeTraefikSetup = async ( - input: TraefikOptions, - serverId = input.serverId, -) => { - const resourceType = await getDockerResourceType("dokploy-traefik", serverId); +export const writeTraefikSetup = async (input: TraefikOptions) => { + const resourceType = await getDockerResourceType( + "dokploy-traefik", + input.serverId, + ); if (resourceType === "service") { await initializeTraefikService({ env: input.env, additionalPorts: input.additionalPorts, - serverId: serverId, + serverId: input.serverId, }); } else { await initializeStandaloneTraefik({ env: input.env, additionalPorts: input.additionalPorts, - serverId: serverId, + serverId: input.serverId, }); } }; From 4b1146ab6be1412a3570c1fa260d1205af5a05c0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:58:15 -0600 Subject: [PATCH 013/144] remove the "isWildcard" column from the "domain" table in the database schema --- .../compose/advanced/add-isolation.tsx | 195 ++++++++++++++++++ .../compose/general/compose-file-editor.tsx | 15 +- .../compose/general/show-utilities.tsx | 46 ----- .../services/compose/[composeId].tsx | 31 +-- 4 files changed, 217 insertions(+), 70 deletions(-) create mode 100644 apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx delete mode 100644 apps/dokploy/components/dashboard/compose/general/show-utilities.tsx diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx new file mode 100644 index 000000000..55cb0d906 --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx @@ -0,0 +1,195 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { AlertTriangle } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { AlertBlock } from "@/components/shared/alert-block"; +import { CodeEditor } from "@/components/shared/code-editor"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, +} from "@/components/ui/form"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import { api } from "@/utils/api"; + +interface Props { + composeId: string; +} + +// Schema for Isolated Deployment +const isolatedSchema = z.object({ + isolatedDeployment: z.boolean().optional(), +}); + +type IsolatedSchema = z.infer; + +export const IsolatedDeploymentTab = ({ composeId }: Props) => { + const utils = api.useUtils(); + const [compose, setCompose] = useState(""); + const { mutateAsync, error, isError } = + api.compose.isolatedDeployment.useMutation(); + + const { mutateAsync: updateCompose } = api.compose.update.useMutation(); + + const { data, refetch } = api.compose.one.useQuery( + { composeId }, + { enabled: !!composeId }, + ); + + const form = useForm({ + defaultValues: { + isolatedDeployment: false, + }, + resolver: zodResolver(isolatedSchema), + }); + + useEffect(() => { + randomizeCompose(); + if (data) { + form.reset({ + isolatedDeployment: data?.isolatedDeployment || false, + }); + } + }, [form, form.reset, form.formState.isSubmitSuccessful, data]); + + const onSubmit = async (formData: IsolatedSchema) => { + await updateCompose({ + composeId, + isolatedDeployment: formData?.isolatedDeployment || false, + }) + .then(async (_data) => { + await randomizeCompose(); + await refetch(); + toast.success("Compose updated"); + }) + .catch(() => { + toast.error("Error updating the compose"); + }); + }; + + const randomizeCompose = async () => { + await mutateAsync({ + composeId, + suffix: data?.appName || "", + }).then(async (data) => { + await utils.project.all.invalidate(); + setCompose(data); + }); + }; + + return ( + + + Enable Isolated Deployment + + Configure isolated deployment to the compose file. + + + +
+
+ + This feature creates an isolated environment for your deployment + by adding unique prefixes to all resources. It establishes a + dedicated network based on your compose file's name, ensuring your + services run in isolation. This prevents conflicts when running + multiple instances of the same template or services with identical + names. + +
+
+

+ Resources that will be isolated: +

+
    +
  • Docker volumes
  • +
  • Docker networks
  • +
+
+
+
+ {isError && {error?.message}} +
+ + {isError && ( +
+ + + {error?.message} + +
+ )} + +
+
+ ( + +
+ + Enable Isolated Deployment ({data?.appName}) + + + Enable isolated deployment to the compose file. + +
+ + + +
+ )} + /> +
+ +
+ +
+
+
+ +
+									
+								
+
+
+ +
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index 41e40efbe..c2db472d2 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; import { @@ -8,13 +13,7 @@ import { FormMessage, } from "@/components/ui/form"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config"; -import { ShowUtilities } from "./show-utilities"; interface Props { composeId: string; @@ -142,9 +141,7 @@ services:
-
- -
+
- - - - Utilities - Modify the application data - - - - Isolated Deployment - Randomize Compose - - - - - - - - - - - ); -}; 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 dd5383697..fe229aa5a 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -1,3 +1,17 @@ +import { validateRequest } from "@dokploy/server/lib/auth"; +import { createServerSideHelpers } from "@trpc/react-query/server"; +import copy from "copy-to-clipboard"; +import { CircuitBoard, HelpCircle, ServerOff } from "lucide-react"; +import type { + GetServerSidePropsContext, + InferGetServerSidePropsType, +} from "next"; +import Head from "next/head"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { type ReactElement, useEffect, useState } from "react"; +import { toast } from "sonner"; +import superjson from "superjson"; import { ShowImport } from "@/components/dashboard/application/advanced/import/show-import"; import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowDeployments } from "@/components/dashboard/application/deployments/show-deployments"; @@ -6,6 +20,7 @@ import { ShowEnvironment } from "@/components/dashboard/application/environment/ import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules"; import { ShowVolumeBackups } from "@/components/dashboard/application/volume-backups/show-volume-backups"; import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-command"; +import { IsolatedDeploymentTab } from "@/components/dashboard/compose/advanced/add-isolation"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; import { ShowDockerLogsCompose } from "@/components/dashboard/compose/logs/show"; @@ -35,21 +50,6 @@ import { } from "@/components/ui/tooltip"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server/lib/auth"; -import { createServerSideHelpers } from "@trpc/react-query/server"; -import copy from "copy-to-clipboard"; -import { CircuitBoard, ServerOff } from "lucide-react"; -import { HelpCircle } from "lucide-react"; -import type { - GetServerSidePropsContext, - InferGetServerSidePropsType, -} from "next"; -import Head from "next/head"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { type ReactElement, useEffect, useState } from "react"; -import { toast } from "sonner"; -import superjson from "superjson"; type TabState = | "projects" @@ -351,6 +351,7 @@ const Service = ( +
From 1fe12ba93e2eff1e93c99a18ec0e3005407a5511 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:38:10 -0600 Subject: [PATCH 014/144] feat(isolation): add preview functionality for isolated deployment with loading state and dialog --- .../compose/advanced/add-isolation.tsx | 101 +++++++++++++----- .../general/show-converted-compose.tsx | 2 +- packages/server/src/utils/docker/collision.ts | 40 +++++-- 3 files changed, 107 insertions(+), 36 deletions(-) diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx index 55cb0d906..cea716858 100644 --- a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx +++ b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx @@ -1,5 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle } from "lucide-react"; +import { AlertTriangle, Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -14,6 +14,13 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { Form, FormControl, @@ -22,7 +29,7 @@ import { FormItem, FormLabel, } from "@/components/ui/form"; -import { Label } from "@/components/ui/label"; + import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; @@ -40,9 +47,12 @@ type IsolatedSchema = z.infer; export const IsolatedDeploymentTab = ({ composeId }: Props) => { const utils = api.useUtils(); const [compose, setCompose] = useState(""); + const [isPreviewLoading, setIsPreviewLoading] = useState(false); const { mutateAsync, error, isError } = api.compose.isolatedDeployment.useMutation(); + const [isOpenPreview, setIsOpenPreview] = useState(false); + const { mutateAsync: updateCompose } = api.compose.update.useMutation(); const { data, refetch } = api.compose.one.useQuery( @@ -58,7 +68,6 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => { }); useEffect(() => { - randomizeCompose(); if (data) { form.reset({ isolatedDeployment: data?.isolatedDeployment || false, @@ -72,7 +81,6 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => { isolatedDeployment: formData?.isolatedDeployment || false, }) .then(async (_data) => { - await randomizeCompose(); await refetch(); toast.success("Compose updated"); }) @@ -81,26 +89,31 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => { }); }; - const randomizeCompose = async () => { - await mutateAsync({ - composeId, - suffix: data?.appName || "", - }).then(async (data) => { - await utils.project.all.invalidate(); - setCompose(data); - }); + const generatePreview = async () => { + setIsOpenPreview(true); + setIsPreviewLoading(true); + try { + await mutateAsync({ + composeId, + suffix: data?.appName || "", + }).then(async (data) => { + await utils.project.all.invalidate(); + setCompose(data); + }); + } catch { + toast.error("Error generating preview"); + setIsOpenPreview(false); + } finally { + setIsPreviewLoading(false); + } }; return ( - Enable Isolated Deployment + Enable Isolated Deployment Configure isolated deployment to the compose file. - - - -
This feature creates an isolated environment for your deployment @@ -122,6 +135,10 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => {
+ + + +
{isError && {error?.message}}
{
-
- -
-									
-								
+ +
+ + + + + Isolated Deployment Preview + + Preview of the compose file with isolated deployment + configuration + + +
+ {isPreviewLoading ? ( +
+ +

+ Generating compose preview... +

+
+ ) : ( +
+													
+												
+ )} +
+
+
diff --git a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx index 253a5fde3..fac6c2a34 100644 --- a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx @@ -62,7 +62,7 @@ export const ShowConvertedCompose = ({ composeId }: Props) => { {isError && {error?.message}} - + Preview your docker-compose file with added domains. Note: At least one domain must be specified for this conversion to take effect. diff --git a/packages/server/src/utils/docker/collision.ts b/packages/server/src/utils/docker/collision.ts index 9d399dc0d..de6d9bbb3 100644 --- a/packages/server/src/utils/docker/collision.ts +++ b/packages/server/src/utils/docker/collision.ts @@ -1,8 +1,14 @@ import { findComposeById } from "@dokploy/server/services/compose"; -import { dump, load } from "js-yaml"; +import { dump } from "js-yaml"; import { addAppNameToAllServiceNames } from "./collision/root-network"; import { generateRandomHash } from "./compose"; import { addSuffixToAllVolumes } from "./compose/volume"; +import { + cloneCompose, + cloneComposeRemote, + loadDockerCompose, + loadDockerComposeRemote, +} from "./domain"; import type { ComposeSpecification } from "./types"; export const addAppNameToPreventCollision = ( @@ -24,16 +30,34 @@ export const randomizeIsolatedDeploymentComposeFile = async ( suffix?: string, ) => { const compose = await findComposeById(composeId); - const composeFile = compose.composeFile; - const composeData = load(composeFile) as ComposeSpecification; + + if (compose.serverId) { + await cloneComposeRemote(compose); + } else { + await cloneCompose(compose); + } + + let composeData: ComposeSpecification | null; + + if (compose.serverId) { + composeData = await loadDockerComposeRemote(compose); + } else { + composeData = await loadDockerCompose(compose); + } + + if (!composeData) { + throw new Error("Compose data not found"); + } const randomSuffix = suffix || compose.appName || generateRandomHash(); - const newComposeFile = addAppNameToPreventCollision( - composeData, - randomSuffix, - compose.isolatedDeploymentsVolume, - ); + const newComposeFile = compose.isolatedDeployment + ? addAppNameToPreventCollision( + composeData, + randomSuffix, + compose.isolatedDeploymentsVolume, + ) + : composeData; return dump(newComposeFile); }; From 85bce827eb617b8b59a97004836db557fecc24eb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:41:18 -0600 Subject: [PATCH 015/144] fix(keyboard-nav): ensure correct type for shortcut keys in navigation --- apps/dokploy/hooks/use-keyboard-nav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 318f06914..d24064c31 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -56,7 +56,7 @@ export function UseKeyboardNavForApplications() { setModPressed(false); if (key in SHORTCUTS) { - const tab = SHORTCUTS[key]; + const tab = SHORTCUTS[key as keyof typeof SHORTCUTS]; router.push( `${pathname}?${updateSearchParam("tab", tab.toLowerCase())}`, ); From cfa01359329ee2ad18b4e756ac38189f441b4dbb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:42:50 -0600 Subject: [PATCH 016/144] remove: delete IsolatedDeployment component from dashboard --- .../compose/general/isolated-deployment.tsx | 188 ------------------ 1 file changed, 188 deletions(-) delete mode 100644 apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx diff --git a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx b/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx deleted file mode 100644 index d76f79021..000000000 --- a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { AlertBlock } from "@/components/shared/alert-block"; -import { CodeEditor } from "@/components/shared/code-editor"; -import { Button } from "@/components/ui/button"; -import { - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, -} from "@/components/ui/form"; -import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; -import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; - -interface Props { - composeId: string; -} - -const schema = z.object({ - isolatedDeployment: z.boolean().optional(), -}); - -type Schema = z.infer; - -export const IsolatedDeployment = ({ composeId }: Props) => { - const utils = api.useUtils(); - const [compose, setCompose] = useState(""); - const { mutateAsync, error, isError } = - api.compose.isolatedDeployment.useMutation(); - - const { mutateAsync: updateCompose } = api.compose.update.useMutation(); - - const { data, refetch } = api.compose.one.useQuery( - { composeId }, - { enabled: !!composeId }, - ); - - console.log(data); - - const form = useForm({ - defaultValues: { - isolatedDeployment: false, - }, - resolver: zodResolver(schema), - }); - - useEffect(() => { - randomizeCompose(); - if (data) { - form.reset({ - isolatedDeployment: data?.isolatedDeployment || false, - }); - } - }, [form, form.reset, form.formState.isSubmitSuccessful, data]); - - const onSubmit = async (formData: Schema) => { - await updateCompose({ - composeId, - isolatedDeployment: formData?.isolatedDeployment || false, - }) - .then(async (_data) => { - await randomizeCompose(); - await refetch(); - toast.success("Compose updated"); - }) - .catch(() => { - toast.error("Error updating the compose"); - }); - }; - - const randomizeCompose = async () => { - await mutateAsync({ - composeId, - suffix: data?.appName || "", - }).then(async (data) => { - await utils.project.all.invalidate(); - setCompose(data); - }); - }; - - return ( - <> - - Isolate Deployment - - Use this option to isolate the deployment of this compose file. - - -
- - This feature creates an isolated environment for your deployment by - adding unique prefixes to all resources. It establishes a dedicated - network based on your compose file's name, ensuring your services run - in isolation. This prevents conflicts when running multiple instances - of the same template or services with identical names. - -
-
-

- Resources that will be isolated: -

-
    -
  • Docker volumes
  • -
  • Docker networks
  • -
-
-
-
- {isError && {error?.message}} -
- - {isError && ( -
- - - {error?.message} - -
- )} - -
-
- ( - -
- - Enable Isolated Deployment ({data?.appName}) - - - Enable isolated deployment to the compose file. - -
- - - -
- )} - /> -
- -
- -
-
-
- -
-							
-						
-
-
- - - ); -}; From 231b8ed19d9c1c82654bc9b0d0d58e0157e86832 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:43:03 -0600 Subject: [PATCH 017/144] remove: eliminate Docker volumes from isolated deployment resources list --- .../components/dashboard/compose/advanced/add-isolation.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx index cea716858..5b6e04154 100644 --- a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx +++ b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx @@ -129,7 +129,6 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => { Resources that will be isolated:
    -
  • Docker volumes
  • Docker networks
From ef6dcaf363f38a00b1ea7005d35e7012952e75bd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:23:15 -0600 Subject: [PATCH 018/144] chore(package): bump version to v0.24.10 --- 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 481c4956c..2758bbced 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.9", + "version": "v0.24.10", "private": true, "license": "Apache-2.0", "type": "module", From 9763dce0450a5f59556c1ece6a8758c93a8b66ae Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:26:20 -0600 Subject: [PATCH 019/144] fix(swarm): adjust validation for containerId to allow empty array --- apps/dokploy/server/api/routers/swarm.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dokploy/server/api/routers/swarm.ts b/apps/dokploy/server/api/routers/swarm.ts index 91409a75c..cd3b042e9 100644 --- a/apps/dokploy/server/api/routers/swarm.ts +++ b/apps/dokploy/server/api/routers/swarm.ts @@ -67,8 +67,7 @@ export const swarmRouter = createTRPCRouter({ .string() .min(1) .regex(containerIdRegex, "Invalid app name.") - .array() - .min(1), + .array(), serverId: z.string().optional(), }), ) From f0278f354b7346f84e79d9573a909063531a8cc1 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Mon, 11 Aug 2025 09:56:49 +0300 Subject: [PATCH 020/144] Added support for Basic Auth present in the GitLab URLs --- .../pages/api/providers/gitlab/callback.ts | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/pages/api/providers/gitlab/callback.ts b/apps/dokploy/pages/api/providers/gitlab/callback.ts index e32e0a7ad..cb51da9d2 100644 --- a/apps/dokploy/pages/api/providers/gitlab/callback.ts +++ b/apps/dokploy/pages/api/providers/gitlab/callback.ts @@ -12,12 +12,29 @@ export default async function handler( } const gitlab = await findGitlabById(gitlabId as string); + const gitlabUrl = new URL(gitlab.gitlabUrl) - const response = await fetch(`${gitlab.gitlabUrl}/oauth/token`, { + const headers: HeadersInit = { + "Content-Type": "application/x-www-form-urlencoded" + } + + // In case of basic auth being present in the URL, we need to remove it from the URL + // and add it to the Authorization header. + if (gitlabUrl.username && gitlabUrl.password) { + headers.Authorization = `Basic ${Buffer.from(`${gitlabUrl.username}:${gitlabUrl.password}`).toString("base64")}`; + } + + const url = (gitlabUrl.username && gitlabUrl.password) + ? new URL(gitlabUrl, { + ...gitlabUrl, + username: "", + password: "", + }).toString() + : gitlabUrl.toString(); + + const response = await fetch(`${url}/oauth/token`, { method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, + headers, body: new URLSearchParams({ client_id: gitlab.applicationId as string, client_secret: gitlab.secret as string, From 429c1e4cd8e292df3b0ebd6579b0c4ff140d83e5 Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:03:30 +0200 Subject: [PATCH 021/144] feat: better UI for submitting labels --- .../show-preview-settings.tsx | 111 +- .../dokploy/drizzle/0104_yummy_silver_fox.sql | 1 - apps/dokploy/drizzle/0106_purple_maggott.sql | 1 + apps/dokploy/drizzle/meta/0106_snapshot.json | 6424 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/pages/api/deploy/github.ts | 5 +- packages/server/src/db/schema/application.ts | 2 +- 7 files changed, 6526 insertions(+), 25 deletions(-) delete mode 100644 apps/dokploy/drizzle/0104_yummy_silver_fox.sql create mode 100644 apps/dokploy/drizzle/0106_purple_maggott.sql create mode 100644 apps/dokploy/drizzle/meta/0106_snapshot.json diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 7e59482d4..0aa676057 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -1,4 +1,5 @@ import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, @@ -27,9 +28,15 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Settings2 } from "lucide-react"; +import { HelpCircle, Plus, Settings2, X } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -42,7 +49,7 @@ const schema = z wildcardDomain: z.string(), port: z.number(), previewLimit: z.number(), - previewLabels: z.string(), + previewLabels: z.array(z.string()).optional(), previewHttps: z.boolean(), previewPath: z.string(), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), @@ -82,7 +89,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { wildcardDomain: "*.traefik.me", port: 3000, previewLimit: 3, - previewLabels: "", + previewLabels: [], previewHttps: false, previewPath: "/", previewCertificateType: "none", @@ -104,7 +111,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { buildArgs: data.previewBuildArgs || "", wildcardDomain: data.previewWildcard || "*.traefik.me", port: data.previewPort || 3000, - previewLabels: data.previewLabels || "", + previewLabels: data.previewLabels || [], previewLimit: data.previewLimit || 3, previewHttps: data.previewHttps || false, previewPath: data.previewPath || "/", @@ -204,22 +211,86 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { )} /> - ( - - Labels - - - - - - )} - /> + ( + +
+ Preview Labels + + + + + + +

+ Add a labels that will trigger a preview deployment + for a pull request. If no labels are specified, all + pull requests will trigger a preview deployment. +

+
+
+
+
+
+ {field.value?.map((label, index) => ( + + {label} + { + const newLabels = [...(field.value || [])]; + newLabels.splice(index, 1); + field.onChange(newLabels); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const label = input.value.trim(); + if (label) { + field.onChange([...(field.value || []), label]); + input.value = ""; + } + } + }} + /> + + +
+ +
+ )} + /> 0) { let hasLabel: boolean = false; const labels = githubBody?.pull_request?.labels; for (const label of labels) { - if (previewLabels.contains(label.name)) { + if (app?.previewLabels.contains(label.name)) { hasLabel = true; break; } diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 7263b895f..21ee9fbda 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -79,7 +79,7 @@ export const applications = pgTable("application", { previewEnv: text("previewEnv"), watchPaths: text("watchPaths").array(), previewBuildArgs: text("previewBuildArgs"), - previewLabels: text("previewLabels"), + previewLabels: text("previewLabels").array(), previewWildcard: text("previewWildcard"), previewPort: integer("previewPort").default(3000), previewHttps: boolean("previewHttps").notNull().default(false), From 15e62961e8f5c595f9d53d530822c340abf54653 Mon Sep 17 00:00:00 2001 From: PiquelChips <63727792+PiquelChips@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:09:02 +0200 Subject: [PATCH 022/144] fix: would only create previews if none of the labels were present --- apps/dokploy/pages/api/deploy/github.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index bdb0c7fe2..7e3df5444 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -452,7 +452,7 @@ export default async function handler( break; } } - if (hasLabel) continue; + if (!hasLabel) continue; } const previewLimit = app?.previewLimit || 0; From a4bbcea28210ae7b27b90848a583593542ad11f4 Mon Sep 17 00:00:00 2001 From: Bob Mannino Date: Mon, 11 Aug 2025 18:25:38 +0100 Subject: [PATCH 023/144] add keyboard shortcuts for compose/redis/postgres pages --- apps/dokploy/hooks/use-keyboard-nav.tsx | 67 +++++++++++++++---- .../services/application/[applicationId].tsx | 4 +- .../services/compose/[composeId].tsx | 2 + .../services/postgres/[postgresId].tsx | 2 + .../[projectId]/services/redis/[redisId].tsx | 2 + 5 files changed, 61 insertions(+), 16 deletions(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index d24064c31..3d4d052c6 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -3,7 +3,26 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; -const SHORTCUTS = { +const PAGES = ["compose", "application", "postgres", "redis"] as const; +type Page = typeof PAGES[number]; + +type Shortcuts = Record; +type ShortcutsDictionary = Record; + +const COMPOSE_SHORTCUTS: Shortcuts = { + g: "general", + e: "environment", + u: "domains", + d: "deployments", + b: "backups", + s: "schedules", + v: "volumeBackups", + l: "logs", + m: "monitoring", + a: "advanced", +}; + +const APPLICATION_SHORTCUTS: Shortcuts = { g: "general", e: "environment", u: "domains", @@ -16,22 +35,40 @@ const SHORTCUTS = { a: "advanced", }; +const POSTGRES_SHORTCUTS: Shortcuts = { + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + b: "backups", + a: "advanced", +}; + +const REDIS_SHORTCUTS: Shortcuts = { + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + a: "advanced", +}; + +const SHORTCUTS: ShortcutsDictionary = { + application: APPLICATION_SHORTCUTS, + compose: COMPOSE_SHORTCUTS, + postgres: POSTGRES_SHORTCUTS, + redis: REDIS_SHORTCUTS, +}; + /** - * Use this to register keyboard shortcuts for the application page. Each - * shortcut must be prefixed with `g` (like GitHub). + * Use this to register keyboard shortcuts for different pages. Each shortcut + * must be prefixed with `g` (like GitHub). * + * @example * - `g g` "General", * - `g e` "Environment", * - `g u` "Domains", - * - `g p` "Preview Deployments", - * - `g s` "Schedules", - * - `g v` "Volume Backups", - * - `g d` "Deployments", - * - `g l` "Logs", - * - `g m` "Monitoring", - * - `g a` "Advanced" */ -export function UseKeyboardNavForApplications() { +export function UseKeyboardNav({ forPage }: { forPage: Page }) { const [isModPressed, setModPressed] = useState(false); const [timer, setTimer] = useState(null); @@ -39,6 +76,8 @@ export function UseKeyboardNavForApplications() { const router = useRouter(); const pathname = usePathname(); + const shortcuts = SHORTCUTS[forPage]; + const updateSearchParam = useCallback( (name: string, value: string) => { const params = new URLSearchParams(sp.toString()); @@ -55,10 +94,10 @@ export function UseKeyboardNavForApplications() { if (timer) clearTimeout(timer); setModPressed(false); - if (key in SHORTCUTS) { - const tab = SHORTCUTS[key as keyof typeof SHORTCUTS]; + if (key in shortcuts) { + const tab = shortcuts[key]!; router.push( - `${pathname}?${updateSearchParam("tab", tab.toLowerCase())}`, + `${pathname}?${updateSearchParam("tab", tab)}`, ); } } else { 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 104b1ff7b..305a931ce 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -51,7 +51,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import { UseKeyboardNavForApplications } from "@/hooks/use-keyboard-nav"; +import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; @@ -92,7 +92,7 @@ const Service = ( return (
- + + + + Date: Mon, 11 Aug 2025 19:15:54 +0100 Subject: [PATCH 024/144] format --- apps/dokploy/hooks/use-keyboard-nav.tsx | 148 ++++++++++++------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 3d4d052c6..3a99dbc61 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -10,46 +10,46 @@ type Shortcuts = Record; type ShortcutsDictionary = Record; const COMPOSE_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - u: "domains", - d: "deployments", - b: "backups", - s: "schedules", - v: "volumeBackups", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + u: "domains", + d: "deployments", + b: "backups", + s: "schedules", + v: "volumeBackups", + l: "logs", + m: "monitoring", + a: "advanced", }; const APPLICATION_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - u: "domains", - p: "preview-deployments", - s: "schedules", - v: "volume-backups", - d: "deployments", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + u: "domains", + p: "preview-deployments", + s: "schedules", + v: "volume-backups", + d: "deployments", + l: "logs", + m: "monitoring", + a: "advanced", }; const POSTGRES_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - l: "logs", - m: "monitoring", - b: "backups", - a: "advanced", + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + b: "backups", + a: "advanced", }; const REDIS_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + a: "advanced", }; const SHORTCUTS: ShortcutsDictionary = { @@ -60,57 +60,57 @@ const SHORTCUTS: ShortcutsDictionary = { }; /** - * Use this to register keyboard shortcuts for different pages. Each shortcut - * must be prefixed with `g` (like GitHub). - * - * @example - * - `g g` "General", - * - `g e` "Environment", - * - `g u` "Domains", - */ +* Use this to register keyboard shortcuts for different pages. Each shortcut +* must be prefixed with `g` (like GitHub). +* +* @example +* - `g g` "General", +* - `g e` "Environment", +* - `g u` "Domains", +*/ export function UseKeyboardNav({ forPage }: { forPage: Page }) { - const [isModPressed, setModPressed] = useState(false); - const [timer, setTimer] = useState(null); + const [isModPressed, setModPressed] = useState(false); + const [timer, setTimer] = useState(null); - const sp = useSearchParams(); - const router = useRouter(); - const pathname = usePathname(); + const sp = useSearchParams(); + const router = useRouter(); + const pathname = usePathname(); const shortcuts = SHORTCUTS[forPage]; - const updateSearchParam = useCallback( - (name: string, value: string) => { - const params = new URLSearchParams(sp.toString()); - params.set(name, value); + const updateSearchParam = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(sp.toString()); + params.set(name, value); - return params.toString(); - }, - [sp], - ); + return params.toString(); + }, + [sp], + ); - useEffect(() => { - const handleKeyDown = ({ key }: KeyboardEvent) => { - if (isModPressed) { - if (timer) clearTimeout(timer); - setModPressed(false); + useEffect(() => { + const handleKeyDown = ({ key }: KeyboardEvent) => { + if (isModPressed) { + if (timer) clearTimeout(timer); + setModPressed(false); - if (key in shortcuts) { - const tab = shortcuts[key]!; - router.push( - `${pathname}?${updateSearchParam("tab", tab)}`, - ); - } - } else { - if (key === "g") { - setModPressed(true); - setTimer(setTimeout(() => setModPressed(false), 5000)); - } - } - }; + if (key in shortcuts) { + const tab = shortcuts[key]!; + router.push( + `${pathname}?${updateSearchParam("tab", tab)}`, + ); + } + } else { + if (key === "g") { + setModPressed(true); + setTimer(setTimeout(() => setModPressed(false), 5000)); + } + } + }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [isModPressed, timer, updateSearchParam, router, pathname]); + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isModPressed, timer, updateSearchParam, router, pathname]); - return null; + return null; } From 957d1b5966ba709600057253259b7af62ef6246d Mon Sep 17 00:00:00 2001 From: Bob Mannino Date: Mon, 11 Aug 2025 19:28:29 +0100 Subject: [PATCH 025/144] add mysql keyboard shortcuts --- apps/dokploy/hooks/use-keyboard-nav.tsx | 3 ++- .../dashboard/project/[projectId]/services/mysql/[mysqlId].tsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 3a99dbc61..7aa9adf6c 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -3,7 +3,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; -const PAGES = ["compose", "application", "postgres", "redis"] as const; +const PAGES = ["compose", "application", "postgres", "redis", "mysql"] as const; type Page = typeof PAGES[number]; type Shortcuts = Record; @@ -57,6 +57,7 @@ const SHORTCUTS: ShortcutsDictionary = { compose: COMPOSE_SHORTCUTS, postgres: POSTGRES_SHORTCUTS, redis: REDIS_SHORTCUTS, + mysql: POSTGRES_SHORTCUTS, }; /** 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 11efc6528..d9fbe07e8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -29,6 +29,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; +import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; @@ -62,6 +63,7 @@ const MySql = ( return (
+ Date: Mon, 11 Aug 2025 18:28:50 +0000 Subject: [PATCH 026/144] [autofix.ci] apply automated fixes --- apps/dokploy/hooks/use-keyboard-nav.tsx | 160 +++++++++--------- .../services/application/[applicationId].tsx | 2 +- .../services/compose/[composeId].tsx | 2 +- .../[projectId]/services/mysql/[mysqlId].tsx | 2 +- .../services/postgres/[postgresId].tsx | 2 +- .../[projectId]/services/redis/[redisId].tsx | 2 +- 6 files changed, 84 insertions(+), 86 deletions(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 7aa9adf6c..be9e206a9 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -4,114 +4,112 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; const PAGES = ["compose", "application", "postgres", "redis", "mysql"] as const; -type Page = typeof PAGES[number]; +type Page = (typeof PAGES)[number]; type Shortcuts = Record; type ShortcutsDictionary = Record; const COMPOSE_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - u: "domains", - d: "deployments", - b: "backups", - s: "schedules", - v: "volumeBackups", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + u: "domains", + d: "deployments", + b: "backups", + s: "schedules", + v: "volumeBackups", + l: "logs", + m: "monitoring", + a: "advanced", }; const APPLICATION_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - u: "domains", - p: "preview-deployments", - s: "schedules", - v: "volume-backups", - d: "deployments", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + u: "domains", + p: "preview-deployments", + s: "schedules", + v: "volume-backups", + d: "deployments", + l: "logs", + m: "monitoring", + a: "advanced", }; const POSTGRES_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - l: "logs", - m: "monitoring", - b: "backups", - a: "advanced", + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + b: "backups", + a: "advanced", }; const REDIS_SHORTCUTS: Shortcuts = { - g: "general", - e: "environment", - l: "logs", - m: "monitoring", - a: "advanced", + g: "general", + e: "environment", + l: "logs", + m: "monitoring", + a: "advanced", }; const SHORTCUTS: ShortcutsDictionary = { - application: APPLICATION_SHORTCUTS, - compose: COMPOSE_SHORTCUTS, - postgres: POSTGRES_SHORTCUTS, - redis: REDIS_SHORTCUTS, - mysql: POSTGRES_SHORTCUTS, + application: APPLICATION_SHORTCUTS, + compose: COMPOSE_SHORTCUTS, + postgres: POSTGRES_SHORTCUTS, + redis: REDIS_SHORTCUTS, + mysql: POSTGRES_SHORTCUTS, }; /** -* Use this to register keyboard shortcuts for different pages. Each shortcut -* must be prefixed with `g` (like GitHub). -* -* @example -* - `g g` "General", -* - `g e` "Environment", -* - `g u` "Domains", -*/ + * Use this to register keyboard shortcuts for different pages. Each shortcut + * must be prefixed with `g` (like GitHub). + * + * @example + * - `g g` "General", + * - `g e` "Environment", + * - `g u` "Domains", + */ export function UseKeyboardNav({ forPage }: { forPage: Page }) { - const [isModPressed, setModPressed] = useState(false); - const [timer, setTimer] = useState(null); + const [isModPressed, setModPressed] = useState(false); + const [timer, setTimer] = useState(null); - const sp = useSearchParams(); - const router = useRouter(); - const pathname = usePathname(); + const sp = useSearchParams(); + const router = useRouter(); + const pathname = usePathname(); - const shortcuts = SHORTCUTS[forPage]; + const shortcuts = SHORTCUTS[forPage]; - const updateSearchParam = useCallback( - (name: string, value: string) => { - const params = new URLSearchParams(sp.toString()); - params.set(name, value); + const updateSearchParam = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(sp.toString()); + params.set(name, value); - return params.toString(); - }, - [sp], - ); + return params.toString(); + }, + [sp], + ); - useEffect(() => { - const handleKeyDown = ({ key }: KeyboardEvent) => { - if (isModPressed) { - if (timer) clearTimeout(timer); - setModPressed(false); + useEffect(() => { + const handleKeyDown = ({ key }: KeyboardEvent) => { + if (isModPressed) { + if (timer) clearTimeout(timer); + setModPressed(false); - if (key in shortcuts) { - const tab = shortcuts[key]!; - router.push( - `${pathname}?${updateSearchParam("tab", tab)}`, - ); - } - } else { - if (key === "g") { - setModPressed(true); - setTimer(setTimeout(() => setModPressed(false), 5000)); - } - } - }; + if (key in shortcuts) { + const tab = shortcuts[key]!; + router.push(`${pathname}?${updateSearchParam("tab", tab)}`); + } + } else { + if (key === "g") { + setModPressed(true); + setTimer(setTimeout(() => setModPressed(false), 5000)); + } + } + }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [isModPressed, timer, updateSearchParam, router, pathname]); + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isModPressed, timer, updateSearchParam, router, pathname]); - return null; + return null; } 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 305a931ce..09766130d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -92,7 +92,7 @@ const Service = ( return (
- + - + - + - + - + Date: Mon, 11 Aug 2025 19:33:25 +0100 Subject: [PATCH 027/144] add mariadb/mongodb keyboard shortcuts --- apps/dokploy/hooks/use-keyboard-nav.tsx | 4 +++- .../project/[projectId]/services/mariadb/[mariadbId].tsx | 2 ++ .../project/[projectId]/services/mongo/[mongoId].tsx | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index be9e206a9..cc9a32496 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -3,7 +3,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; -const PAGES = ["compose", "application", "postgres", "redis", "mysql"] as const; +const PAGES = ["compose", "application", "postgres", "redis", "mysql", "mariadb", "mongodb"] as const; type Page = (typeof PAGES)[number]; type Shortcuts = Record; @@ -58,6 +58,8 @@ const SHORTCUTS: ShortcutsDictionary = { postgres: POSTGRES_SHORTCUTS, redis: REDIS_SHORTCUTS, mysql: POSTGRES_SHORTCUTS, + mariadb: POSTGRES_SHORTCUTS, + mongodb: POSTGRES_SHORTCUTS, }; /** 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 d6745a241..a10bca422 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -29,6 +29,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; +import { UseKeyboardNav } from "@/hooks/use-keyboard-nav"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; @@ -63,6 +64,7 @@ const Mariadb = ( return (
+ + Date: Mon, 11 Aug 2025 18:33:47 +0000 Subject: [PATCH 028/144] [autofix.ci] apply automated fixes --- apps/dokploy/hooks/use-keyboard-nav.tsx | 10 +++++++++- .../[projectId]/services/mariadb/[mariadbId].tsx | 2 +- .../project/[projectId]/services/mongo/[mongoId].tsx | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index cc9a32496..d9dc75bd6 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -3,7 +3,15 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; -const PAGES = ["compose", "application", "postgres", "redis", "mysql", "mariadb", "mongodb"] as const; +const PAGES = [ + "compose", + "application", + "postgres", + "redis", + "mysql", + "mariadb", + "mongodb", +] as const; type Page = (typeof PAGES)[number]; type Shortcuts = Record; 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 a10bca422..ba17aadac 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -64,7 +64,7 @@ const Mariadb = ( return (
- + - + Date: Tue, 12 Aug 2025 18:13:04 +0100 Subject: [PATCH 029/144] fix: do not navigate if typing in input/textarea/select fixes #2367 --- apps/dokploy/hooks/use-keyboard-nav.tsx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index d9dc75bd6..768c47b67 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -100,7 +100,20 @@ export function UseKeyboardNav({ forPage }: { forPage: Page }) { ); useEffect(() => { - const handleKeyDown = ({ key }: KeyboardEvent) => { + const handleKeyDown = ({ key, target }: KeyboardEvent) => { + const active = target as HTMLElement | null; + + if (active) { + const tag = active.tagName; + if ( + active.isContentEditable || + tag === "INPUT" || + tag === "TEXTAREA" || + tag === "SELECT" || + active.getAttribute("role") === "textbox" + ) return; + } + if (isModPressed) { if (timer) clearTimeout(timer); setModPressed(false); @@ -109,11 +122,9 @@ export function UseKeyboardNav({ forPage }: { forPage: Page }) { const tab = shortcuts[key]!; router.push(`${pathname}?${updateSearchParam("tab", tab)}`); } - } else { - if (key === "g") { - setModPressed(true); - setTimer(setTimeout(() => setModPressed(false), 5000)); - } + } else if (key === "g") { + setModPressed(true); + setTimer(setTimeout(() => setModPressed(false), 5000)); } }; From a41137aaccb4c6d395a53e0b799ba349cc88fbe5 Mon Sep 17 00:00:00 2001 From: Bob Mannino Date: Tue, 12 Aug 2025 18:15:35 +0100 Subject: [PATCH 030/144] reduce time waiting for second nav key to 1.5s --- apps/dokploy/hooks/use-keyboard-nav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 768c47b67..1070dd702 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -124,7 +124,7 @@ export function UseKeyboardNav({ forPage }: { forPage: Page }) { } } else if (key === "g") { setModPressed(true); - setTimer(setTimeout(() => setModPressed(false), 5000)); + setTimer(setTimeout(() => setModPressed(false), 1500)); } }; From 83e8c82c4ac4d8367d9faa177bf1557e108a741b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:16:10 +0000 Subject: [PATCH 031/144] [autofix.ci] apply automated fixes --- apps/dokploy/hooks/use-keyboard-nav.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx index 1070dd702..95f38d7e2 100644 --- a/apps/dokploy/hooks/use-keyboard-nav.tsx +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -111,7 +111,8 @@ export function UseKeyboardNav({ forPage }: { forPage: Page }) { tag === "TEXTAREA" || tag === "SELECT" || active.getAttribute("role") === "textbox" - ) return; + ) + return; } if (isModPressed) { From 3864c50deb3f4592b3440726c4b5c93eccf4c7fb Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 13 Aug 2025 08:23:30 +0700 Subject: [PATCH 032/144] bump: Traefik v3.5.0 --- apps/dokploy/setup.ts | 51 +- packages/server/src/setup/traefik-setup.ts | 696 ++++++++++----------- 2 files changed, 362 insertions(+), 385 deletions(-) diff --git a/apps/dokploy/setup.ts b/apps/dokploy/setup.ts index 7abf9fa2d..22dea48d3 100644 --- a/apps/dokploy/setup.ts +++ b/apps/dokploy/setup.ts @@ -1,31 +1,28 @@ -import { execAsync } from "@dokploy/server"; -import { setupDirectories } from "@dokploy/server/setup/config-paths"; -import { initializePostgres } from "@dokploy/server/setup/postgres-setup"; -import { initializeRedis } from "@dokploy/server/setup/redis-setup"; +import { execAsync } from '@dokploy/server'; +import { setupDirectories } from '@dokploy/server/setup/config-paths'; +import { initializePostgres } from '@dokploy/server/setup/postgres-setup'; +import { initializeRedis } from '@dokploy/server/setup/redis-setup'; +import { initializeNetwork, initializeSwarm } from '@dokploy/server/setup/setup'; import { - initializeNetwork, - initializeSwarm, -} from "@dokploy/server/setup/setup"; -import { - createDefaultMiddlewares, - createDefaultServerTraefikConfig, - createDefaultTraefikConfig, - initializeStandaloneTraefik, -} from "@dokploy/server/setup/traefik-setup"; + createDefaultMiddlewares, + createDefaultServerTraefikConfig, + createDefaultTraefikConfig, + initializeStandaloneTraefik, +} from '@dokploy/server/setup/traefik-setup'; (async () => { - try { - setupDirectories(); - createDefaultMiddlewares(); - await initializeSwarm(); - await initializeNetwork(); - createDefaultTraefikConfig(); - createDefaultServerTraefikConfig(); - await execAsync("docker pull traefik:v3.1.2"); - await initializeStandaloneTraefik(); - await initializeRedis(); - await initializePostgres(); - } catch (e) { - console.error("Error in dokploy setup", e); - } + try { + setupDirectories(); + createDefaultMiddlewares(); + await initializeSwarm(); + await initializeNetwork(); + createDefaultTraefikConfig(); + createDefaultServerTraefikConfig(); + await execAsync('docker pull traefik:v3.5.0'); + await initializeStandaloneTraefik(); + await initializeRedis(); + await initializePostgres(); + } catch (e) { + console.error('Error in dokploy setup', e); + } })(); diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index ccdfa30f8..9022aeb12 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -1,402 +1,382 @@ -import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs"; -import path from "node:path"; -import type { ContainerCreateOptions, CreateServiceOptions } from "dockerode"; -import { dump } from "js-yaml"; -import { paths } from "../constants"; -import { getRemoteDocker } from "../utils/servers/remote-docker"; -import type { FileConfig } from "../utils/traefik/file-types"; -import type { MainTraefikConfig } from "../utils/traefik/types"; +import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import type { ContainerCreateOptions, CreateServiceOptions } from 'dockerode'; +import { dump } from 'js-yaml'; +import { paths } from '../constants'; +import { getRemoteDocker } from '../utils/servers/remote-docker'; +import type { FileConfig } from '../utils/traefik/file-types'; +import type { MainTraefikConfig } from '../utils/traefik/types'; -export const TRAEFIK_SSL_PORT = - Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443; -export const TRAEFIK_PORT = - Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; -export const TRAEFIK_HTTP3_PORT = - Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; -export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2"; +export const TRAEFIK_SSL_PORT = Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443; +export const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; +export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; +export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || '3.5.0'; export interface TraefikOptions { - env?: string[]; - serverId?: string; - additionalPorts?: { - targetPort: number; - publishedPort: number; - protocol?: string; - }[]; + env?: string[]; + serverId?: string; + additionalPorts?: { + targetPort: number; + publishedPort: number; + protocol?: string; + }[]; } -export const initializeStandaloneTraefik = async ({ - env, - serverId, - additionalPorts = [], -}: TraefikOptions = {}) => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const imageName = `traefik:v${TRAEFIK_VERSION}`; - const containerName = "dokploy-traefik"; +export const initializeStandaloneTraefik = async ({ env, serverId, additionalPorts = [] }: TraefikOptions = {}) => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); + const imageName = `traefik:v${TRAEFIK_VERSION}`; + const containerName = 'dokploy-traefik'; - const exposedPorts: Record = { - [`${TRAEFIK_PORT}/tcp`]: {}, - [`${TRAEFIK_SSL_PORT}/tcp`]: {}, - [`${TRAEFIK_HTTP3_PORT}/udp`]: {}, - }; + const exposedPorts: Record = { + [`${TRAEFIK_PORT}/tcp`]: {}, + [`${TRAEFIK_SSL_PORT}/tcp`]: {}, + [`${TRAEFIK_HTTP3_PORT}/udp`]: {}, + }; - const portBindings: Record> = { - [`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }], - [`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }], - [`${TRAEFIK_HTTP3_PORT}/udp`]: [ - { HostPort: TRAEFIK_HTTP3_PORT.toString() }, - ], - }; + const portBindings: Record> = { + [`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }], + [`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }], + [`${TRAEFIK_HTTP3_PORT}/udp`]: [{ HostPort: TRAEFIK_HTTP3_PORT.toString() }], + }; - const enableDashboard = additionalPorts.some( - (port) => port.targetPort === 8080, - ); + const enableDashboard = additionalPorts.some((port) => port.targetPort === 8080); - if (enableDashboard) { - exposedPorts["8080/tcp"] = {}; - portBindings["8080/tcp"] = [{ HostPort: "8080" }]; - } + if (enableDashboard) { + exposedPorts['8080/tcp'] = {}; + portBindings['8080/tcp'] = [{ HostPort: '8080' }]; + } - for (const port of additionalPorts) { - const portKey = `${port.targetPort}/${port.protocol ?? "tcp"}`; - exposedPorts[portKey] = {}; - portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }]; - } + for (const port of additionalPorts) { + const portKey = `${port.targetPort}/${port.protocol ?? 'tcp'}`; + exposedPorts[portKey] = {}; + portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }]; + } - const settings: ContainerCreateOptions = { - name: containerName, - Image: imageName, - NetworkingConfig: { - EndpointsConfig: { - "dokploy-network": {}, - }, - }, - ExposedPorts: exposedPorts, - HostConfig: { - RestartPolicy: { - Name: "always", - }, - Binds: [ - `${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`, - `${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`, - "/var/run/docker.sock:/var/run/docker.sock", - ], - PortBindings: portBindings, - }, - Env: env, - }; + const settings: ContainerCreateOptions = { + name: containerName, + Image: imageName, + NetworkingConfig: { + EndpointsConfig: { + 'dokploy-network': {}, + }, + }, + ExposedPorts: exposedPorts, + HostConfig: { + RestartPolicy: { + Name: 'always', + }, + Binds: [ + `${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`, + `${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`, + '/var/run/docker.sock:/var/run/docker.sock', + ], + PortBindings: portBindings, + }, + Env: env, + }; - const docker = await getRemoteDocker(serverId); - try { - const container = docker.getContainer(containerName); - await container.remove({ force: true }); - await new Promise((resolve) => setTimeout(resolve, 5000)); - } catch {} + const docker = await getRemoteDocker(serverId); + try { + const container = docker.getContainer(containerName); + await container.remove({ force: true }); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } catch {} - await docker.createContainer(settings); - const newContainer = docker.getContainer(containerName); - await newContainer.start(); - console.log("Traefik Started ✅"); + await docker.createContainer(settings); + const newContainer = docker.getContainer(containerName); + await newContainer.start(); + console.log('Traefik Started ✅'); }; -export const initializeTraefikService = async ({ - env, - additionalPorts = [], - serverId, -}: TraefikOptions) => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const imageName = `traefik:v${TRAEFIK_VERSION}`; - const appName = "dokploy-traefik"; +export const initializeTraefikService = async ({ env, additionalPorts = [], serverId }: TraefikOptions) => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); + const imageName = `traefik:v${TRAEFIK_VERSION}`; + const appName = 'dokploy-traefik'; - const settings: CreateServiceOptions = { - Name: appName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Env: env, - Mounts: [ - { - Type: "bind", - Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`, - Target: "/etc/traefik/traefik.yml", - }, - { - Type: "bind", - Source: DYNAMIC_TRAEFIK_PATH, - Target: "/etc/dokploy/traefik/dynamic", - }, - { - Type: "bind", - Source: "/var/run/docker.sock", - Target: "/var/run/docker.sock", - }, - ], - }, - Networks: [{ Target: "dokploy-network" }], - Placement: { - Constraints: ["node.role==manager"], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 443, - PublishedPort: TRAEFIK_SSL_PORT, - PublishMode: "host", - Protocol: "tcp", - }, - { - TargetPort: 443, - PublishedPort: TRAEFIK_SSL_PORT, - PublishMode: "host", - Protocol: "udp", - }, - { - TargetPort: 80, - PublishedPort: TRAEFIK_PORT, - PublishMode: "host", - Protocol: "tcp", - }, + const settings: CreateServiceOptions = { + Name: appName, + TaskTemplate: { + ContainerSpec: { + Image: imageName, + Env: env, + Mounts: [ + { + Type: 'bind', + Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`, + Target: '/etc/traefik/traefik.yml', + }, + { + Type: 'bind', + Source: DYNAMIC_TRAEFIK_PATH, + Target: '/etc/dokploy/traefik/dynamic', + }, + { + Type: 'bind', + Source: '/var/run/docker.sock', + Target: '/var/run/docker.sock', + }, + ], + }, + Networks: [{ Target: 'dokploy-network' }], + Placement: { + Constraints: ['node.role==manager'], + }, + }, + Mode: { + Replicated: { + Replicas: 1, + }, + }, + EndpointSpec: { + Ports: [ + { + TargetPort: 443, + PublishedPort: TRAEFIK_SSL_PORT, + PublishMode: 'host', + Protocol: 'tcp', + }, + { + TargetPort: 443, + PublishedPort: TRAEFIK_SSL_PORT, + PublishMode: 'host', + Protocol: 'udp', + }, + { + TargetPort: 80, + PublishedPort: TRAEFIK_PORT, + PublishMode: 'host', + Protocol: 'tcp', + }, - ...additionalPorts.map((port) => ({ - TargetPort: port.targetPort, - PublishedPort: port.publishedPort, - Protocol: port.protocol as "tcp" | "udp" | "sctp" | undefined, - PublishMode: "host" as const, - })), - ], - }, - }; - const docker = await getRemoteDocker(serverId); - try { - const service = docker.getService(appName); - const inspect = await service.inspect(); + ...additionalPorts.map((port) => ({ + TargetPort: port.targetPort, + PublishedPort: port.publishedPort, + Protocol: port.protocol as 'tcp' | 'udp' | 'sctp' | undefined, + PublishMode: 'host' as const, + })), + ], + }, + }; + const docker = await getRemoteDocker(serverId); + try { + const service = docker.getService(appName); + const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - TaskTemplate: { - ...settings.TaskTemplate, - ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, - }, - }); - console.log("Traefik Updated ✅"); - } catch { - await docker.createService(settings); - console.log("Traefik Started ✅"); - } + await service.update({ + version: Number.parseInt(inspect.Version.Index), + ...settings, + TaskTemplate: { + ...settings.TaskTemplate, + ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, + }, + }); + console.log('Traefik Updated ✅'); + } catch { + await docker.createService(settings); + console.log('Traefik Started ✅'); + } }; export const createDefaultServerTraefikConfig = () => { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml"); + const { DYNAMIC_TRAEFIK_PATH } = paths(); + const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, 'dokploy.yml'); - if (existsSync(configFilePath)) { - console.log("Default traefik config already exists"); - return; - } + if (existsSync(configFilePath)) { + console.log('Default traefik config already exists'); + return; + } - const appName = "dokploy"; - const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`; - const config: FileConfig = { - http: { - routers: { - [`${appName}-router-app`]: { - rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`, - service: `${appName}-service-app`, - entryPoints: ["web"], - }, - }, - services: { - [`${appName}-service-app`]: { - loadBalancer: { - servers: [{ url: serviceURLDefault }], - passHostHeader: true, - }, - }, - }, - }, - }; + const appName = 'dokploy'; + const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`; + const config: FileConfig = { + http: { + routers: { + [`${appName}-router-app`]: { + rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`, + service: `${appName}-service-app`, + entryPoints: ['web'], + }, + }, + services: { + [`${appName}-service-app`]: { + loadBalancer: { + servers: [{ url: serviceURLDefault }], + passHostHeader: true, + }, + }, + }, + }, + }; - const yamlStr = dump(config); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync( - path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), - yamlStr, - "utf8", - ); + const yamlStr = dump(config); + mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); + writeFileSync(path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), yamlStr, 'utf8'); }; export const getDefaultTraefikConfig = () => { - const configObject: MainTraefikConfig = { - global: { - sendAnonymousUsage: false, - }, - providers: { - ...(process.env.NODE_ENV === "development" - ? { - docker: { - defaultRule: - "Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)", - }, - } - : { - swarm: { - exposedByDefault: false, - watch: true, - }, - docker: { - exposedByDefault: false, - watch: true, - network: "dokploy-network", - }, - }), - file: { - directory: "/etc/dokploy/traefik/dynamic", - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - http3: { - advertisedPort: TRAEFIK_HTTP3_PORT, - }, - ...(process.env.NODE_ENV === "production" && { - http: { - tls: { - certResolver: "letsencrypt", - }, - }, - }), - }, - }, - api: { - insecure: true, - }, - ...(process.env.NODE_ENV === "production" && { - certificatesResolvers: { - letsencrypt: { - acme: { - email: "test@localhost.com", - storage: "/etc/dokploy/traefik/dynamic/acme.json", - httpChallenge: { - entryPoint: "web", - }, - }, - }, - }, - }), - }; + const configObject: MainTraefikConfig = { + global: { + sendAnonymousUsage: false, + }, + providers: { + ...(process.env.NODE_ENV === 'development' + ? { + docker: { + defaultRule: 'Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)', + }, + } + : { + swarm: { + exposedByDefault: false, + watch: true, + }, + docker: { + exposedByDefault: false, + watch: true, + network: 'dokploy-network', + }, + }), + file: { + directory: '/etc/dokploy/traefik/dynamic', + watch: true, + }, + }, + entryPoints: { + web: { + address: `:${TRAEFIK_PORT}`, + }, + websecure: { + address: `:${TRAEFIK_SSL_PORT}`, + http3: { + advertisedPort: TRAEFIK_HTTP3_PORT, + }, + ...(process.env.NODE_ENV === 'production' && { + http: { + tls: { + certResolver: 'letsencrypt', + }, + }, + }), + }, + }, + api: { + insecure: true, + }, + ...(process.env.NODE_ENV === 'production' && { + certificatesResolvers: { + letsencrypt: { + acme: { + email: 'test@localhost.com', + storage: '/etc/dokploy/traefik/dynamic/acme.json', + httpChallenge: { + entryPoint: 'web', + }, + }, + }, + }, + }), + }; - const yamlStr = dump(configObject); + const yamlStr = dump(configObject); - return yamlStr; + return yamlStr; }; export const getDefaultServerTraefikConfig = () => { - const configObject: MainTraefikConfig = { - providers: { - swarm: { - exposedByDefault: false, - watch: true, - }, - docker: { - exposedByDefault: false, - watch: true, - network: "dokploy-network", - }, - file: { - directory: "/etc/dokploy/traefik/dynamic", - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - http3: { - advertisedPort: TRAEFIK_HTTP3_PORT, - }, - http: { - tls: { - certResolver: "letsencrypt", - }, - }, - }, - }, - api: { - insecure: true, - }, - certificatesResolvers: { - letsencrypt: { - acme: { - email: "test@localhost.com", - storage: "/etc/dokploy/traefik/dynamic/acme.json", - httpChallenge: { - entryPoint: "web", - }, - }, - }, - }, - }; + const configObject: MainTraefikConfig = { + providers: { + swarm: { + exposedByDefault: false, + watch: true, + }, + docker: { + exposedByDefault: false, + watch: true, + network: 'dokploy-network', + }, + file: { + directory: '/etc/dokploy/traefik/dynamic', + watch: true, + }, + }, + entryPoints: { + web: { + address: `:${TRAEFIK_PORT}`, + }, + websecure: { + address: `:${TRAEFIK_SSL_PORT}`, + http3: { + advertisedPort: TRAEFIK_HTTP3_PORT, + }, + http: { + tls: { + certResolver: 'letsencrypt', + }, + }, + }, + }, + api: { + insecure: true, + }, + certificatesResolvers: { + letsencrypt: { + acme: { + email: 'test@localhost.com', + storage: '/etc/dokploy/traefik/dynamic/acme.json', + httpChallenge: { + entryPoint: 'web', + }, + }, + }, + }, + }; - const yamlStr = dump(configObject); + const yamlStr = dump(configObject); - return yamlStr; + return yamlStr; }; export const createDefaultTraefikConfig = () => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); - const mainConfig = path.join(MAIN_TRAEFIK_PATH, "traefik.yml"); - const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, "acme.json"); + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); + const mainConfig = path.join(MAIN_TRAEFIK_PATH, 'traefik.yml'); + const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, 'acme.json'); - if (existsSync(acmeJsonPath)) { - chmodSync(acmeJsonPath, "600"); - } - if (existsSync(mainConfig)) { - console.log("Main config already exists"); - return; - } - const yamlStr = getDefaultTraefikConfig(); - mkdirSync(MAIN_TRAEFIK_PATH, { recursive: true }); - writeFileSync(mainConfig, yamlStr, "utf8"); + if (existsSync(acmeJsonPath)) { + chmodSync(acmeJsonPath, '600'); + } + if (existsSync(mainConfig)) { + console.log('Main config already exists'); + return; + } + const yamlStr = getDefaultTraefikConfig(); + mkdirSync(MAIN_TRAEFIK_PATH, { recursive: true }); + writeFileSync(mainConfig, yamlStr, 'utf8'); }; export const getDefaultMiddlewares = () => { - const defaultMiddlewares = { - http: { - middlewares: { - "redirect-to-https": { - redirectScheme: { - scheme: "https", - permanent: true, - }, - }, - }, - }, - }; - const yamlStr = dump(defaultMiddlewares); - return yamlStr; + const defaultMiddlewares = { + http: { + middlewares: { + 'redirect-to-https': { + redirectScheme: { + scheme: 'https', + permanent: true, + }, + }, + }, + }, + }; + const yamlStr = dump(defaultMiddlewares); + return yamlStr; }; export const createDefaultMiddlewares = () => { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); - if (existsSync(middlewaresPath)) { - console.log("Default middlewares already exists"); - return; - } - const yamlStr = getDefaultMiddlewares(); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync(middlewaresPath, yamlStr, "utf8"); + const { DYNAMIC_TRAEFIK_PATH } = paths(); + const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, 'middlewares.yml'); + if (existsSync(middlewaresPath)) { + console.log('Default middlewares already exists'); + return; + } + const yamlStr = getDefaultMiddlewares(); + mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); + writeFileSync(middlewaresPath, yamlStr, 'utf8'); }; From 2c591cbd03510d24c3f5c9f072bd9a2747b12b49 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:25:30 +0000 Subject: [PATCH 033/144] [autofix.ci] apply automated fixes --- apps/dokploy/setup.ts | 51 +- packages/server/src/setup/traefik-setup.ts | 696 +++++++++++---------- 2 files changed, 385 insertions(+), 362 deletions(-) diff --git a/apps/dokploy/setup.ts b/apps/dokploy/setup.ts index 22dea48d3..13590e4e7 100644 --- a/apps/dokploy/setup.ts +++ b/apps/dokploy/setup.ts @@ -1,28 +1,31 @@ -import { execAsync } from '@dokploy/server'; -import { setupDirectories } from '@dokploy/server/setup/config-paths'; -import { initializePostgres } from '@dokploy/server/setup/postgres-setup'; -import { initializeRedis } from '@dokploy/server/setup/redis-setup'; -import { initializeNetwork, initializeSwarm } from '@dokploy/server/setup/setup'; +import { execAsync } from "@dokploy/server"; +import { setupDirectories } from "@dokploy/server/setup/config-paths"; +import { initializePostgres } from "@dokploy/server/setup/postgres-setup"; +import { initializeRedis } from "@dokploy/server/setup/redis-setup"; import { - createDefaultMiddlewares, - createDefaultServerTraefikConfig, - createDefaultTraefikConfig, - initializeStandaloneTraefik, -} from '@dokploy/server/setup/traefik-setup'; + initializeNetwork, + initializeSwarm, +} from "@dokploy/server/setup/setup"; +import { + createDefaultMiddlewares, + createDefaultServerTraefikConfig, + createDefaultTraefikConfig, + initializeStandaloneTraefik, +} from "@dokploy/server/setup/traefik-setup"; (async () => { - try { - setupDirectories(); - createDefaultMiddlewares(); - await initializeSwarm(); - await initializeNetwork(); - createDefaultTraefikConfig(); - createDefaultServerTraefikConfig(); - await execAsync('docker pull traefik:v3.5.0'); - await initializeStandaloneTraefik(); - await initializeRedis(); - await initializePostgres(); - } catch (e) { - console.error('Error in dokploy setup', e); - } + try { + setupDirectories(); + createDefaultMiddlewares(); + await initializeSwarm(); + await initializeNetwork(); + createDefaultTraefikConfig(); + createDefaultServerTraefikConfig(); + await execAsync("docker pull traefik:v3.5.0"); + await initializeStandaloneTraefik(); + await initializeRedis(); + await initializePostgres(); + } catch (e) { + console.error("Error in dokploy setup", e); + } })(); diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 9022aeb12..17c48d0ff 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -1,382 +1,402 @@ -import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'; -import path from 'node:path'; -import type { ContainerCreateOptions, CreateServiceOptions } from 'dockerode'; -import { dump } from 'js-yaml'; -import { paths } from '../constants'; -import { getRemoteDocker } from '../utils/servers/remote-docker'; -import type { FileConfig } from '../utils/traefik/file-types'; -import type { MainTraefikConfig } from '../utils/traefik/types'; +import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import type { ContainerCreateOptions, CreateServiceOptions } from "dockerode"; +import { dump } from "js-yaml"; +import { paths } from "../constants"; +import { getRemoteDocker } from "../utils/servers/remote-docker"; +import type { FileConfig } from "../utils/traefik/file-types"; +import type { MainTraefikConfig } from "../utils/traefik/types"; -export const TRAEFIK_SSL_PORT = Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443; -export const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; -export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; -export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || '3.5.0'; +export const TRAEFIK_SSL_PORT = + Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443; +export const TRAEFIK_PORT = + Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; +export const TRAEFIK_HTTP3_PORT = + Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; +export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0"; export interface TraefikOptions { - env?: string[]; - serverId?: string; - additionalPorts?: { - targetPort: number; - publishedPort: number; - protocol?: string; - }[]; + env?: string[]; + serverId?: string; + additionalPorts?: { + targetPort: number; + publishedPort: number; + protocol?: string; + }[]; } -export const initializeStandaloneTraefik = async ({ env, serverId, additionalPorts = [] }: TraefikOptions = {}) => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const imageName = `traefik:v${TRAEFIK_VERSION}`; - const containerName = 'dokploy-traefik'; +export const initializeStandaloneTraefik = async ({ + env, + serverId, + additionalPorts = [], +}: TraefikOptions = {}) => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); + const imageName = `traefik:v${TRAEFIK_VERSION}`; + const containerName = "dokploy-traefik"; - const exposedPorts: Record = { - [`${TRAEFIK_PORT}/tcp`]: {}, - [`${TRAEFIK_SSL_PORT}/tcp`]: {}, - [`${TRAEFIK_HTTP3_PORT}/udp`]: {}, - }; + const exposedPorts: Record = { + [`${TRAEFIK_PORT}/tcp`]: {}, + [`${TRAEFIK_SSL_PORT}/tcp`]: {}, + [`${TRAEFIK_HTTP3_PORT}/udp`]: {}, + }; - const portBindings: Record> = { - [`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }], - [`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }], - [`${TRAEFIK_HTTP3_PORT}/udp`]: [{ HostPort: TRAEFIK_HTTP3_PORT.toString() }], - }; + const portBindings: Record> = { + [`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }], + [`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }], + [`${TRAEFIK_HTTP3_PORT}/udp`]: [ + { HostPort: TRAEFIK_HTTP3_PORT.toString() }, + ], + }; - const enableDashboard = additionalPorts.some((port) => port.targetPort === 8080); + const enableDashboard = additionalPorts.some( + (port) => port.targetPort === 8080, + ); - if (enableDashboard) { - exposedPorts['8080/tcp'] = {}; - portBindings['8080/tcp'] = [{ HostPort: '8080' }]; - } + if (enableDashboard) { + exposedPorts["8080/tcp"] = {}; + portBindings["8080/tcp"] = [{ HostPort: "8080" }]; + } - for (const port of additionalPorts) { - const portKey = `${port.targetPort}/${port.protocol ?? 'tcp'}`; - exposedPorts[portKey] = {}; - portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }]; - } + for (const port of additionalPorts) { + const portKey = `${port.targetPort}/${port.protocol ?? "tcp"}`; + exposedPorts[portKey] = {}; + portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }]; + } - const settings: ContainerCreateOptions = { - name: containerName, - Image: imageName, - NetworkingConfig: { - EndpointsConfig: { - 'dokploy-network': {}, - }, - }, - ExposedPorts: exposedPorts, - HostConfig: { - RestartPolicy: { - Name: 'always', - }, - Binds: [ - `${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`, - `${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`, - '/var/run/docker.sock:/var/run/docker.sock', - ], - PortBindings: portBindings, - }, - Env: env, - }; + const settings: ContainerCreateOptions = { + name: containerName, + Image: imageName, + NetworkingConfig: { + EndpointsConfig: { + "dokploy-network": {}, + }, + }, + ExposedPorts: exposedPorts, + HostConfig: { + RestartPolicy: { + Name: "always", + }, + Binds: [ + `${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`, + `${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`, + "/var/run/docker.sock:/var/run/docker.sock", + ], + PortBindings: portBindings, + }, + Env: env, + }; - const docker = await getRemoteDocker(serverId); - try { - const container = docker.getContainer(containerName); - await container.remove({ force: true }); - await new Promise((resolve) => setTimeout(resolve, 5000)); - } catch {} + const docker = await getRemoteDocker(serverId); + try { + const container = docker.getContainer(containerName); + await container.remove({ force: true }); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } catch {} - await docker.createContainer(settings); - const newContainer = docker.getContainer(containerName); - await newContainer.start(); - console.log('Traefik Started ✅'); + await docker.createContainer(settings); + const newContainer = docker.getContainer(containerName); + await newContainer.start(); + console.log("Traefik Started ✅"); }; -export const initializeTraefikService = async ({ env, additionalPorts = [], serverId }: TraefikOptions) => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const imageName = `traefik:v${TRAEFIK_VERSION}`; - const appName = 'dokploy-traefik'; +export const initializeTraefikService = async ({ + env, + additionalPorts = [], + serverId, +}: TraefikOptions) => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); + const imageName = `traefik:v${TRAEFIK_VERSION}`; + const appName = "dokploy-traefik"; - const settings: CreateServiceOptions = { - Name: appName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Env: env, - Mounts: [ - { - Type: 'bind', - Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`, - Target: '/etc/traefik/traefik.yml', - }, - { - Type: 'bind', - Source: DYNAMIC_TRAEFIK_PATH, - Target: '/etc/dokploy/traefik/dynamic', - }, - { - Type: 'bind', - Source: '/var/run/docker.sock', - Target: '/var/run/docker.sock', - }, - ], - }, - Networks: [{ Target: 'dokploy-network' }], - Placement: { - Constraints: ['node.role==manager'], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 443, - PublishedPort: TRAEFIK_SSL_PORT, - PublishMode: 'host', - Protocol: 'tcp', - }, - { - TargetPort: 443, - PublishedPort: TRAEFIK_SSL_PORT, - PublishMode: 'host', - Protocol: 'udp', - }, - { - TargetPort: 80, - PublishedPort: TRAEFIK_PORT, - PublishMode: 'host', - Protocol: 'tcp', - }, + const settings: CreateServiceOptions = { + Name: appName, + TaskTemplate: { + ContainerSpec: { + Image: imageName, + Env: env, + Mounts: [ + { + Type: "bind", + Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`, + Target: "/etc/traefik/traefik.yml", + }, + { + Type: "bind", + Source: DYNAMIC_TRAEFIK_PATH, + Target: "/etc/dokploy/traefik/dynamic", + }, + { + Type: "bind", + Source: "/var/run/docker.sock", + Target: "/var/run/docker.sock", + }, + ], + }, + Networks: [{ Target: "dokploy-network" }], + Placement: { + Constraints: ["node.role==manager"], + }, + }, + Mode: { + Replicated: { + Replicas: 1, + }, + }, + EndpointSpec: { + Ports: [ + { + TargetPort: 443, + PublishedPort: TRAEFIK_SSL_PORT, + PublishMode: "host", + Protocol: "tcp", + }, + { + TargetPort: 443, + PublishedPort: TRAEFIK_SSL_PORT, + PublishMode: "host", + Protocol: "udp", + }, + { + TargetPort: 80, + PublishedPort: TRAEFIK_PORT, + PublishMode: "host", + Protocol: "tcp", + }, - ...additionalPorts.map((port) => ({ - TargetPort: port.targetPort, - PublishedPort: port.publishedPort, - Protocol: port.protocol as 'tcp' | 'udp' | 'sctp' | undefined, - PublishMode: 'host' as const, - })), - ], - }, - }; - const docker = await getRemoteDocker(serverId); - try { - const service = docker.getService(appName); - const inspect = await service.inspect(); + ...additionalPorts.map((port) => ({ + TargetPort: port.targetPort, + PublishedPort: port.publishedPort, + Protocol: port.protocol as "tcp" | "udp" | "sctp" | undefined, + PublishMode: "host" as const, + })), + ], + }, + }; + const docker = await getRemoteDocker(serverId); + try { + const service = docker.getService(appName); + const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - TaskTemplate: { - ...settings.TaskTemplate, - ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, - }, - }); - console.log('Traefik Updated ✅'); - } catch { - await docker.createService(settings); - console.log('Traefik Started ✅'); - } + await service.update({ + version: Number.parseInt(inspect.Version.Index), + ...settings, + TaskTemplate: { + ...settings.TaskTemplate, + ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, + }, + }); + console.log("Traefik Updated ✅"); + } catch { + await docker.createService(settings); + console.log("Traefik Started ✅"); + } }; export const createDefaultServerTraefikConfig = () => { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, 'dokploy.yml'); + const { DYNAMIC_TRAEFIK_PATH } = paths(); + const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml"); - if (existsSync(configFilePath)) { - console.log('Default traefik config already exists'); - return; - } + if (existsSync(configFilePath)) { + console.log("Default traefik config already exists"); + return; + } - const appName = 'dokploy'; - const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`; - const config: FileConfig = { - http: { - routers: { - [`${appName}-router-app`]: { - rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`, - service: `${appName}-service-app`, - entryPoints: ['web'], - }, - }, - services: { - [`${appName}-service-app`]: { - loadBalancer: { - servers: [{ url: serviceURLDefault }], - passHostHeader: true, - }, - }, - }, - }, - }; + const appName = "dokploy"; + const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`; + const config: FileConfig = { + http: { + routers: { + [`${appName}-router-app`]: { + rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`, + service: `${appName}-service-app`, + entryPoints: ["web"], + }, + }, + services: { + [`${appName}-service-app`]: { + loadBalancer: { + servers: [{ url: serviceURLDefault }], + passHostHeader: true, + }, + }, + }, + }, + }; - const yamlStr = dump(config); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync(path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), yamlStr, 'utf8'); + const yamlStr = dump(config); + mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); + writeFileSync( + path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), + yamlStr, + "utf8", + ); }; export const getDefaultTraefikConfig = () => { - const configObject: MainTraefikConfig = { - global: { - sendAnonymousUsage: false, - }, - providers: { - ...(process.env.NODE_ENV === 'development' - ? { - docker: { - defaultRule: 'Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)', - }, - } - : { - swarm: { - exposedByDefault: false, - watch: true, - }, - docker: { - exposedByDefault: false, - watch: true, - network: 'dokploy-network', - }, - }), - file: { - directory: '/etc/dokploy/traefik/dynamic', - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - http3: { - advertisedPort: TRAEFIK_HTTP3_PORT, - }, - ...(process.env.NODE_ENV === 'production' && { - http: { - tls: { - certResolver: 'letsencrypt', - }, - }, - }), - }, - }, - api: { - insecure: true, - }, - ...(process.env.NODE_ENV === 'production' && { - certificatesResolvers: { - letsencrypt: { - acme: { - email: 'test@localhost.com', - storage: '/etc/dokploy/traefik/dynamic/acme.json', - httpChallenge: { - entryPoint: 'web', - }, - }, - }, - }, - }), - }; + const configObject: MainTraefikConfig = { + global: { + sendAnonymousUsage: false, + }, + providers: { + ...(process.env.NODE_ENV === "development" + ? { + docker: { + defaultRule: + "Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)", + }, + } + : { + swarm: { + exposedByDefault: false, + watch: true, + }, + docker: { + exposedByDefault: false, + watch: true, + network: "dokploy-network", + }, + }), + file: { + directory: "/etc/dokploy/traefik/dynamic", + watch: true, + }, + }, + entryPoints: { + web: { + address: `:${TRAEFIK_PORT}`, + }, + websecure: { + address: `:${TRAEFIK_SSL_PORT}`, + http3: { + advertisedPort: TRAEFIK_HTTP3_PORT, + }, + ...(process.env.NODE_ENV === "production" && { + http: { + tls: { + certResolver: "letsencrypt", + }, + }, + }), + }, + }, + api: { + insecure: true, + }, + ...(process.env.NODE_ENV === "production" && { + certificatesResolvers: { + letsencrypt: { + acme: { + email: "test@localhost.com", + storage: "/etc/dokploy/traefik/dynamic/acme.json", + httpChallenge: { + entryPoint: "web", + }, + }, + }, + }, + }), + }; - const yamlStr = dump(configObject); + const yamlStr = dump(configObject); - return yamlStr; + return yamlStr; }; export const getDefaultServerTraefikConfig = () => { - const configObject: MainTraefikConfig = { - providers: { - swarm: { - exposedByDefault: false, - watch: true, - }, - docker: { - exposedByDefault: false, - watch: true, - network: 'dokploy-network', - }, - file: { - directory: '/etc/dokploy/traefik/dynamic', - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - http3: { - advertisedPort: TRAEFIK_HTTP3_PORT, - }, - http: { - tls: { - certResolver: 'letsencrypt', - }, - }, - }, - }, - api: { - insecure: true, - }, - certificatesResolvers: { - letsencrypt: { - acme: { - email: 'test@localhost.com', - storage: '/etc/dokploy/traefik/dynamic/acme.json', - httpChallenge: { - entryPoint: 'web', - }, - }, - }, - }, - }; + const configObject: MainTraefikConfig = { + providers: { + swarm: { + exposedByDefault: false, + watch: true, + }, + docker: { + exposedByDefault: false, + watch: true, + network: "dokploy-network", + }, + file: { + directory: "/etc/dokploy/traefik/dynamic", + watch: true, + }, + }, + entryPoints: { + web: { + address: `:${TRAEFIK_PORT}`, + }, + websecure: { + address: `:${TRAEFIK_SSL_PORT}`, + http3: { + advertisedPort: TRAEFIK_HTTP3_PORT, + }, + http: { + tls: { + certResolver: "letsencrypt", + }, + }, + }, + }, + api: { + insecure: true, + }, + certificatesResolvers: { + letsencrypt: { + acme: { + email: "test@localhost.com", + storage: "/etc/dokploy/traefik/dynamic/acme.json", + httpChallenge: { + entryPoint: "web", + }, + }, + }, + }, + }; - const yamlStr = dump(configObject); + const yamlStr = dump(configObject); - return yamlStr; + return yamlStr; }; export const createDefaultTraefikConfig = () => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); - const mainConfig = path.join(MAIN_TRAEFIK_PATH, 'traefik.yml'); - const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, 'acme.json'); + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); + const mainConfig = path.join(MAIN_TRAEFIK_PATH, "traefik.yml"); + const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, "acme.json"); - if (existsSync(acmeJsonPath)) { - chmodSync(acmeJsonPath, '600'); - } - if (existsSync(mainConfig)) { - console.log('Main config already exists'); - return; - } - const yamlStr = getDefaultTraefikConfig(); - mkdirSync(MAIN_TRAEFIK_PATH, { recursive: true }); - writeFileSync(mainConfig, yamlStr, 'utf8'); + if (existsSync(acmeJsonPath)) { + chmodSync(acmeJsonPath, "600"); + } + if (existsSync(mainConfig)) { + console.log("Main config already exists"); + return; + } + const yamlStr = getDefaultTraefikConfig(); + mkdirSync(MAIN_TRAEFIK_PATH, { recursive: true }); + writeFileSync(mainConfig, yamlStr, "utf8"); }; export const getDefaultMiddlewares = () => { - const defaultMiddlewares = { - http: { - middlewares: { - 'redirect-to-https': { - redirectScheme: { - scheme: 'https', - permanent: true, - }, - }, - }, - }, - }; - const yamlStr = dump(defaultMiddlewares); - return yamlStr; + const defaultMiddlewares = { + http: { + middlewares: { + "redirect-to-https": { + redirectScheme: { + scheme: "https", + permanent: true, + }, + }, + }, + }, + }; + const yamlStr = dump(defaultMiddlewares); + return yamlStr; }; export const createDefaultMiddlewares = () => { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, 'middlewares.yml'); - if (existsSync(middlewaresPath)) { - console.log('Default middlewares already exists'); - return; - } - const yamlStr = getDefaultMiddlewares(); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync(middlewaresPath, yamlStr, 'utf8'); + const { DYNAMIC_TRAEFIK_PATH } = paths(); + const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); + if (existsSync(middlewaresPath)) { + console.log("Default middlewares already exists"); + return; + } + const yamlStr = getDefaultMiddlewares(); + mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); + writeFileSync(middlewaresPath, yamlStr, "utf8"); }; From 6248abd88e8cb5d60d643651b758874610472f57 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 13 Aug 2025 10:05:16 +0700 Subject: [PATCH 034/144] fix(ui): pointer events transparent modal overlay --- apps/dokploy/components/ui/dialog.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/ui/dialog.tsx b/apps/dokploy/components/ui/dialog.tsx index f3b6fedbd..ae92d3797 100644 --- a/apps/dokploy/components/ui/dialog.tsx +++ b/apps/dokploy/components/ui/dialog.tsx @@ -37,7 +37,7 @@ const DialogOverlay = React.forwardRef< {/* Custom overlay for modal=false - no click handler to avoid Command conflicts */} -
+
Date: Wed, 13 Aug 2025 10:07:08 +0700 Subject: [PATCH 035/144] refactor: remove overflow hidden cause of useless --- apps/dokploy/components/ui/dialog.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dokploy/components/ui/dialog.tsx b/apps/dokploy/components/ui/dialog.tsx index ae92d3797..9fef5b60f 100644 --- a/apps/dokploy/components/ui/dialog.tsx +++ b/apps/dokploy/components/ui/dialog.tsx @@ -61,7 +61,6 @@ const DialogContent = React.forwardRef< const originalPaddingRight = body.style.paddingRight; const originalOverflow = body.style.overflow; - body.style.overflow = "hidden"; if (scrollbarWidth > 0) { body.style.paddingRight = `${scrollbarWidth}px`; } From 6e069154ef4ecfd1ebe86f7bc2757ed8fe222604 Mon Sep 17 00:00:00 2001 From: Vyacheslav Scherbinin Date: Wed, 13 Aug 2025 12:51:44 +0700 Subject: [PATCH 036/144] fix(dialog-ux): prevent default Radix double event onInteractOutside --- apps/dokploy/components/ui/dialog.tsx | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/dokploy/components/ui/dialog.tsx b/apps/dokploy/components/ui/dialog.tsx index 9fef5b60f..64e2717e9 100644 --- a/apps/dokploy/components/ui/dialog.tsx +++ b/apps/dokploy/components/ui/dialog.tsx @@ -73,8 +73,21 @@ const DialogContent = React.forwardRef< // Handle outside interactions properly with Command components const handleInteractOutside = React.useCallback( - (_e: Event) => { + (event: Event | React.MouseEvent) => { + // Don't close when clicking inside popovers, dropdowns, or command components + const target = event.target as HTMLElement; + if ( + target.closest("[data-radix-popper-content-wrapper]") || + target.closest("[cmdk-root]") || + target.closest("[data-radix-command-root]") + ) { + event.preventDefault(); + return; + } + if (onOpenChange) { + event.preventDefault(); + event.stopPropagation(); onOpenChange(false); } }, @@ -95,7 +108,10 @@ const DialogContent = React.forwardRef< return ( {/* Custom overlay for modal=false - no click handler to avoid Command conflicts */} -
+
{ - const target = e.target as HTMLElement; - // Don't close when clicking inside popovers, dropdowns, or command components - if ( - target.closest("[data-radix-popper-content-wrapper]") || - target.closest("[cmdk-root]") || - target.closest("[data-radix-command-root]") - ) { - e.preventDefault(); - return; - } - // Use our custom handler for modal=false behavior - handleInteractOutside(e); - }} + onInteractOutside={(event) => event.preventDefault()} {...props} >
Date: Wed, 13 Aug 2025 12:54:58 +0700 Subject: [PATCH 037/144] fix(dialog-ux): internal component state control --- apps/dokploy/components/ui/dialog.tsx | 32 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/components/ui/dialog.tsx b/apps/dokploy/components/ui/dialog.tsx index 64e2717e9..7be34e9b2 100644 --- a/apps/dokploy/components/ui/dialog.tsx +++ b/apps/dokploy/components/ui/dialog.tsx @@ -12,16 +12,28 @@ const Dialog = ({ onOpenChange, open, ...props -}: React.ComponentPropsWithoutRef) => ( - - - -); +}: React.ComponentPropsWithoutRef) => { + const [isOpened, setIsOpened] = React.useState(false); // for internal control + + const handleOpenChange = (open: boolean) => { + if (onOpenChange) { + onOpenChange(open); + } else { + setIsOpened(open); + } + }; + + return ( + + + + ); +}; Dialog.displayName = DialogPrimitive.Root.displayName; const DialogTrigger = DialogPrimitive.Trigger; From f3ec01ec771bc664e43ef9b68ee1d9ae03399309 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 06:46:05 +0000 Subject: [PATCH 038/144] [autofix.ci] apply automated fixes --- apps/dokploy/components/ui/dialog.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/components/ui/dialog.tsx b/apps/dokploy/components/ui/dialog.tsx index 7be34e9b2..0d71d059c 100644 --- a/apps/dokploy/components/ui/dialog.tsx +++ b/apps/dokploy/components/ui/dialog.tsx @@ -24,7 +24,9 @@ const Dialog = ({ }; return ( - + Date: Fri, 15 Aug 2025 15:13:24 +0000 Subject: [PATCH 039/144] trim the ip address --- .../components/dashboard/settings/servers/handle-servers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx index 1ff2f09dc..cdbe8a95b 100644 --- a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx @@ -112,7 +112,7 @@ export const HandleServers = ({ serverId }: Props) => { await mutateAsync({ name: data.name, description: data.description || "", - ipAddress: data.ipAddress || "", + ipAddress: data.ipAddress?.trim() || "", port: data.port || 22, username: data.username || "root", sshKeyId: data.sshKeyId || "", From b6cbf9127d50f8305c96ac352377d2de241f20dd Mon Sep 17 00:00:00 2001 From: Haouari haitam Kouider <57036855+haouarihk@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:14:26 +0000 Subject: [PATCH 040/144] trim ip address --- .../dashboard/settings/servers/welcome-stripe/create-server.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx index b895f385b..0141aca08 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx @@ -91,7 +91,7 @@ export const CreateServer = ({ stepper }: Props) => { await mutateAsync({ name: data.name, description: data.description || "", - ipAddress: data.ipAddress || "", + ipAddress: data.ipAddress?.trim() || "", port: data.port || 22, username: data.username || "root", sshKeyId: data.sshKeyId || "", From 4e7378371d067b9774967ce526511e80e516a9c0 Mon Sep 17 00:00:00 2001 From: Leonhard Breuer Date: Fri, 15 Aug 2025 19:52:55 +0200 Subject: [PATCH 041/144] fix(template): make every key in map unique using index FIXES #2382 --- apps/dokploy/components/dashboard/project/add-template.tsx | 4 ++-- 1 file 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 5eb994fc4..2bdd85a01 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -307,9 +307,9 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => { : "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6", )} > - {templates?.map((template) => ( + {templates?.map((template, idx) => (
Date: Sun, 17 Aug 2025 01:33:47 +0000 Subject: [PATCH 042/144] [autofix.ci] apply automated fixes --- .../pages/api/providers/gitlab/callback.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/pages/api/providers/gitlab/callback.ts b/apps/dokploy/pages/api/providers/gitlab/callback.ts index cb51da9d2..008873511 100644 --- a/apps/dokploy/pages/api/providers/gitlab/callback.ts +++ b/apps/dokploy/pages/api/providers/gitlab/callback.ts @@ -12,11 +12,11 @@ export default async function handler( } const gitlab = await findGitlabById(gitlabId as string); - const gitlabUrl = new URL(gitlab.gitlabUrl) + const gitlabUrl = new URL(gitlab.gitlabUrl); const headers: HeadersInit = { - "Content-Type": "application/x-www-form-urlencoded" - } + "Content-Type": "application/x-www-form-urlencoded", + }; // In case of basic auth being present in the URL, we need to remove it from the URL // and add it to the Authorization header. @@ -24,13 +24,14 @@ export default async function handler( headers.Authorization = `Basic ${Buffer.from(`${gitlabUrl.username}:${gitlabUrl.password}`).toString("base64")}`; } - const url = (gitlabUrl.username && gitlabUrl.password) - ? new URL(gitlabUrl, { - ...gitlabUrl, - username: "", - password: "", - }).toString() - : gitlabUrl.toString(); + const url = + gitlabUrl.username && gitlabUrl.password + ? new URL(gitlabUrl, { + ...gitlabUrl, + username: "", + password: "", + }).toString() + : gitlabUrl.toString(); const response = await fetch(`${url}/oauth/token`, { method: "POST", From 0cdacb5501eeb2314ac05c80d86b0f8e3dc4da39 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 16 Aug 2025 19:50:15 -0600 Subject: [PATCH 043/144] update(package): bump version to v0.24.11 --- 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 2758bbced..2308efce3 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.10", + "version": "v0.24.11", "private": true, "license": "Apache-2.0", "type": "module", From 774365c68edefdc1a3a0cb46b783378b9ccdc428 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 16 Aug 2025 20:18:08 -0600 Subject: [PATCH 044/144] Refactor and update various components in the Dokploy application, enhancing functionality and fixing minor issues across multiple pages and features, including dashboard, settings, and API integrations. --- .../advanced/general/add-command.tsx | 11 +- .../advanced/import/show-import.tsx | 12 +- .../application/advanced/ports/show-port.tsx | 5 +- .../advanced/redirects/handle-redirect.tsx | 12 +- .../advanced/redirects/show-redirects.tsx | 4 +- .../advanced/security/handle-security.tsx | 12 +- .../advanced/security/show-security.tsx | 8 +- .../application/advanced/show-resources.tsx | 12 +- .../advanced/traefik/show-traefik-config.tsx | 3 +- .../traefik/update-traefik-config.tsx | 12 +- .../advanced/volumes/add-volumes.tsx | 15 +- .../advanced/volumes/update-volume.tsx | 12 +- .../application/deployments/cancel-queues.tsx | 4 +- .../application/deployments/refresh-token.tsx | 4 +- .../deployments/show-deployment.tsx | 4 +- .../deployments/show-deployments-modal.tsx | 3 +- .../deployments/show-deployments.tsx | 8 +- .../application/domains/dns-helper-modal.tsx | 4 +- .../application/domains/show-domains.tsx | 30 ++-- .../environment/show-enviroment.tsx | 12 +- .../application/environment/show.tsx | 10 +- .../generic/save-bitbucket-provider.tsx | 14 +- .../general/generic/save-docker-provider.tsx | 10 +- .../general/generic/save-drag-n-drop.tsx | 10 +- .../general/generic/save-git-provider.tsx | 21 ++- .../general/generic/save-gitea-provider.tsx | 14 +- .../general/generic/save-github-provider.tsx | 14 +- .../general/generic/save-gitlab-provider.tsx | 14 +- .../application/general/generic/show.tsx | 10 +- .../generic/unauthorized-git-provider.tsx | 4 +- .../dashboard/application/general/show.tsx | 23 +-- .../dashboard/application/logs/show.tsx | 6 +- .../add-preview-domain.tsx | 15 +- .../show-preview-deployments.tsx | 20 +-- .../show-preview-settings.tsx | 12 +- .../rollbacks/show-rollback-settings.tsx | 10 +- .../schedules/handle-schedules.tsx | 24 +-- .../application/schedules/show-schedules.tsx | 18 +-- .../application/update-application.tsx | 12 +- .../volume-backups/handle-volume-backups.tsx | 24 +-- .../volume-backups/restore-volume-backups.tsx | 16 +- .../volume-backups/show-volume-backups.tsx | 16 +- .../compose/advanced/add-command.tsx | 11 +- .../dashboard/compose/delete-service.tsx | 18 +-- .../dashboard/compose/general/actions.tsx | 8 +- .../compose/general/compose-file-editor.tsx | 2 +- .../save-bitbucket-provider-compose.tsx | 14 +- .../generic/save-git-provider-compose.tsx | 16 +- .../generic/save-gitea-provider-compose.tsx | 14 +- .../generic/save-github-provider-compose.tsx | 14 +- .../generic/save-gitlab-provider-compose.tsx | 14 +- .../compose/general/generic/show.tsx | 10 +- .../compose/general/randomize-compose.tsx | 12 +- .../dashboard/compose/general/show.tsx | 1 + .../dashboard/compose/logs/show-stack.tsx | 6 +- .../dashboard/compose/logs/show.tsx | 6 +- .../dashboard/compose/update-compose.tsx | 12 +- .../database/backups/handle-backup.tsx | 27 ++-- .../database/backups/restore-backup.tsx | 30 ++-- .../database/backups/show-backups.tsx | 20 +-- .../docker/logs/line-count-filter.tsx | 8 +- .../docker/logs/show-docker-modal-logs.tsx | 4 +- .../logs/show-docker-modal-stack-logs.tsx | 4 +- .../docker/logs/since-logs-filter.tsx | 2 +- .../docker/logs/status-logs-filter.tsx | 4 +- .../dashboard/docker/logs/terminal-line.tsx | 6 +- .../dashboard/docker/show/colums.tsx | 4 +- .../dashboard/docker/show/show-containers.tsx | 28 ++-- .../docker/terminal/docker-terminal-modal.tsx | 4 +- .../docker/terminal/docker-terminal.tsx | 2 +- .../file-system/show-traefik-file.tsx | 15 +- .../file-system/show-traefik-system.tsx | 4 +- .../impersonation/impersonation-bar.tsx | 38 ++--- .../show-external-mariadb-credentials.tsx | 12 +- .../mariadb/general/show-general-mariadb.tsx | 8 +- .../dashboard/mariadb/update-mariadb.tsx | 12 +- .../show-external-mongo-credentials.tsx | 12 +- .../mongo/general/show-general-mongo.tsx | 9 +- .../dashboard/mongo/update-mongo.tsx | 12 +- .../free/container/docker-memory-chart.tsx | 1 + .../free/container/docker-network-chart.tsx | 1 + .../show-free-compose-monitoring.tsx | 6 +- .../show-free-container-monitoring.tsx | 2 +- .../paid/container/container-block-chart.tsx | 2 +- .../paid/container/container-cpu-chart.tsx | 2 +- .../paid/container/container-memory-chart.tsx | 2 +- .../container/container-network-chart.tsx | 2 +- .../show-paid-compose-monitoring.tsx | 6 +- .../show-paid-container-monitoring.tsx | 4 +- .../monitoring/paid/servers/cpu-chart.tsx | 2 +- .../monitoring/paid/servers/memory-chart.tsx | 2 +- .../monitoring/paid/servers/network-chart.tsx | 2 +- .../paid/servers/show-paid-monitoring.tsx | 4 +- .../show-external-mysql-credentials.tsx | 12 +- .../mysql/general/show-general-mysql.tsx | 9 +- .../dashboard/mysql/update-mysql.tsx | 12 +- .../organization/handle-organization.tsx | 12 +- .../postgres/advanced/show-custom-command.tsx | 10 +- .../show-external-postgres-credentials.tsx | 12 +- .../general/show-general-postgres.tsx | 8 +- .../dashboard/postgres/update-postgres.tsx | 12 +- .../dashboard/project/add-template.tsx | 2 +- .../dashboard/project/ai/step-three.tsx | 2 +- .../dashboard/project/ai/step-two.tsx | 8 +- .../project/ai/template-generator.tsx | 10 +- .../dashboard/project/duplicate-project.tsx | 8 +- .../dashboard/projects/handle-project.tsx | 15 +- .../projects/project-environment.tsx | 12 +- .../show-external-redis-credentials.tsx | 12 +- .../redis/general/show-general-redis.tsx | 8 +- .../dashboard/redis/update-redis.tsx | 12 +- .../components/dashboard/requests/columns.tsx | 4 +- .../requests/request-distribution-chart.tsx | 14 +- .../dashboard/requests/requests-table.tsx | 48 +++--- .../dashboard/requests/show-requests.tsx | 22 +-- .../requests/status-request-filter.tsx | 2 +- .../components/dashboard/search-command.tsx | 8 +- .../components/dashboard/settings/ai-form.tsx | 4 +- .../dashboard/settings/api/add-api-key.tsx | 12 +- .../dashboard/settings/api/show-api-keys.tsx | 8 +- .../settings/billing/show-billing.tsx | 24 +-- .../settings/billing/show-welcome-dokploy.tsx | 2 +- .../settings/certificates/add-certificate.tsx | 12 +- .../certificates/show-certificates.tsx | 4 +- .../settings/cluster/nodes/add-node.tsx | 4 +- .../cluster/nodes/manager/add-manager.tsx | 6 +- .../cluster/nodes/show-nodes-modal.tsx | 2 +- .../settings/cluster/nodes/show-nodes.tsx | 16 +- .../cluster/nodes/workers/add-worker.tsx | 6 +- .../cluster/registry/handle-registry.tsx | 12 +- .../cluster/registry/show-registry.tsx | 4 +- .../destination/handle-destinations.tsx | 12 +- .../destination/show-destinations.tsx | 4 +- .../git/bitbucket/add-bitbucket-provider.tsx | 16 +- .../git/bitbucket/edit-bitbucket-provider.tsx | 12 +- .../settings/git/gitea/add-gitea-provider.tsx | 14 +- .../git/gitea/edit-gitea-provider.tsx | 14 +- .../git/github/add-github-provider.tsx | 4 +- .../git/github/edit-github-provider.tsx | 12 +- .../git/gitlab/add-gitlab-provider.tsx | 15 +- .../git/gitlab/edit-gitlab-provider.tsx | 12 +- .../settings/git/show-git-providers.tsx | 36 ++--- .../dashboard/settings/handle-ai.tsx | 12 +- .../notifications/handle-notifications.tsx | 24 +-- .../notifications/show-notifications.tsx | 4 +- .../settings/profile/disable-2fa.tsx | 10 +- .../dashboard/settings/profile/enable-2fa.tsx | 14 +- .../settings/profile/profile-form.tsx | 16 +- .../servers/actions/show-dokploy-actions.tsx | 7 +- .../servers/actions/show-server-actions.tsx | 3 +- .../servers/actions/show-storage-actions.tsx | 5 +- .../servers/actions/toggle-docker-cleanup.tsx | 2 +- .../settings/servers/edit-script.tsx | 12 +- .../settings/servers/gpu-support-modal.tsx | 2 +- .../settings/servers/gpu-support.tsx | 6 +- .../settings/servers/security-audit.tsx | 4 +- .../settings/servers/setup-monitoring.tsx | 12 +- .../settings/servers/setup-server.tsx | 10 +- .../servers/show-docker-containers-modal.tsx | 2 +- .../servers/show-monitoring-modal.tsx | 2 +- .../settings/servers/show-schedules-modal.tsx | 2 +- .../servers/show-swarm-overview-modal.tsx | 2 +- .../show-traefik-file-system-modal.tsx | 2 +- .../settings/servers/validate-server.tsx | 4 +- .../settings/servers/welcome-stripe/setup.tsx | 2 +- .../servers/welcome-stripe/verify.tsx | 9 +- .../welcome-stripe/welcome-suscription.tsx | 23 ++- .../settings/ssh-keys/handle-ssh-keys.tsx | 12 +- .../settings/ssh-keys/show-ssh-keys.tsx | 6 +- .../settings/users/add-invitation.tsx | 12 +- .../settings/users/add-permissions.tsx | 10 +- .../settings/users/show-invitations.tsx | 9 +- .../dashboard/settings/users/show-users.tsx | 7 +- .../dashboard/settings/web-domain.tsx | 14 +- .../dashboard/settings/web-server.tsx | 4 +- .../web-server/docker-terminal-modal.tsx | 8 +- .../settings/web-server/edit-traefik-env.tsx | 10 +- .../web-server/local-server-config.tsx | 10 +- .../settings/web-server/show-modal-logs.tsx | 8 +- .../settings/web-server/terminal-modal.tsx | 6 +- .../web-server/toggle-auto-check-updates.tsx | 2 +- .../settings/web-server/update-server-ip.tsx | 12 +- .../settings/web-server/update-server.tsx | 26 +-- .../settings/web-server/update-webserver.tsx | 6 +- .../dashboard/shared/rebuild-database.tsx | 4 +- .../dashboard/swarm/applications/columns.tsx | 4 +- .../swarm/applications/data-table.tsx | 11 +- .../dashboard/swarm/details/details-card.tsx | 2 +- .../swarm/details/show-node-config.tsx | 2 +- .../dashboard/swarm/monitoring-card.tsx | 16 +- apps/dokploy/components/ui/alert.tsx | 2 +- apps/dokploy/components/ui/badge.tsx | 2 +- apps/dokploy/components/ui/button.tsx | 7 +- apps/dokploy/components/ui/dialog.tsx | 2 +- apps/dokploy/components/ui/dropzone.tsx | 4 +- apps/dokploy/components/ui/file-tree.tsx | 2 +- apps/dokploy/components/ui/input.tsx | 2 +- apps/dokploy/components/ui/label.tsx | 2 +- apps/dokploy/components/ui/modeToggle.tsx | 2 +- apps/dokploy/components/ui/secrets.tsx | 6 +- apps/dokploy/components/ui/sheet.tsx | 2 +- apps/dokploy/components/ui/sidebar.tsx | 2 +- apps/dokploy/components/ui/toggle.tsx | 2 +- apps/dokploy/esbuild.config.ts | 3 +- apps/dokploy/lib/auth-client.ts | 10 +- apps/dokploy/pages/_app.tsx | 12 +- apps/dokploy/pages/_error.tsx | 4 +- .../accept-invitation/[accept-invitation].tsx | 2 +- apps/dokploy/pages/api/[...trpc].ts | 4 +- .../pages/api/deploy/[refreshToken].ts | 6 +- .../api/deploy/compose/[refreshToken].ts | 8 +- apps/dokploy/pages/api/deploy/github.ts | 12 +- .../pages/api/providers/gitea/callback.ts | 2 +- .../pages/api/providers/github/setup.ts | 4 +- apps/dokploy/pages/api/stripe/webhook.ts | 6 +- apps/dokploy/pages/api/trpc/[trpc].ts | 4 +- apps/dokploy/pages/dashboard/docker.tsx | 6 +- apps/dokploy/pages/dashboard/monitoring.tsx | 10 +- .../pages/dashboard/project/[projectId].tsx | 59 ++++--- .../services/mariadb/[mariadbId].tsx | 24 +-- .../[projectId]/services/mongo/[mongoId].tsx | 24 +-- .../[projectId]/services/mysql/[mysqlId].tsx | 24 +-- .../services/postgres/[postgresId].tsx | 24 +-- .../[projectId]/services/redis/[redisId].tsx | 24 +-- apps/dokploy/pages/dashboard/projects.tsx | 8 +- apps/dokploy/pages/dashboard/requests.tsx | 4 +- apps/dokploy/pages/dashboard/schedules.tsx | 9 +- apps/dokploy/pages/dashboard/settings/ai.tsx | 8 +- .../pages/dashboard/settings/billing.tsx | 7 +- .../pages/dashboard/settings/certificates.tsx | 8 +- .../pages/dashboard/settings/cluster.tsx | 7 +- .../pages/dashboard/settings/destinations.tsx | 7 +- .../dashboard/settings/git-providers.tsx | 7 +- .../dashboard/settings/notifications.tsx | 7 +- .../pages/dashboard/settings/profile.tsx | 13 +- .../pages/dashboard/settings/registry.tsx | 7 +- .../pages/dashboard/settings/server.tsx | 11 +- .../pages/dashboard/settings/servers.tsx | 9 +- .../pages/dashboard/settings/ssh-keys.tsx | 7 +- .../pages/dashboard/settings/users.tsx | 9 +- apps/dokploy/pages/dashboard/swarm.tsx | 6 +- apps/dokploy/pages/dashboard/traefik.tsx | 6 +- apps/dokploy/pages/index.tsx | 22 +-- apps/dokploy/pages/invitation.tsx | 18 +-- apps/dokploy/pages/register.tsx | 20 +-- apps/dokploy/pages/reset-password.tsx | 18 +-- apps/dokploy/pages/send-reset-password.tsx | 18 +-- apps/dokploy/pages/swagger.tsx | 4 +- apps/dokploy/reset-password.ts | 3 +- apps/dokploy/server/api/routers/admin.ts | 4 +- apps/dokploy/server/api/routers/ai.ts | 16 +- apps/dokploy/server/api/routers/backup.ts | 21 ++- apps/dokploy/server/api/routers/bitbucket.ts | 18 +-- .../dokploy/server/api/routers/certificate.ts | 19 ++- apps/dokploy/server/api/routers/cluster.ts | 2 +- apps/dokploy/server/api/routers/compose.ts | 32 ++-- apps/dokploy/server/api/routers/deployment.ts | 16 +- .../dokploy/server/api/routers/destination.ts | 22 +-- apps/dokploy/server/api/routers/domain.ts | 16 +- .../server/api/routers/git-provider.ts | 6 +- apps/dokploy/server/api/routers/gitea.ts | 22 ++- apps/dokploy/server/api/routers/github.ts | 14 +- apps/dokploy/server/api/routers/gitlab.ts | 21 ++- apps/dokploy/server/api/routers/mariadb.ts | 46 +++--- apps/dokploy/server/api/routers/mongo.ts | 46 +++--- apps/dokploy/server/api/routers/mount.ts | 12 +- apps/dokploy/server/api/routers/mysql.ts | 50 +++--- .../server/api/routers/notification.ts | 48 +++--- .../server/api/routers/organization.ts | 4 +- apps/dokploy/server/api/routers/port.ts | 12 +- apps/dokploy/server/api/routers/postgres.ts | 46 +++--- .../server/api/routers/preview-deployment.ts | 2 +- apps/dokploy/server/api/routers/project.ts | 37 +++-- apps/dokploy/server/api/routers/redirects.ts | 10 +- apps/dokploy/server/api/routers/redis.ts | 49 +++--- apps/dokploy/server/api/routers/registry.ts | 22 +-- apps/dokploy/server/api/routers/rollbacks.ts | 4 +- apps/dokploy/server/api/routers/schedule.ts | 5 +- apps/dokploy/server/api/routers/security.ts | 10 +- apps/dokploy/server/api/routers/server.ts | 42 ++--- apps/dokploy/server/api/routers/ssh-key.ts | 18 +-- apps/dokploy/server/api/routers/stripe.ts | 4 +- apps/dokploy/server/api/routers/user.ts | 4 +- .../server/api/routers/volume-backups.ts | 4 +- apps/dokploy/server/api/trpc.ts | 7 +- apps/dokploy/server/db/index.ts | 2 +- apps/dokploy/server/server.ts | 6 +- apps/dokploy/server/wss/terminal.ts | 2 +- apps/dokploy/utils/api.ts | 3 +- apps/dokploy/utils/hooks/use-locale.ts | 2 +- apps/dokploy/utils/i18n.ts | 2 +- biome.json | 9 +- packages/server/esbuild.config.ts | 1 + packages/server/src/db/index.ts | 3 +- packages/server/src/db/schema/compose.ts | 3 +- packages/server/src/db/schema/index.ts | 56 +++---- packages/server/src/index.ts | 151 ++++++++---------- packages/server/src/services/deployment.ts | 9 +- packages/server/src/services/gitea.ts | 2 +- packages/server/src/services/github.ts | 2 +- packages/server/src/services/gitlab.ts | 2 +- packages/server/src/services/mariadb.ts | 5 +- packages/server/src/services/mongo.ts | 5 +- packages/server/src/services/mysql.ts | 11 +- packages/server/src/services/postgres.ts | 5 +- packages/server/src/services/redis.ts | 10 +- packages/server/src/services/schedule.ts | 2 +- packages/server/src/types/with.ts | 1 + .../server/src/utils/access-log/handler.ts | 2 +- packages/server/src/utils/backups/utils.ts | 2 +- .../server/src/utils/builders/docker-file.ts | 2 +- packages/server/src/utils/builders/heroku.ts | 2 +- .../server/src/utils/builders/nixpacks.ts | 4 +- packages/server/src/utils/builders/paketo.ts | 2 +- packages/server/src/utils/builders/static.ts | 2 +- .../server/src/utils/databases/rebuild.ts | 3 +- .../src/utils/docker/compose/service.ts | 1 + packages/server/src/utils/gpu-setup.ts | 5 +- packages/server/src/utils/providers/git.ts | 6 +- packages/server/src/utils/providers/github.ts | 9 +- packages/server/src/utils/restore/index.ts | 6 +- packages/server/src/utils/schedules/utils.ts | 4 +- .../server/src/utils/traefik/application.ts | 3 +- .../server/src/utils/volume-backups/index.ts | 1 + .../server/src/utils/volume-backups/utils.ts | 2 +- 325 files changed, 1743 insertions(+), 1768 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx b/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx index 50e36ad76..1bf69394a 100644 --- a/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Card, @@ -16,11 +21,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; + interface Props { applicationId: string; } diff --git a/apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx b/apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx index 29033f6b6..17d033cf2 100644 --- a/apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { Code2, Globe2, HardDrive } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; @@ -27,12 +33,6 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Code2, Globe2, HardDrive } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const ImportSchema = z.object({ base64: z.string(), diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx index 7d79b268a..816949f2b 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx @@ -1,3 +1,5 @@ +import { Rss, Trash2 } from "lucide-react"; +import { toast } from "sonner"; import { AlertBlock } from "@/components/shared/alert-block"; import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; @@ -9,9 +11,8 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { Rss, Trash2 } from "lucide-react"; -import { toast } from "sonner"; import { HandlePorts } from "./handle-ports"; + interface Props { applicationId: string; } diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx index 253a8c24d..c4d38ef18 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { PenBoxIcon, PlusIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -30,12 +36,6 @@ import { import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon, PlusIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const AddRedirectchema = z.object({ regex: z.string().min(1, "Regex required"), diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx index 5c2c5943c..f1b14bfc0 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx @@ -1,3 +1,5 @@ +import { Split, Trash2 } from "lucide-react"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { @@ -8,8 +10,6 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { Split, Trash2 } from "lucide-react"; -import { toast } from "sonner"; import { HandleRedirect } from "./handle-redirect"; interface Props { diff --git a/apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx b/apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx index 2fc50c7c7..c52976eb1 100644 --- a/apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { PenBoxIcon, PlusIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -19,12 +25,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon, PlusIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const AddSecuritychema = z.object({ username: z.string().min(1, "Username is required"), diff --git a/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx b/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx index 552a186a1..5676e6f00 100644 --- a/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx @@ -1,4 +1,7 @@ +import { LockKeyhole, Trash2 } from "lucide-react"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; +import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { Button } from "@/components/ui/button"; import { Card, @@ -7,12 +10,9 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Label } from "@/components/ui/label"; -import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import { LockKeyhole, Trash2 } from "lucide-react"; -import { toast } from "sonner"; import { HandleSecurity } from "./handle-security"; interface Props { diff --git a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx index 3d26716fc..25040067b 100644 --- a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { InfoIcon } from "lucide-react"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -23,12 +29,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { InfoIcon } from "lucide-react"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const addResourcesSchema = z.object({ memoryReservation: z.string().optional(), diff --git a/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx b/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx index 58601fb49..ae23f1866 100644 --- a/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx @@ -1,3 +1,4 @@ +import { File, Loader2 } from "lucide-react"; import { CodeEditor } from "@/components/shared/code-editor"; import { Card, @@ -7,8 +8,8 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { File, Loader2 } from "lucide-react"; import { UpdateTraefikConfig } from "./update-traefik-config"; + interface Props { applicationId: string; } diff --git a/apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx b/apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx index c73ed5b3d..73512837f 100644 --- a/apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import jsyaml from "js-yaml"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; @@ -19,12 +25,6 @@ import { FormMessage, } from "@/components/ui/form"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import jsyaml from "js-yaml"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const UpdateTraefikConfigSchema = z.object({ traefikConfig: z.string(), diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx index 84c864e3a..00be8a1e1 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { PlusIcon } from "lucide-react"; +import type React from "react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; @@ -22,13 +29,7 @@ import { Label } from "@/components/ui/label"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PlusIcon } from "lucide-react"; -import type React from "react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; + interface Props { serviceId: string; serviceType: diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx index 2a692f100..38d02ec90 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { PenBoxIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; @@ -20,12 +26,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const mountSchema = z.object({ mountPath: z.string().min(1, "Mount path required"), diff --git a/apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx b/apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx index eb85f383b..e957a496c 100644 --- a/apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx @@ -1,3 +1,5 @@ +import { Paintbrush } from "lucide-react"; +import { toast } from "sonner"; import { AlertDialog, AlertDialogAction, @@ -11,8 +13,6 @@ import { } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { api } from "@/utils/api"; -import { Paintbrush } from "lucide-react"; -import { toast } from "sonner"; interface Props { id: string; diff --git a/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx b/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx index abfe37c3c..c49331ed7 100644 --- a/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx @@ -1,3 +1,5 @@ +import { RefreshCcw } from "lucide-react"; +import { toast } from "sonner"; import { AlertDialog, AlertDialogAction, @@ -10,8 +12,6 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { api } from "@/utils/api"; -import { RefreshCcw } from "lucide-react"; -import { toast } from "sonner"; interface Props { id: string; diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index 8733c745b..69c697721 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -1,3 +1,5 @@ +import { Loader2 } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { @@ -7,8 +9,6 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { Loader2 } from "lucide-react"; -import { useEffect, useRef, useState } from "react"; import { TerminalLine } from "../../docker/logs/terminal-line"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx index 4631a066e..7c937883e 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx @@ -1,8 +1,7 @@ +import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; - import type { RouterOutputs } from "@/utils/api"; -import { useState } from "react"; import { ShowDeployment } from "../deployments/show-deployment"; import { ShowDeployments } from "./show-deployments"; diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index 5790c129c..5efa9e5f2 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -1,3 +1,6 @@ +import { Clock, Loader2, RefreshCcw, RocketIcon, Settings } from "lucide-react"; +import React, { useEffect, useState } from "react"; +import { toast } from "sonner"; import { DateTooltip } from "@/components/shared/date-tooltip"; import { DialogAction } from "@/components/shared/dialog-action"; import { StatusTooltip } from "@/components/shared/status-tooltip"; @@ -10,10 +13,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { type RouterOutputs, api } from "@/utils/api"; -import { Clock, Loader2, RefreshCcw, RocketIcon, Settings } from "lucide-react"; -import React, { useEffect, useState } from "react"; -import { toast } from "sonner"; +import { api, type RouterOutputs } from "@/utils/api"; import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings"; import { CancelQueues } from "./cancel-queues"; import { RefreshToken } from "./refresh-token"; diff --git a/apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx b/apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx index c67c2fbfc..768ece858 100644 --- a/apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx +++ b/apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx @@ -1,3 +1,5 @@ +import { Copy, HelpCircle, Server } from "lucide-react"; +import { toast } from "sonner"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -8,8 +10,6 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { Copy, HelpCircle, Server } from "lucide-react"; -import { toast } from "sonner"; interface Props { domain: { diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index 7bb58dfbe..1fd3d82e9 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -1,3 +1,18 @@ +import { + CheckCircle2, + ExternalLink, + GlobeIcon, + InfoIcon, + Loader2, + PenBoxIcon, + RefreshCw, + Server, + Trash2, + XCircle, +} from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -15,21 +30,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { - CheckCircle2, - ExternalLink, - GlobeIcon, - InfoIcon, - Loader2, - PenBoxIcon, - RefreshCw, - Server, - Trash2, - XCircle, -} from "lucide-react"; -import Link from "next/link"; -import { useState } from "react"; -import { toast } from "sonner"; import { DnsHelperModal } from "./dns-helper-modal"; import { AddDomain } from "./handle-domain"; diff --git a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx index 8a78c2745..4a5d0270b 100644 --- a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { EyeIcon, EyeOffIcon } from "lucide-react"; +import { type CSSProperties, useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; import { @@ -16,12 +22,6 @@ import { } from "@/components/ui/form"; import { Toggle } from "@/components/ui/toggle"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { EyeIcon, EyeOffIcon } from "lucide-react"; -import { type CSSProperties, useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import type { ServiceType } from "../advanced/show-resources"; const addEnvironmentSchema = z.object({ diff --git a/apps/dokploy/components/dashboard/application/environment/show.tsx b/apps/dokploy/components/dashboard/application/environment/show.tsx index 6f504959c..78edb1aaa 100644 --- a/apps/dokploy/components/dashboard/application/environment/show.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show.tsx @@ -1,13 +1,13 @@ -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { Form } from "@/components/ui/form"; -import { Secrets } from "@/components/ui/secrets"; -import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Form } from "@/components/ui/form"; +import { Secrets } from "@/components/ui/secrets"; +import { api } from "@/utils/api"; const addEnvironmentSchema = z.object({ env: z.string(), diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx index befc85957..6f6db5dd1 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; +import Link from "next/link"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { BitbucketIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -40,13 +47,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; -import Link from "next/link"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const BitbucketProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx index 72b2578c5..fcdcf0a93 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Form, @@ -9,11 +14,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const DockerProviderSchema = z.object({ dockerImage: z.string().min(1, { diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx index 3732860d4..00e18c2ab 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { TrashIcon } from "lucide-react"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Dropzone } from "@/components/ui/dropzone"; import { @@ -11,11 +16,6 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { type UploadFile, uploadFileSchema } from "@/utils/schema"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { TrashIcon } from "lucide-react"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; interface Props { applicationId: string; diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx index f3e8116e6..61690e740 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx @@ -1,3 +1,13 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { KeyRoundIcon, LockIcon, X } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { GitIcon } from "@/components/icons/data-tools-icons"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Form, @@ -25,17 +35,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { KeyRoundIcon, LockIcon, X } from "lucide-react"; -import Link from "next/link"; -import { useRouter } from "next/router"; - -import { GitIcon } from "@/components/icons/data-tools-icons"; -import { Badge } from "@/components/ui/badge"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const GitProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx index 55fbfebda..2198f4a97 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; +import Link from "next/link"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { GiteaIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -40,13 +47,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; -import Link from "next/link"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; interface GiteaRepository { name: string; 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 c76b9ae58..9a4b92ce1 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 @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; +import Link from "next/link"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { GithubIcon } from "@/components/icons/data-tools-icons"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -39,13 +46,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; -import Link from "next/link"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const GithubProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), 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 2995e45f3..cb7209f8a 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 @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useMemo } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { GitlabIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; @@ -40,13 +47,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; -import Link from "next/link"; -import { useEffect, useMemo } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const GitlabProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), diff --git a/apps/dokploy/components/dashboard/application/general/generic/show.tsx b/apps/dokploy/components/dashboard/application/general/generic/show.tsx index 786c79e5c..a60db800c 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/show.tsx @@ -1,3 +1,7 @@ +import { GitBranch, Loader2, UploadCloud } from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; +import { toast } from "sonner"; import { SaveDockerProvider } from "@/components/dashboard/application/general/generic/save-docker-provider"; import { SaveGitProvider } from "@/components/dashboard/application/general/generic/save-git-provider"; import { SaveGiteaProvider } from "@/components/dashboard/application/general/generic/save-gitea-provider"; @@ -5,18 +9,14 @@ import { SaveGithubProvider } from "@/components/dashboard/application/general/g import { BitbucketIcon, DockerIcon, - GitIcon, GiteaIcon, GithubIcon, + GitIcon, GitlabIcon, } from "@/components/icons/data-tools-icons"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { api } from "@/utils/api"; -import { GitBranch, Loader2, UploadCloud } from "lucide-react"; -import Link from "next/link"; -import { useState } from "react"; -import { toast } from "sonner"; import { SaveBitbucketProvider } from "./save-bitbucket-provider"; import { SaveDragNDrop } from "./save-drag-n-drop"; import { SaveGitlabProvider } from "./save-gitlab-provider"; diff --git a/apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx index 4dbdf7a69..de3fbff06 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx @@ -1,8 +1,9 @@ +import { AlertCircle, GitBranch, Unlink } from "lucide-react"; import { BitbucketIcon, - GitIcon, GiteaIcon, GithubIcon, + GitIcon, GitlabIcon, } from "@/components/icons/data-tools-icons"; import { DialogAction } from "@/components/shared/dialog-action"; @@ -10,7 +11,6 @@ import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import type { RouterOutputs } from "@/utils/api"; -import { AlertCircle, GitBranch, Unlink } from "lucide-react"; interface Props { service: diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index c917d7ab7..a8fef349b 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -1,3 +1,14 @@ +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { + Ban, + CheckCircle2, + Hammer, + RefreshCcw, + Rocket, + Terminal, +} from "lucide-react"; +import { useRouter } from "next/router"; +import { toast } from "sonner"; import { ShowBuildChooseForm } from "@/components/dashboard/application/build/show"; import { ShowProviderForm } from "@/components/dashboard/application/general/generic/show"; import { DialogAction } from "@/components/shared/dialog-action"; @@ -11,18 +22,8 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { - Ban, - CheckCircle2, - Hammer, - RefreshCcw, - Rocket, - Terminal, -} from "lucide-react"; -import { useRouter } from "next/router"; -import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; + interface Props { applicationId: string; } diff --git a/apps/dokploy/components/dashboard/application/logs/show.tsx b/apps/dokploy/components/dashboard/application/logs/show.tsx index a73b99d25..e5dff075e 100644 --- a/apps/dokploy/components/dashboard/application/logs/show.tsx +++ b/apps/dokploy/components/dashboard/application/logs/show.tsx @@ -1,3 +1,6 @@ +import { Loader2 } from "lucide-react"; +import dynamic from "next/dynamic"; +import { useEffect, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Card, @@ -18,9 +21,6 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { Loader2 } from "lucide-react"; -import dynamic from "next/dynamic"; -import { useEffect, useState } from "react"; export const DockerLogs = dynamic( () => import("@/components/dashboard/docker/logs/docker-logs-id").then( diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx index bb6f0e0a7..eac4559f1 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { Dices } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import type z from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -33,15 +39,8 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import { api } from "@/utils/api"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; - import { domain } from "@/server/db/validations/domain"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Dices } from "lucide-react"; -import type z from "zod"; +import { api } from "@/utils/api"; type Domain = z.infer; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx index bf93af718..d93bbd1c8 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx @@ -1,3 +1,13 @@ +import { + ExternalLink, + FileText, + GitPullRequest, + Loader2, + PenSquare, + RocketIcon, + Trash2, +} from "lucide-react"; +import { toast } from "sonner"; import { GithubIcon } from "@/components/icons/data-tools-icons"; import { DateTooltip } from "@/components/shared/date-tooltip"; import { DialogAction } from "@/components/shared/dialog-action"; @@ -13,16 +23,6 @@ import { } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { - ExternalLink, - FileText, - GitPullRequest, - Loader2, - PenSquare, - RocketIcon, - Trash2, -} from "lucide-react"; -import { toast } from "sonner"; import { ShowModalLogs } from "../../settings/web-server/show-modal-logs"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; import { AddPreviewDomain } from "./add-preview-domain"; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index a0f6ae0e4..3605480a0 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { Settings2 } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -28,12 +34,6 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Settings2 } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const schema = z .object({ diff --git a/apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx b/apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx index 77575ea03..2fc7c0522 100644 --- a/apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx +++ b/apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -18,11 +23,6 @@ import { } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const formSchema = z.object({ rollbackActive: z.boolean(), diff --git a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx index 077c289b8..e403c8be5 100644 --- a/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx @@ -1,3 +1,15 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { + DatabaseZap, + Info, + PenBoxIcon, + PlusCircle, + RefreshCw, +} from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; @@ -35,18 +47,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - DatabaseZap, - Info, - PenBoxIcon, - PlusCircle, - RefreshCw, -} from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import type { CacheType } from "../domains/handle-domain"; export const commonCronExpressions = [ diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index 2f2ebc85a..3ebc76def 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -1,3 +1,12 @@ +import { + ClipboardList, + Clock, + Loader2, + Play, + Terminal, + Trash2, +} from "lucide-react"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -15,15 +24,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { - ClipboardList, - Clock, - Loader2, - Play, - Terminal, - Trash2, -} from "lucide-react"; -import { toast } from "sonner"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; import { HandleSchedules } from "./handle-schedules"; diff --git a/apps/dokploy/components/dashboard/application/update-application.tsx b/apps/dokploy/components/dashboard/application/update-application.tsx index 4d4190fa2..754074d75 100644 --- a/apps/dokploy/components/dashboard/application/update-application.tsx +++ b/apps/dokploy/components/dashboard/application/update-application.tsx @@ -1,3 +1,9 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { PenBoxIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -20,12 +26,6 @@ import { import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const updateApplicationSchema = z.object({ name: z.string().min(1, { diff --git a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx index c66b05850..29f8f6e15 100644 --- a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx +++ b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx @@ -1,3 +1,15 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { + DatabaseZap, + Info, + PenBoxIcon, + PlusCircle, + RefreshCw, +} from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -34,18 +46,6 @@ import { } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - DatabaseZap, - Info, - PenBoxIcon, - PlusCircle, - RefreshCw, -} from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import type { CacheType } from "../domains/handle-domain"; import { commonCronExpressions } from "../schedules/handle-schedules"; diff --git a/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx index b3baeb38f..6eda33648 100644 --- a/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx +++ b/apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx @@ -1,3 +1,11 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import copy from "copy-to-clipboard"; +import { debounce } from "lodash"; +import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { DrawerLogs } from "@/components/shared/drawer-logs"; import { Badge } from "@/components/ui/badge"; @@ -35,14 +43,6 @@ import { import { ScrollArea } from "@/components/ui/scroll-area"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import copy from "copy-to-clipboard"; -import { debounce } from "lodash"; -import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import { formatBytes } from "../../database/backups/restore-backup"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; diff --git a/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx index 6d7895ee7..c88dd92f5 100644 --- a/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx +++ b/apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx @@ -1,3 +1,11 @@ +import { + ClipboardList, + DatabaseBackup, + Loader2, + Play, + Trash2, +} from "lucide-react"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -15,14 +23,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { - ClipboardList, - DatabaseBackup, - Loader2, - Play, - Trash2, -} from "lucide-react"; -import { toast } from "sonner"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; import { HandleVolumeBackups } from "./handle-volume-backups"; import { RestoreVolumeBackups } from "./restore-volume-backups"; diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx index c5a34b3c1..52eb18907 100644 --- a/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx +++ b/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx @@ -1,3 +1,8 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -18,11 +23,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; + interface Props { composeId: string; } diff --git a/apps/dokploy/components/dashboard/compose/delete-service.tsx b/apps/dokploy/components/dashboard/compose/delete-service.tsx index 65689afd1..cae27c264 100644 --- a/apps/dokploy/components/dashboard/compose/delete-service.tsx +++ b/apps/dokploy/components/dashboard/compose/delete-service.tsx @@ -1,3 +1,12 @@ +import type { ServiceType } from "@dokploy/server/db/schema"; +import { zodResolver } from "@hookform/resolvers/zod"; +import copy from "copy-to-clipboard"; +import { Copy, Trash2 } from "lucide-react"; +import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; @@ -20,15 +29,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; -import type { ServiceType } from "@dokploy/server/db/schema"; -import { zodResolver } from "@hookform/resolvers/zod"; -import copy from "copy-to-clipboard"; -import { Copy, Trash2 } from "lucide-react"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; const deleteComposeSchema = z.object({ projectName: z.string().min(1, { diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index 0a4433e7c..29a9f9be3 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -1,3 +1,7 @@ +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; +import { useRouter } from "next/router"; +import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; @@ -8,10 +12,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; -import { useRouter } from "next/router"; -import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index c2db472d2..afb66bd54 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -141,7 +141,7 @@ services:
-
+
+ + + + +
From ba1f4dbd3a76c0079de2ea28417a5dc243e2f577 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 23 Aug 2025 16:04:13 -0600 Subject: [PATCH 068/144] feat(ui): add bulk deploy functionality for services in project dashboard --- .../pages/dashboard/project/[projectId].tsx | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index a32bae06f..65f7d064d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -10,6 +10,7 @@ import { FolderInput, GlobeIcon, Loader2, + Play, PlusIcon, Search, ServerIcon, @@ -312,6 +313,7 @@ const Project = ( stop: api.compose.stop.useMutation(), move: api.compose.move.useMutation(), delete: api.compose.delete.useMutation(), + deploy: api.compose.deploy.useMutation(), }; const applicationActions = { @@ -319,6 +321,7 @@ const Project = ( stop: api.application.stop.useMutation(), move: api.application.move.useMutation(), delete: api.application.delete.useMutation(), + deploy: api.application.deploy.useMutation(), }; const postgresActions = { @@ -326,6 +329,7 @@ const Project = ( stop: api.postgres.stop.useMutation(), move: api.postgres.move.useMutation(), delete: api.postgres.remove.useMutation(), + deploy: api.postgres.deploy.useMutation(), }; const mysqlActions = { @@ -333,6 +337,7 @@ const Project = ( stop: api.mysql.stop.useMutation(), move: api.mysql.move.useMutation(), delete: api.mysql.remove.useMutation(), + deploy: api.mysql.deploy.useMutation(), }; const mariadbActions = { @@ -340,6 +345,7 @@ const Project = ( stop: api.mariadb.stop.useMutation(), move: api.mariadb.move.useMutation(), delete: api.mariadb.remove.useMutation(), + deploy: api.mariadb.deploy.useMutation(), }; const redisActions = { @@ -347,6 +353,7 @@ const Project = ( stop: api.redis.stop.useMutation(), move: api.redis.move.useMutation(), delete: api.redis.remove.useMutation(), + deploy: api.redis.deploy.useMutation(), }; const mongoActions = { @@ -354,6 +361,7 @@ const Project = ( stop: api.mongo.stop.useMutation(), move: api.mongo.move.useMutation(), delete: api.mongo.remove.useMutation(), + deploy: api.mongo.deploy.useMutation(), }; const handleBulkStart = async () => { @@ -586,6 +594,83 @@ const Project = ( setIsBulkActionLoading(false); }; + const handleBulkDeploy = async () => { + let success = 0; + let failed = 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.deploy.mutateAsync({ + applicationId: serviceId, + }); + break; + case "compose": + await composeActions.deploy.mutateAsync({ + composeId: serviceId, + }); + + break; + case "postgres": + await postgresActions.deploy.mutateAsync({ + postgresId: serviceId, + }); + + break; + case "mysql": + await mysqlActions.deploy.mutateAsync({ + mysqlId: serviceId, + }); + + break; + case "mariadb": + await mariadbActions.deploy.mutateAsync({ + mariadbId: serviceId, + }); + + break; + case "redis": + await redisActions.deploy.mutateAsync({ + redisId: serviceId, + }); + + break; + case "mongo": + await mongoActions.deploy.mutateAsync({ + mongoId: serviceId, + }); + + break; + } + success++; + } catch (error) { + failed++; + toast.error( + `Error deploying service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + } + if (success > 0) { + toast.success( + `${success} service${success !== 1 ? "s" : ""} deployed successfully`, + ); + } + if (failed > 0) { + toast.error( + `${failed} service${failed !== 1 ? "s" : ""} failed to deploy`, + ); + } + + setSelectedServices([]); + setIsDropdownOpen(false); + setIsBulkActionLoading(false); + }; + const filteredServices = useMemo(() => { if (!applications) return []; const filtered = applications.filter( @@ -729,6 +814,24 @@ const Project = ( Start + + + Date: Sat, 23 Aug 2025 16:06:25 -0600 Subject: [PATCH 069/144] feat(ui): implement bulk delete dialog for services in project dashboard --- .../pages/dashboard/project/[projectId].tsx | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 65f7d064d..3182f316a 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -290,6 +290,8 @@ const Project = ( const [openCombobox, setOpenCombobox] = useState(false); const [selectedServices, setSelectedServices] = useState([]); const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); + const [deleteVolumes, setDeleteVolumes] = useState(false); const handleSelectAll = () => { if (selectedServices.length === filteredServices.length) { @@ -532,7 +534,7 @@ const Project = ( setIsBulkActionLoading(false); }; - const handleBulkDelete = async () => { + const handleBulkDelete = async (deleteVolumes = false) => { let success = 0; setIsBulkActionLoading(true); for (const serviceId of selectedServices) { @@ -549,7 +551,7 @@ const Project = ( case "compose": await composeActions.delete.mutateAsync({ composeId: serviceId, - deleteVolumes: false, + deleteVolumes, }); break; case "postgres": @@ -879,7 +881,7 @@ const Project = ( disabled={ selectedServicesWithRunningStatus.length > 0 } - onClick={handleBulkDelete} + onClick={() => setIsBulkDeleteDialogOpen(true)} > + + + +
From 112b898d989bcc5a6c3faa154a31e51e31fe6b4c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:01:00 +0000 Subject: [PATCH 070/144] [autofix.ci] apply automated fixes --- .../show-preview-settings.tsx | 164 +++++++++--------- 1 file changed, 84 insertions(+), 80 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 0aa676057..8e5edf248 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -211,86 +211,90 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { )} /> - ( - -
- Preview Labels - - - - - - -

- Add a labels that will trigger a preview deployment - for a pull request. If no labels are specified, all - pull requests will trigger a preview deployment. -

-
-
-
-
-
- {field.value?.map((label, index) => ( - - {label} - { - const newLabels = [...(field.value || [])]; - newLabels.splice(index, 1); - field.onChange(newLabels); - }} - /> - - ))} -
-
- - { - if (e.key === "Enter") { - e.preventDefault(); - const input = e.currentTarget; - const label = input.value.trim(); - if (label) { - field.onChange([...(field.value || []), label]); - input.value = ""; - } - } - }} - /> - - -
- -
- )} - /> + ( + +
+ Preview Labels + + + + + + +

+ Add a labels that will trigger a preview + deployment for a pull request. If no labels + are specified, all pull requests will trigger + a preview deployment. +

+
+
+
+
+
+ {field.value?.map((label, index) => ( + + {label} + { + const newLabels = [...(field.value || [])]; + newLabels.splice(index, 1); + field.onChange(newLabels); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const label = input.value.trim(); + if (label) { + field.onChange([ + ...(field.value || []), + label, + ]); + input.value = ""; + } + } + }} + /> + + +
+ +
+ )} + /> Date: Sat, 23 Aug 2025 20:11:18 -0600 Subject: [PATCH 071/144] feat: add support for preview labels in deployment process --- apps/dokploy/pages/api/deploy/github.ts | 22 +++++++++++--------- packages/server/src/db/schema/application.ts | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 7e3df5444..92cf3dc9e 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -1,22 +1,22 @@ -import { db } from "@/server/db"; -import { applications, compose, github } from "@/server/db/schema"; -import type { DeploymentJob } from "@/server/queues/queue-types"; -import { myQueue } from "@/server/queues/queueSetup"; -import { deploy } from "@/server/utils/deploy"; import { - IS_CLOUD, checkUserRepositoryPermissions, createPreviewDeployment, createSecurityBlockedComment, findGithubById, findPreviewDeploymentByApplicationId, findPreviewDeploymentsByPullRequestId, + IS_CLOUD, removePreviewDeployment, shouldDeploy, } from "@dokploy/server"; import { Webhooks } from "@octokit/webhooks"; import { and, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; +import { db } from "@/server/db"; +import { applications, compose, github } from "@/server/db/schema"; +import type { DeploymentJob } from "@/server/queues/queue-types"; +import { myQueue } from "@/server/queues/queueSetup"; +import { deploy } from "@/server/utils/deploy"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; export default async function handler( @@ -343,7 +343,9 @@ export default async function handler( if ( action === "opened" || action === "synchronize" || - action === "reopened" + action === "reopened" || + action === "labeled" || + action === "unlabeled" ) { const repository = githubBody?.repository?.name; const deploymentHash = githubBody?.pull_request?.head?.sha; @@ -443,11 +445,11 @@ export default async function handler( for (const app of secureApps) { // check for labels - if (app?.previewLabels.size() > 0) { - let hasLabel: boolean = false; + if (app?.previewLabels && app?.previewLabels?.length > 0) { + let hasLabel = false; const labels = githubBody?.pull_request?.labels; for (const label of labels) { - if (app?.previewLabels.contains(label.name)) { + if (app?.previewLabels?.includes(label.name)) { hasLabel = true; break; } diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 21ee9fbda..198611a77 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -309,6 +309,7 @@ const createSchema = createInsertSchema(applications, { previewCertificateType: z.enum(["letsencrypt", "none", "custom"]).optional(), previewRequireCollaboratorPermissions: z.boolean().optional(), watchPaths: z.array(z.string()).optional(), + previewLabels: z.array(z.string()).optional(), cleanCache: z.boolean().optional(), }); From 2ef5f967a9f5f95b1c970204adb63a0168b95d69 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:14:41 -0600 Subject: [PATCH 072/144] refactor: clean up imports in show-preview-settings component --- .../preview-deployments/show-preview-settings.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 0a6637a0a..16c916d93 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -1,9 +1,9 @@ -import { zodResolver, zodResolver } from "@hookform/resolvers/zod"; -import { HelpCircle, Plus, Settings2, Settings2, X } from "lucide-react"; -import { useEffect, useEffect, useState, useState } from "react"; -import { useForm, useForm } from "react-hook-form"; -import { toast, toast } from "sonner"; -import { z, z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { HelpCircle, Plus, Settings2, X } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { From 40877e437047947899145a8354d8220d57ddaa84 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:16:35 +0000 Subject: [PATCH 073/144] [autofix.ci] apply automated fixes --- packages/server/src/setup/traefik-setup.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 5427291ef..17c48d0ff 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -15,7 +15,6 @@ export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0"; - export interface TraefikOptions { env?: string[]; serverId?: string; From c653dd604f7f6b46bee8480074602fc9258e87c9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:19:14 -0600 Subject: [PATCH 074/144] feat: add previewLabels property to baseApp in drop and traefik test files --- apps/dokploy/__test__/drop/drop.test.ts | 1 + apps/dokploy/__test__/traefik/traefik.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/dokploy/__test__/drop/drop.test.ts b/apps/dokploy/__test__/drop/drop.test.ts index 301278dc3..a9bc178a2 100644 --- a/apps/dokploy/__test__/drop/drop.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.ts @@ -27,6 +27,7 @@ if (typeof window === "undefined") { const baseApp: ApplicationNested = { railpackVersion: "0.2.2", applicationId: "", + previewLabels: [], herokuVersion: "", giteaBranch: "", giteaBuildPath: "", diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 8d9f78aba..ff8a99620 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -6,6 +6,7 @@ const baseApp: ApplicationNested = { railpackVersion: "0.2.2", rollbackActive: false, applicationId: "", + previewLabels: [], herokuVersion: "", giteaRepository: "", giteaOwner: "", From 746cf76cf3a32dc2cff2eaffb01185bea277af5a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 23 Aug 2025 22:59:52 -0600 Subject: [PATCH 075/144] fix: correct application not found error message and improve error handling in removePreviewDeployment function --- .../server/src/services/preview-deployment.ts | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index 1b358946f..44d1604ae 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -70,7 +70,7 @@ export const findApplicationByPreview = async (applicationId: string) => { if (!application) { throw new TRPCError({ code: "NOT_FOUND", - message: "Applicationnot found", + message: "Application not found", }); } return application; @@ -78,35 +78,41 @@ export const findApplicationByPreview = async (applicationId: string) => { export const removePreviewDeployment = async (previewDeploymentId: string) => { try { - const application = await findApplicationByPreview(previewDeploymentId); const previewDeployment = await findPreviewDeploymentById(previewDeploymentId); - - const deployment = await db - .delete(previewDeployments) - .where(eq(previewDeployments.previewDeploymentId, previewDeploymentId)) - .returning(); + const application = await findApplicationById( + previewDeployment.applicationId, + ); application.appName = previewDeployment.appName; const cleanupOperations = [ + async () => + await removeService(application?.appName, application?.serverId), async () => await removeDeploymentsByPreviewDeploymentId( previewDeployment, - application.serverId, + application?.serverId, ), async () => - await removeDirectoryCode(application.appName, application.serverId), + await removeDirectoryCode(application?.appName, application?.serverId), async () => - await removeTraefikConfig(application.appName, application.serverId), + await removeTraefikConfig(application?.appName, application?.serverId), async () => - await removeService(application?.appName, application.serverId), + await db + .delete(previewDeployments) + .where( + eq(previewDeployments.previewDeploymentId, previewDeploymentId), + ) + .returning(), ]; for (const operation of cleanupOperations) { try { await operation(); - } catch {} + } catch (error) { + console.error(error); + } } - return deployment[0]; + return previewDeployment; } catch (error) { const message = error instanceof Error From 5e4444610cc9512f544d05f425a9e312dddd42dc Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 06:33:36 +0000 Subject: [PATCH 076/144] [autofix.ci] apply automated fixes --- .../volume-backups/handle-volume-backups.tsx | 8 ++++---- packages/server/src/utils/volume-backups/utils.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx index 09ba71839..feb99175b 100644 --- a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx +++ b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx @@ -55,8 +55,7 @@ const formSchema = z cronExpression: z.string().min(1, "Cron expression is required"), volumeName: z.string().min(1, "Volume name is required"), prefix: z.string(), - keepLatestCount: z - .coerce + keepLatestCount: z.coerce .number() .int() .gte(1, "Must be at least 1") @@ -188,7 +187,8 @@ export const HandleVolumeBackups = ({ serviceType: volumeBackup.serviceType, }); setKeepLatestCountInput( - volumeBackup.keepLatestCount !== null && volumeBackup.keepLatestCount !== undefined + volumeBackup.keepLatestCount !== null && + volumeBackup.keepLatestCount !== undefined ? String(volumeBackup.keepLatestCount) : "", ); @@ -203,7 +203,7 @@ export const HandleVolumeBackups = ({ if (!id && !volumeBackupId) return; const preparedKeepLatestCount = - keepLatestCountInput === "" ? null : values.keepLatestCount ?? null; + keepLatestCountInput === "" ? null : (values.keepLatestCount ?? null); await mutateAsync({ ...values, diff --git a/packages/server/src/utils/volume-backups/utils.ts b/packages/server/src/utils/volume-backups/utils.ts index 7ee1ccb4d..5b55c240c 100644 --- a/packages/server/src/utils/volume-backups/utils.ts +++ b/packages/server/src/utils/volume-backups/utils.ts @@ -1,7 +1,13 @@ import { findVolumeBackupById } from "@dokploy/server/services/volume-backups"; import { scheduledJobs, scheduleJob } from "node-schedule"; -import { createDeploymentVolumeBackup, updateDeploymentStatus } from "@dokploy/server/services/deployment"; -import { execAsync, execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; +import { + createDeploymentVolumeBackup, + updateDeploymentStatus, +} from "@dokploy/server/services/deployment"; +import { + execAsync, + execAsyncRemote, +} from "@dokploy/server/utils/process/execAsync"; import { backupVolume } from "./backup"; import { getS3Credentials, normalizeS3Path } from "../backups/utils"; From 59aaa1a47a4dd7505643a8d0270ccc3ac1ae0dfc Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 00:40:17 -0600 Subject: [PATCH 077/144] fix(ui): adjust max width for volume backup dialog based on backup type --- .../application/volume-backups/handle-volume-backups.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx index feb99175b..f00b91a9d 100644 --- a/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx +++ b/apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx @@ -273,9 +273,8 @@ export const HandleVolumeBackups = ({ From cbf6f95891b327657e5c3c7c945c88b541c46930 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:19:33 -0600 Subject: [PATCH 078/144] refactor: update database connection handling and remove unused migration and seed files --- apps/dokploy/esbuild.config.ts | 6 + apps/dokploy/migrate.ts | 149 ----------------------- apps/dokploy/server/db/index.ts | 8 +- apps/dokploy/server/db/migration.ts | 21 ---- apps/dokploy/server/db/seed.ts | 29 ----- packages/server/package.json | 31 +++-- packages/server/src/db/drizzle.config.ts | 14 --- packages/server/src/db/index.ts | 21 ---- packages/server/src/db/migration.ts | 21 ---- packages/server/src/db/reset.ts | 23 ---- packages/server/src/db/seed.ts | 35 ------ 11 files changed, 33 insertions(+), 325 deletions(-) delete mode 100644 apps/dokploy/migrate.ts delete mode 100644 apps/dokploy/server/db/migration.ts delete mode 100644 apps/dokploy/server/db/seed.ts delete mode 100644 packages/server/src/db/drizzle.config.ts delete mode 100644 packages/server/src/db/index.ts delete mode 100644 packages/server/src/db/migration.ts delete mode 100644 packages/server/src/db/reset.ts delete mode 100644 packages/server/src/db/seed.ts diff --git a/apps/dokploy/esbuild.config.ts b/apps/dokploy/esbuild.config.ts index c84135e5d..6d96cf058 100644 --- a/apps/dokploy/esbuild.config.ts +++ b/apps/dokploy/esbuild.config.ts @@ -7,6 +7,10 @@ function prepareDefine(config: DotenvParseOutput | undefined) { const define = {}; // @ts-ignore for (const [key, value] of Object.entries(config)) { + // Skip DATABASE_URL to allow runtime environment variable override + if (key === "DATABASE_URL") { + continue; + } // @ts-ignore define[`process.env.${key}`] = JSON.stringify(value); } @@ -14,6 +18,8 @@ function prepareDefine(config: DotenvParseOutput | undefined) { } const define = prepareDefine(result.parsed); + +console.log(define); try { esbuild .build({ diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts deleted file mode 100644 index e1f52c9a3..000000000 --- a/apps/dokploy/migrate.ts +++ /dev/null @@ -1,149 +0,0 @@ -// import { drizzle } from "drizzle-orm/postgres-js"; -// import { nanoid } from "nanoid"; -// import postgres from "postgres"; -// import * as schema from "./server/db/schema"; - -// const connectionString = process.env.DATABASE_URL!; - -// const sql = postgres(connectionString, { max: 1 }); -// const db = drizzle(sql, { -// schema, -// }); - -// await db -// .transaction(async (db) => { -// const admins = await db.query.admins.findMany({ -// with: { -// auth: true, -// users: { -// with: { -// auth: true, -// }, -// }, -// }, -// }); -// for (const admin of admins) { -// const user = await db -// .insert(schema.users_temp) -// .values({ -// id: admin.adminId, -// email: admin.auth.email, -// token: admin.auth.token || "", -// emailVerified: true, -// updatedAt: new Date(), -// role: "admin", -// serverIp: admin.serverIp, -// image: admin.auth.image, -// certificateType: admin.certificateType, -// host: admin.host, -// letsEncryptEmail: admin.letsEncryptEmail, -// sshPrivateKey: admin.sshPrivateKey, -// enableDockerCleanup: admin.enableDockerCleanup, -// enableLogRotation: admin.enableLogRotation, -// enablePaidFeatures: admin.enablePaidFeatures, -// metricsConfig: admin.metricsConfig, -// cleanupCacheApplications: admin.cleanupCacheApplications, -// cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, -// cleanupCacheOnCompose: admin.cleanupCacheOnCompose, -// stripeCustomerId: admin.stripeCustomerId, -// stripeSubscriptionId: admin.stripeSubscriptionId, -// serversQuantity: admin.serversQuantity, -// }) -// .returning() -// .then((user) => user[0]); - -// await db.insert(schema.account).values({ -// providerId: "credential", -// userId: user?.id || "", -// password: admin.auth.password, -// is2FAEnabled: admin.auth.is2FAEnabled || false, -// createdAt: new Date(admin.auth.createdAt) || new Date(), -// updatedAt: new Date(admin.auth.createdAt) || new Date(), -// }); - -// const organization = await db -// .insert(schema.organization) -// .values({ -// name: "My Organization", -// slug: nanoid(), -// ownerId: user?.id || "", -// createdAt: new Date(admin.createdAt) || new Date(), -// }) -// .returning() -// .then((organization) => organization[0]); - -// for (const member of admin.users) { -// const userTemp = await db -// .insert(schema.users_temp) -// .values({ -// id: member.userId, -// email: member.auth.email, -// token: member.token || "", -// emailVerified: true, -// updatedAt: new Date(admin.createdAt) || new Date(), -// role: "user", -// image: member.auth.image, -// createdAt: admin.createdAt, -// canAccessToAPI: member.canAccessToAPI || false, -// canAccessToDocker: member.canAccessToDocker || false, -// canAccessToGitProviders: member.canAccessToGitProviders || false, -// canAccessToSSHKeys: member.canAccessToSSHKeys || false, -// canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, -// canCreateProjects: member.canCreateProjects || false, -// canCreateServices: member.canCreateServices || false, -// canDeleteProjects: member.canDeleteProjects || false, -// canDeleteServices: member.canDeleteServices || false, -// accessedProjects: member.accessedProjects || [], -// accessedServices: member.accessedServices || [], -// }) -// .returning() -// .then((userTemp) => userTemp[0]); - -// await db.insert(schema.account).values({ -// providerId: "credential", -// userId: member?.userId || "", -// password: member.auth.password, -// is2FAEnabled: member.auth.is2FAEnabled || false, -// createdAt: new Date(member.auth.createdAt) || new Date(), -// updatedAt: new Date(member.auth.createdAt) || new Date(), -// }); - -// await db.insert(schema.member).values({ -// organizationId: organization?.id || "", -// userId: userTemp?.id || "", -// role: "admin", -// createdAt: new Date(member.createdAt) || new Date(), -// }); -// } -// } -// }) -// .then(() => { -// console.log("Migration finished"); -// }) -// .catch((error) => { -// console.error(error); -// }); - -// await db -// .transaction(async (db) => { -// const projects = await db.query.projects.findMany({ -// with: { -// user: { -// with: { -// organizations: true, -// }, -// }, -// }, -// }); -// for (const project of projects) { -// const _user = await db.update(schema.projects).set({ -// organizationId: project.user.organizations[0]?.id || "", -// }); -// } -// }) -// .then(() => { -// console.log("Migration finished"); -// }) -// .catch((error) => { -// console.error(error); -// }); diff --git a/apps/dokploy/server/db/index.ts b/apps/dokploy/server/db/index.ts index 3ac6e3940..55d6d3a46 100644 --- a/apps/dokploy/server/db/index.ts +++ b/apps/dokploy/server/db/index.ts @@ -6,14 +6,18 @@ declare global { var db: PostgresJsDatabase | undefined; } +const dbUrl = + process.env.DATABASE_URL || + "postgres://dokploy:amukds4wi9001583845717ad2@dokploy-postgres:5432/dokploy"; + export let db: PostgresJsDatabase; if (process.env.NODE_ENV === "production") { - db = drizzle(postgres(process.env.DATABASE_URL!), { + db = drizzle(postgres(dbUrl!), { schema, }); } else { if (!global.db) - global.db = drizzle(postgres(process.env.DATABASE_URL!), { + global.db = drizzle(postgres(dbUrl!), { schema, }); diff --git a/apps/dokploy/server/db/migration.ts b/apps/dokploy/server/db/migration.ts deleted file mode 100644 index fa2e1a80f..000000000 --- a/apps/dokploy/server/db/migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { drizzle } from "drizzle-orm/postgres-js"; -import { migrate } from "drizzle-orm/postgres-js/migrator"; -import postgres from "postgres"; - -const connectionString = process.env.DATABASE_URL!; - -const sql = postgres(connectionString, { max: 1 }); -const db = drizzle(sql); - -export const migration = async () => - await migrate(db, { migrationsFolder: "drizzle" }) - .then(() => { - console.log("Migration complete"); - sql.end(); - }) - .catch((error) => { - console.log("Migration failed", error); - }) - .finally(() => { - sql.end(); - }); diff --git a/apps/dokploy/server/db/seed.ts b/apps/dokploy/server/db/seed.ts deleted file mode 100644 index 5b3eb6c62..000000000 --- a/apps/dokploy/server/db/seed.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { drizzle } from "drizzle-orm/postgres-js"; -import postgres from "postgres"; - -const connectionString = process.env.DATABASE_URL!; - -const pg = postgres(connectionString, { max: 1 }); -const _db = drizzle(pg); - -async function seed() { - console.log("> Seed:", process.env.DATABASE_PATH, "\n"); - - // const authenticationR = await db - // .insert(users) - // .values([ - // { - // email: "user1@hotmail.com", - // password: password("12345671"), - // }, - // ]) - // .onConflictDoNothing() - // .returning(); - - // console.log("\nSemillas Update:", authenticationR.length); -} - -seed().catch((e) => { - console.error(e); - process.exit(1); -}); diff --git a/packages/server/package.json b/packages/server/package.json index dbf7d3c65..fc531d540 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,21 +1,32 @@ { "name": "@dokploy/server", "version": "1.0.0", - "main": "./src/index.ts", + "main": "./dist/index.js", "type": "module", "exports": { - ".": "./src/index.ts", + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs.js" + }, "./db": { - "import": "./src/db/index.ts", + "import": "./dist/db/index.js", "require": "./dist/db/index.cjs.js" }, - "./setup/*": { - "import": "./src/setup/*.ts", - "require": "./dist/setup/index.cjs.js" + "./*": { + "import": "./dist/*", + "require": "./dist/*.cjs" }, - "./constants": { - "import": "./src/constants/index.ts", - "require": "./dist/constants.cjs.js" + "./dist": { + "import": "./dist/index.js", + "require": "./dist/index.cjs.js" + }, + "./dist/db": { + "import": "./dist/db/index.js", + "require": "./dist/db/index.cjs.js" + }, + "./dist/db/schema": { + "import": "./dist/db/schema/index.js", + "require": "./dist/db/schema/index.cjs.js" } }, "scripts": { @@ -111,4 +122,4 @@ "node": "^20.16.0", "pnpm": ">=9.12.0" } -} +} \ No newline at end of file diff --git a/packages/server/src/db/drizzle.config.ts b/packages/server/src/db/drizzle.config.ts deleted file mode 100644 index 60a3bb937..000000000 --- a/packages/server/src/db/drizzle.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineConfig } from "drizzle-kit"; - -export default defineConfig({ - schema: "./server/db/schema/index.ts", - dialect: "postgresql", - dbCredentials: { - url: process.env.DATABASE_URL!, - }, - out: "drizzle", - migrations: { - table: "migrations", - schema: "public", - }, -}); diff --git a/packages/server/src/db/index.ts b/packages/server/src/db/index.ts deleted file mode 100644 index 3ac6e3940..000000000 --- a/packages/server/src/db/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { drizzle, type PostgresJsDatabase } from "drizzle-orm/postgres-js"; -import postgres from "postgres"; -import * as schema from "./schema"; - -declare global { - var db: PostgresJsDatabase | undefined; -} - -export let db: PostgresJsDatabase; -if (process.env.NODE_ENV === "production") { - db = drizzle(postgres(process.env.DATABASE_URL!), { - schema, - }); -} else { - if (!global.db) - global.db = drizzle(postgres(process.env.DATABASE_URL!), { - schema, - }); - - db = global.db; -} diff --git a/packages/server/src/db/migration.ts b/packages/server/src/db/migration.ts deleted file mode 100644 index 6fada0833..000000000 --- a/packages/server/src/db/migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -// import { drizzle } from "drizzle-orm/postgres-js"; -// import { migrate } from "drizzle-orm/postgres-js/migrator"; -// import postgres from "postgres"; - -// const connectionString = process.env.DATABASE_URL!; - -// const sql = postgres(connectionString, { max: 1 }); -// const db = drizzle(sql); - -// export const migration = async () => -// await migrate(db, { migrationsFolder: "drizzle" }) -// .then(() => { -// console.log("Migration complete"); -// sql.end(); -// }) -// .catch((error) => { -// console.log("Migration failed", error); -// }) -// .finally(() => { -// sql.end(); -// }); diff --git a/packages/server/src/db/reset.ts b/packages/server/src/db/reset.ts deleted file mode 100644 index c22291478..000000000 --- a/packages/server/src/db/reset.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { sql } from "drizzle-orm"; -// Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406 -import { drizzle } from "drizzle-orm/postgres-js"; -import postgres from "postgres"; - -const connectionString = process.env.DATABASE_URL!; - -const pg = postgres(connectionString, { max: 1 }); -const db = drizzle(pg); - -const clearDb = async (): Promise => { - try { - const tablesQuery = sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`; - const tables = await db.execute(tablesQuery); - console.log(tables); - await pg.end(); - } catch (error) { - console.error("Error cleaning database", error); - } finally { - } -}; - -clearDb(); diff --git a/packages/server/src/db/seed.ts b/packages/server/src/db/seed.ts deleted file mode 100644 index 7e2736b00..000000000 --- a/packages/server/src/db/seed.ts +++ /dev/null @@ -1,35 +0,0 @@ -// import bc from "bcrypt"; -// import { drizzle } from "drizzle-orm/postgres-js"; -// import postgres from "postgres"; -// import { users } from "./schema"; - -// const connectionString = process.env.DATABASE_URL!; - -// const pg = postgres(connectionString, { max: 1 }); -// const db = drizzle(pg); - -// function password(txt: string) { -// return bc.hashSync(txt, 10); -// } - -// async function seed() { -// console.log("> Seed:", process.env.DATABASE_PATH, "\n"); - -// // const authenticationR = await db -// // .insert(users) -// // .values([ -// // { -// // email: "user1@hotmail.com", -// // password: password("12345671"), -// // }, -// // ]) -// // .onConflictDoNothing() -// // .returning(); - -// // console.log("\nSemillas Update:", authenticationR.length); -// } - -// seed().catch((e) => { -// console.error(e); -// process.exit(1); -// }); From 8c420ff4f5b080c26d1c28fd9d5a685876a28555 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:20:32 -0600 Subject: [PATCH 079/144] refactor: update package.json to use TypeScript source files instead of compiled JavaScript --- packages/server/package.json | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index fc531d540..3b249a65b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,32 +1,21 @@ { "name": "@dokploy/server", "version": "1.0.0", - "main": "./dist/index.js", + "main": "./src/index.ts", "type": "module", "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs.js" - }, + ".": "./src/index.ts", "./db": { - "import": "./dist/db/index.js", + "import": "./src/db/index.ts", "require": "./dist/db/index.cjs.js" }, - "./*": { - "import": "./dist/*", - "require": "./dist/*.cjs" + "./setup/*": { + "import": "./src/setup/*.ts", + "require": "./dist/setup/index.cjs.js" }, - "./dist": { - "import": "./dist/index.js", - "require": "./dist/index.cjs.js" - }, - "./dist/db": { - "import": "./dist/db/index.js", - "require": "./dist/db/index.cjs.js" - }, - "./dist/db/schema": { - "import": "./dist/db/schema/index.js", - "require": "./dist/db/schema/index.cjs.js" + "./constants": { + "import": "./src/constants/index.ts", + "require": "./dist/constants.cjs.js" } }, "scripts": { From 03588bf375544ac1d284ebe39227372555e8af85 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:21:01 -0600 Subject: [PATCH 080/144] chore: remove console.log statement from esbuild configuration --- apps/dokploy/esbuild.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dokploy/esbuild.config.ts b/apps/dokploy/esbuild.config.ts index 6d96cf058..a1747ac55 100644 --- a/apps/dokploy/esbuild.config.ts +++ b/apps/dokploy/esbuild.config.ts @@ -19,7 +19,6 @@ function prepareDefine(config: DotenvParseOutput | undefined) { const define = prepareDefine(result.parsed); -console.log(define); try { esbuild .build({ From c42054b965e4dcfb27cff60fcbffba4e52075d55 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:22:42 -0600 Subject: [PATCH 081/144] feat(migration): implement database migration functionality using drizzle-orm --- apps/dokploy/server/db/migration.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 apps/dokploy/server/db/migration.ts diff --git a/apps/dokploy/server/db/migration.ts b/apps/dokploy/server/db/migration.ts new file mode 100644 index 000000000..fa2e1a80f --- /dev/null +++ b/apps/dokploy/server/db/migration.ts @@ -0,0 +1,21 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import { migrate } from "drizzle-orm/postgres-js/migrator"; +import postgres from "postgres"; + +const connectionString = process.env.DATABASE_URL!; + +const sql = postgres(connectionString, { max: 1 }); +const db = drizzle(sql); + +export const migration = async () => + await migrate(db, { migrationsFolder: "drizzle" }) + .then(() => { + console.log("Migration complete"); + sql.end(); + }) + .catch((error) => { + console.log("Migration failed", error); + }) + .finally(() => { + sql.end(); + }); From aa434cbdeab8a2506c48ac0ecc811e91425837a3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:25:04 -0600 Subject: [PATCH 082/144] feat(db): add database connection setup using drizzle-orm for PostgreSQL --- packages/server/src/db/index.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 packages/server/src/db/index.ts diff --git a/packages/server/src/db/index.ts b/packages/server/src/db/index.ts new file mode 100644 index 000000000..3ac6e3940 --- /dev/null +++ b/packages/server/src/db/index.ts @@ -0,0 +1,21 @@ +import { drizzle, type PostgresJsDatabase } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import * as schema from "./schema"; + +declare global { + var db: PostgresJsDatabase | undefined; +} + +export let db: PostgresJsDatabase; +if (process.env.NODE_ENV === "production") { + db = drizzle(postgres(process.env.DATABASE_URL!), { + schema, + }); +} else { + if (!global.db) + global.db = drizzle(postgres(process.env.DATABASE_URL!), { + schema, + }); + + db = global.db; +} From 17f333ac2a5c789f6426c1d56daa44763ee5c7c2 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:44:00 -0600 Subject: [PATCH 083/144] =?UTF-8?q?Revert=20"refactor:=20update=20database?= =?UTF-8?q?=20connection=20handling=20and=20remove=20unused=20migra?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dokploy/esbuild.config.ts | 5 - apps/dokploy/migrate.ts | 149 +++++++++++++++++++++++ apps/dokploy/server/db/index.ts | 8 +- apps/dokploy/server/db/seed.ts | 29 +++++ packages/server/package.json | 2 +- packages/server/src/db/drizzle.config.ts | 14 +++ packages/server/src/db/migration.ts | 21 ++++ packages/server/src/db/reset.ts | 23 ++++ packages/server/src/db/seed.ts | 35 ++++++ 9 files changed, 274 insertions(+), 12 deletions(-) create mode 100644 apps/dokploy/migrate.ts create mode 100644 apps/dokploy/server/db/seed.ts create mode 100644 packages/server/src/db/drizzle.config.ts create mode 100644 packages/server/src/db/migration.ts create mode 100644 packages/server/src/db/reset.ts create mode 100644 packages/server/src/db/seed.ts diff --git a/apps/dokploy/esbuild.config.ts b/apps/dokploy/esbuild.config.ts index a1747ac55..c84135e5d 100644 --- a/apps/dokploy/esbuild.config.ts +++ b/apps/dokploy/esbuild.config.ts @@ -7,10 +7,6 @@ function prepareDefine(config: DotenvParseOutput | undefined) { const define = {}; // @ts-ignore for (const [key, value] of Object.entries(config)) { - // Skip DATABASE_URL to allow runtime environment variable override - if (key === "DATABASE_URL") { - continue; - } // @ts-ignore define[`process.env.${key}`] = JSON.stringify(value); } @@ -18,7 +14,6 @@ function prepareDefine(config: DotenvParseOutput | undefined) { } const define = prepareDefine(result.parsed); - try { esbuild .build({ diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts new file mode 100644 index 000000000..e1f52c9a3 --- /dev/null +++ b/apps/dokploy/migrate.ts @@ -0,0 +1,149 @@ +// import { drizzle } from "drizzle-orm/postgres-js"; +// import { nanoid } from "nanoid"; +// import postgres from "postgres"; +// import * as schema from "./server/db/schema"; + +// const connectionString = process.env.DATABASE_URL!; + +// const sql = postgres(connectionString, { max: 1 }); +// const db = drizzle(sql, { +// schema, +// }); + +// await db +// .transaction(async (db) => { +// const admins = await db.query.admins.findMany({ +// with: { +// auth: true, +// users: { +// with: { +// auth: true, +// }, +// }, +// }, +// }); +// for (const admin of admins) { +// const user = await db +// .insert(schema.users_temp) +// .values({ +// id: admin.adminId, +// email: admin.auth.email, +// token: admin.auth.token || "", +// emailVerified: true, +// updatedAt: new Date(), +// role: "admin", +// serverIp: admin.serverIp, +// image: admin.auth.image, +// certificateType: admin.certificateType, +// host: admin.host, +// letsEncryptEmail: admin.letsEncryptEmail, +// sshPrivateKey: admin.sshPrivateKey, +// enableDockerCleanup: admin.enableDockerCleanup, +// enableLogRotation: admin.enableLogRotation, +// enablePaidFeatures: admin.enablePaidFeatures, +// metricsConfig: admin.metricsConfig, +// cleanupCacheApplications: admin.cleanupCacheApplications, +// cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, +// cleanupCacheOnCompose: admin.cleanupCacheOnCompose, +// stripeCustomerId: admin.stripeCustomerId, +// stripeSubscriptionId: admin.stripeSubscriptionId, +// serversQuantity: admin.serversQuantity, +// }) +// .returning() +// .then((user) => user[0]); + +// await db.insert(schema.account).values({ +// providerId: "credential", +// userId: user?.id || "", +// password: admin.auth.password, +// is2FAEnabled: admin.auth.is2FAEnabled || false, +// createdAt: new Date(admin.auth.createdAt) || new Date(), +// updatedAt: new Date(admin.auth.createdAt) || new Date(), +// }); + +// const organization = await db +// .insert(schema.organization) +// .values({ +// name: "My Organization", +// slug: nanoid(), +// ownerId: user?.id || "", +// createdAt: new Date(admin.createdAt) || new Date(), +// }) +// .returning() +// .then((organization) => organization[0]); + +// for (const member of admin.users) { +// const userTemp = await db +// .insert(schema.users_temp) +// .values({ +// id: member.userId, +// email: member.auth.email, +// token: member.token || "", +// emailVerified: true, +// updatedAt: new Date(admin.createdAt) || new Date(), +// role: "user", +// image: member.auth.image, +// createdAt: admin.createdAt, +// canAccessToAPI: member.canAccessToAPI || false, +// canAccessToDocker: member.canAccessToDocker || false, +// canAccessToGitProviders: member.canAccessToGitProviders || false, +// canAccessToSSHKeys: member.canAccessToSSHKeys || false, +// canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, +// canCreateProjects: member.canCreateProjects || false, +// canCreateServices: member.canCreateServices || false, +// canDeleteProjects: member.canDeleteProjects || false, +// canDeleteServices: member.canDeleteServices || false, +// accessedProjects: member.accessedProjects || [], +// accessedServices: member.accessedServices || [], +// }) +// .returning() +// .then((userTemp) => userTemp[0]); + +// await db.insert(schema.account).values({ +// providerId: "credential", +// userId: member?.userId || "", +// password: member.auth.password, +// is2FAEnabled: member.auth.is2FAEnabled || false, +// createdAt: new Date(member.auth.createdAt) || new Date(), +// updatedAt: new Date(member.auth.createdAt) || new Date(), +// }); + +// await db.insert(schema.member).values({ +// organizationId: organization?.id || "", +// userId: userTemp?.id || "", +// role: "admin", +// createdAt: new Date(member.createdAt) || new Date(), +// }); +// } +// } +// }) +// .then(() => { +// console.log("Migration finished"); +// }) +// .catch((error) => { +// console.error(error); +// }); + +// await db +// .transaction(async (db) => { +// const projects = await db.query.projects.findMany({ +// with: { +// user: { +// with: { +// organizations: true, +// }, +// }, +// }, +// }); +// for (const project of projects) { +// const _user = await db.update(schema.projects).set({ +// organizationId: project.user.organizations[0]?.id || "", +// }); +// } +// }) +// .then(() => { +// console.log("Migration finished"); +// }) +// .catch((error) => { +// console.error(error); +// }); diff --git a/apps/dokploy/server/db/index.ts b/apps/dokploy/server/db/index.ts index 55d6d3a46..3ac6e3940 100644 --- a/apps/dokploy/server/db/index.ts +++ b/apps/dokploy/server/db/index.ts @@ -6,18 +6,14 @@ declare global { var db: PostgresJsDatabase | undefined; } -const dbUrl = - process.env.DATABASE_URL || - "postgres://dokploy:amukds4wi9001583845717ad2@dokploy-postgres:5432/dokploy"; - export let db: PostgresJsDatabase; if (process.env.NODE_ENV === "production") { - db = drizzle(postgres(dbUrl!), { + db = drizzle(postgres(process.env.DATABASE_URL!), { schema, }); } else { if (!global.db) - global.db = drizzle(postgres(dbUrl!), { + global.db = drizzle(postgres(process.env.DATABASE_URL!), { schema, }); diff --git a/apps/dokploy/server/db/seed.ts b/apps/dokploy/server/db/seed.ts new file mode 100644 index 000000000..5b3eb6c62 --- /dev/null +++ b/apps/dokploy/server/db/seed.ts @@ -0,0 +1,29 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; + +const connectionString = process.env.DATABASE_URL!; + +const pg = postgres(connectionString, { max: 1 }); +const _db = drizzle(pg); + +async function seed() { + console.log("> Seed:", process.env.DATABASE_PATH, "\n"); + + // const authenticationR = await db + // .insert(users) + // .values([ + // { + // email: "user1@hotmail.com", + // password: password("12345671"), + // }, + // ]) + // .onConflictDoNothing() + // .returning(); + + // console.log("\nSemillas Update:", authenticationR.length); +} + +seed().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/packages/server/package.json b/packages/server/package.json index 3b249a65b..dbf7d3c65 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -111,4 +111,4 @@ "node": "^20.16.0", "pnpm": ">=9.12.0" } -} \ No newline at end of file +} diff --git a/packages/server/src/db/drizzle.config.ts b/packages/server/src/db/drizzle.config.ts new file mode 100644 index 000000000..60a3bb937 --- /dev/null +++ b/packages/server/src/db/drizzle.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: "./server/db/schema/index.ts", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL!, + }, + out: "drizzle", + migrations: { + table: "migrations", + schema: "public", + }, +}); diff --git a/packages/server/src/db/migration.ts b/packages/server/src/db/migration.ts new file mode 100644 index 000000000..6fada0833 --- /dev/null +++ b/packages/server/src/db/migration.ts @@ -0,0 +1,21 @@ +// import { drizzle } from "drizzle-orm/postgres-js"; +// import { migrate } from "drizzle-orm/postgres-js/migrator"; +// import postgres from "postgres"; + +// const connectionString = process.env.DATABASE_URL!; + +// const sql = postgres(connectionString, { max: 1 }); +// const db = drizzle(sql); + +// export const migration = async () => +// await migrate(db, { migrationsFolder: "drizzle" }) +// .then(() => { +// console.log("Migration complete"); +// sql.end(); +// }) +// .catch((error) => { +// console.log("Migration failed", error); +// }) +// .finally(() => { +// sql.end(); +// }); diff --git a/packages/server/src/db/reset.ts b/packages/server/src/db/reset.ts new file mode 100644 index 000000000..c22291478 --- /dev/null +++ b/packages/server/src/db/reset.ts @@ -0,0 +1,23 @@ +import { sql } from "drizzle-orm"; +// Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406 +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; + +const connectionString = process.env.DATABASE_URL!; + +const pg = postgres(connectionString, { max: 1 }); +const db = drizzle(pg); + +const clearDb = async (): Promise => { + try { + const tablesQuery = sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`; + const tables = await db.execute(tablesQuery); + console.log(tables); + await pg.end(); + } catch (error) { + console.error("Error cleaning database", error); + } finally { + } +}; + +clearDb(); diff --git a/packages/server/src/db/seed.ts b/packages/server/src/db/seed.ts new file mode 100644 index 000000000..7e2736b00 --- /dev/null +++ b/packages/server/src/db/seed.ts @@ -0,0 +1,35 @@ +// import bc from "bcrypt"; +// import { drizzle } from "drizzle-orm/postgres-js"; +// import postgres from "postgres"; +// import { users } from "./schema"; + +// const connectionString = process.env.DATABASE_URL!; + +// const pg = postgres(connectionString, { max: 1 }); +// const db = drizzle(pg); + +// function password(txt: string) { +// return bc.hashSync(txt, 10); +// } + +// async function seed() { +// console.log("> Seed:", process.env.DATABASE_PATH, "\n"); + +// // const authenticationR = await db +// // .insert(users) +// // .values([ +// // { +// // email: "user1@hotmail.com", +// // password: password("12345671"), +// // }, +// // ]) +// // .onConflictDoNothing() +// // .returning(); + +// // console.log("\nSemillas Update:", authenticationR.length); +// } + +// seed().catch((e) => { +// console.error(e); +// process.exit(1); +// }); From 1635bab44fe1a1ba29a7bae3d9583345bd7c0723 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:49:48 -0600 Subject: [PATCH 084/144] =?UTF-8?q?Reapply=20"refactor:=20update=20databas?= =?UTF-8?q?e=20connection=20handling=20and=20remove=20unused=20migra?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 17f333ac2a5c789f6426c1d56daa44763ee5c7c2. --- apps/dokploy/esbuild.config.ts | 5 + apps/dokploy/migrate.ts | 149 ----------------------- apps/dokploy/server/db/index.ts | 8 +- apps/dokploy/server/db/seed.ts | 29 ----- packages/server/package.json | 2 +- packages/server/src/db/drizzle.config.ts | 14 --- packages/server/src/db/migration.ts | 21 ---- packages/server/src/db/reset.ts | 23 ---- packages/server/src/db/seed.ts | 35 ------ 9 files changed, 12 insertions(+), 274 deletions(-) delete mode 100644 apps/dokploy/migrate.ts delete mode 100644 apps/dokploy/server/db/seed.ts delete mode 100644 packages/server/src/db/drizzle.config.ts delete mode 100644 packages/server/src/db/migration.ts delete mode 100644 packages/server/src/db/reset.ts delete mode 100644 packages/server/src/db/seed.ts diff --git a/apps/dokploy/esbuild.config.ts b/apps/dokploy/esbuild.config.ts index c84135e5d..a1747ac55 100644 --- a/apps/dokploy/esbuild.config.ts +++ b/apps/dokploy/esbuild.config.ts @@ -7,6 +7,10 @@ function prepareDefine(config: DotenvParseOutput | undefined) { const define = {}; // @ts-ignore for (const [key, value] of Object.entries(config)) { + // Skip DATABASE_URL to allow runtime environment variable override + if (key === "DATABASE_URL") { + continue; + } // @ts-ignore define[`process.env.${key}`] = JSON.stringify(value); } @@ -14,6 +18,7 @@ function prepareDefine(config: DotenvParseOutput | undefined) { } const define = prepareDefine(result.parsed); + try { esbuild .build({ diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts deleted file mode 100644 index e1f52c9a3..000000000 --- a/apps/dokploy/migrate.ts +++ /dev/null @@ -1,149 +0,0 @@ -// import { drizzle } from "drizzle-orm/postgres-js"; -// import { nanoid } from "nanoid"; -// import postgres from "postgres"; -// import * as schema from "./server/db/schema"; - -// const connectionString = process.env.DATABASE_URL!; - -// const sql = postgres(connectionString, { max: 1 }); -// const db = drizzle(sql, { -// schema, -// }); - -// await db -// .transaction(async (db) => { -// const admins = await db.query.admins.findMany({ -// with: { -// auth: true, -// users: { -// with: { -// auth: true, -// }, -// }, -// }, -// }); -// for (const admin of admins) { -// const user = await db -// .insert(schema.users_temp) -// .values({ -// id: admin.adminId, -// email: admin.auth.email, -// token: admin.auth.token || "", -// emailVerified: true, -// updatedAt: new Date(), -// role: "admin", -// serverIp: admin.serverIp, -// image: admin.auth.image, -// certificateType: admin.certificateType, -// host: admin.host, -// letsEncryptEmail: admin.letsEncryptEmail, -// sshPrivateKey: admin.sshPrivateKey, -// enableDockerCleanup: admin.enableDockerCleanup, -// enableLogRotation: admin.enableLogRotation, -// enablePaidFeatures: admin.enablePaidFeatures, -// metricsConfig: admin.metricsConfig, -// cleanupCacheApplications: admin.cleanupCacheApplications, -// cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, -// cleanupCacheOnCompose: admin.cleanupCacheOnCompose, -// stripeCustomerId: admin.stripeCustomerId, -// stripeSubscriptionId: admin.stripeSubscriptionId, -// serversQuantity: admin.serversQuantity, -// }) -// .returning() -// .then((user) => user[0]); - -// await db.insert(schema.account).values({ -// providerId: "credential", -// userId: user?.id || "", -// password: admin.auth.password, -// is2FAEnabled: admin.auth.is2FAEnabled || false, -// createdAt: new Date(admin.auth.createdAt) || new Date(), -// updatedAt: new Date(admin.auth.createdAt) || new Date(), -// }); - -// const organization = await db -// .insert(schema.organization) -// .values({ -// name: "My Organization", -// slug: nanoid(), -// ownerId: user?.id || "", -// createdAt: new Date(admin.createdAt) || new Date(), -// }) -// .returning() -// .then((organization) => organization[0]); - -// for (const member of admin.users) { -// const userTemp = await db -// .insert(schema.users_temp) -// .values({ -// id: member.userId, -// email: member.auth.email, -// token: member.token || "", -// emailVerified: true, -// updatedAt: new Date(admin.createdAt) || new Date(), -// role: "user", -// image: member.auth.image, -// createdAt: admin.createdAt, -// canAccessToAPI: member.canAccessToAPI || false, -// canAccessToDocker: member.canAccessToDocker || false, -// canAccessToGitProviders: member.canAccessToGitProviders || false, -// canAccessToSSHKeys: member.canAccessToSSHKeys || false, -// canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, -// canCreateProjects: member.canCreateProjects || false, -// canCreateServices: member.canCreateServices || false, -// canDeleteProjects: member.canDeleteProjects || false, -// canDeleteServices: member.canDeleteServices || false, -// accessedProjects: member.accessedProjects || [], -// accessedServices: member.accessedServices || [], -// }) -// .returning() -// .then((userTemp) => userTemp[0]); - -// await db.insert(schema.account).values({ -// providerId: "credential", -// userId: member?.userId || "", -// password: member.auth.password, -// is2FAEnabled: member.auth.is2FAEnabled || false, -// createdAt: new Date(member.auth.createdAt) || new Date(), -// updatedAt: new Date(member.auth.createdAt) || new Date(), -// }); - -// await db.insert(schema.member).values({ -// organizationId: organization?.id || "", -// userId: userTemp?.id || "", -// role: "admin", -// createdAt: new Date(member.createdAt) || new Date(), -// }); -// } -// } -// }) -// .then(() => { -// console.log("Migration finished"); -// }) -// .catch((error) => { -// console.error(error); -// }); - -// await db -// .transaction(async (db) => { -// const projects = await db.query.projects.findMany({ -// with: { -// user: { -// with: { -// organizations: true, -// }, -// }, -// }, -// }); -// for (const project of projects) { -// const _user = await db.update(schema.projects).set({ -// organizationId: project.user.organizations[0]?.id || "", -// }); -// } -// }) -// .then(() => { -// console.log("Migration finished"); -// }) -// .catch((error) => { -// console.error(error); -// }); diff --git a/apps/dokploy/server/db/index.ts b/apps/dokploy/server/db/index.ts index 3ac6e3940..55d6d3a46 100644 --- a/apps/dokploy/server/db/index.ts +++ b/apps/dokploy/server/db/index.ts @@ -6,14 +6,18 @@ declare global { var db: PostgresJsDatabase | undefined; } +const dbUrl = + process.env.DATABASE_URL || + "postgres://dokploy:amukds4wi9001583845717ad2@dokploy-postgres:5432/dokploy"; + export let db: PostgresJsDatabase; if (process.env.NODE_ENV === "production") { - db = drizzle(postgres(process.env.DATABASE_URL!), { + db = drizzle(postgres(dbUrl!), { schema, }); } else { if (!global.db) - global.db = drizzle(postgres(process.env.DATABASE_URL!), { + global.db = drizzle(postgres(dbUrl!), { schema, }); diff --git a/apps/dokploy/server/db/seed.ts b/apps/dokploy/server/db/seed.ts deleted file mode 100644 index 5b3eb6c62..000000000 --- a/apps/dokploy/server/db/seed.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { drizzle } from "drizzle-orm/postgres-js"; -import postgres from "postgres"; - -const connectionString = process.env.DATABASE_URL!; - -const pg = postgres(connectionString, { max: 1 }); -const _db = drizzle(pg); - -async function seed() { - console.log("> Seed:", process.env.DATABASE_PATH, "\n"); - - // const authenticationR = await db - // .insert(users) - // .values([ - // { - // email: "user1@hotmail.com", - // password: password("12345671"), - // }, - // ]) - // .onConflictDoNothing() - // .returning(); - - // console.log("\nSemillas Update:", authenticationR.length); -} - -seed().catch((e) => { - console.error(e); - process.exit(1); -}); diff --git a/packages/server/package.json b/packages/server/package.json index dbf7d3c65..3b249a65b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -111,4 +111,4 @@ "node": "^20.16.0", "pnpm": ">=9.12.0" } -} +} \ No newline at end of file diff --git a/packages/server/src/db/drizzle.config.ts b/packages/server/src/db/drizzle.config.ts deleted file mode 100644 index 60a3bb937..000000000 --- a/packages/server/src/db/drizzle.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineConfig } from "drizzle-kit"; - -export default defineConfig({ - schema: "./server/db/schema/index.ts", - dialect: "postgresql", - dbCredentials: { - url: process.env.DATABASE_URL!, - }, - out: "drizzle", - migrations: { - table: "migrations", - schema: "public", - }, -}); diff --git a/packages/server/src/db/migration.ts b/packages/server/src/db/migration.ts deleted file mode 100644 index 6fada0833..000000000 --- a/packages/server/src/db/migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -// import { drizzle } from "drizzle-orm/postgres-js"; -// import { migrate } from "drizzle-orm/postgres-js/migrator"; -// import postgres from "postgres"; - -// const connectionString = process.env.DATABASE_URL!; - -// const sql = postgres(connectionString, { max: 1 }); -// const db = drizzle(sql); - -// export const migration = async () => -// await migrate(db, { migrationsFolder: "drizzle" }) -// .then(() => { -// console.log("Migration complete"); -// sql.end(); -// }) -// .catch((error) => { -// console.log("Migration failed", error); -// }) -// .finally(() => { -// sql.end(); -// }); diff --git a/packages/server/src/db/reset.ts b/packages/server/src/db/reset.ts deleted file mode 100644 index c22291478..000000000 --- a/packages/server/src/db/reset.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { sql } from "drizzle-orm"; -// Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406 -import { drizzle } from "drizzle-orm/postgres-js"; -import postgres from "postgres"; - -const connectionString = process.env.DATABASE_URL!; - -const pg = postgres(connectionString, { max: 1 }); -const db = drizzle(pg); - -const clearDb = async (): Promise => { - try { - const tablesQuery = sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`; - const tables = await db.execute(tablesQuery); - console.log(tables); - await pg.end(); - } catch (error) { - console.error("Error cleaning database", error); - } finally { - } -}; - -clearDb(); diff --git a/packages/server/src/db/seed.ts b/packages/server/src/db/seed.ts deleted file mode 100644 index 7e2736b00..000000000 --- a/packages/server/src/db/seed.ts +++ /dev/null @@ -1,35 +0,0 @@ -// import bc from "bcrypt"; -// import { drizzle } from "drizzle-orm/postgres-js"; -// import postgres from "postgres"; -// import { users } from "./schema"; - -// const connectionString = process.env.DATABASE_URL!; - -// const pg = postgres(connectionString, { max: 1 }); -// const db = drizzle(pg); - -// function password(txt: string) { -// return bc.hashSync(txt, 10); -// } - -// async function seed() { -// console.log("> Seed:", process.env.DATABASE_PATH, "\n"); - -// // const authenticationR = await db -// // .insert(users) -// // .values([ -// // { -// // email: "user1@hotmail.com", -// // password: password("12345671"), -// // }, -// // ]) -// // .onConflictDoNothing() -// // .returning(); - -// // console.log("\nSemillas Update:", authenticationR.length); -// } - -// seed().catch((e) => { -// console.error(e); -// process.exit(1); -// }); From 8a1e36cc3b813a0066b8c73d4411ec80ed149ed6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:26:05 -0600 Subject: [PATCH 085/144] feat(settings): add user subscription check to dashboard layout --- .../components/layouts/dashboard-layout.tsx | 11 ++++++++++- apps/dokploy/server/api/routers/settings.ts | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/layouts/dashboard-layout.tsx b/apps/dokploy/components/layouts/dashboard-layout.tsx index b4832b4b3..b65e7dbb8 100644 --- a/apps/dokploy/components/layouts/dashboard-layout.tsx +++ b/apps/dokploy/components/layouts/dashboard-layout.tsx @@ -11,11 +11,20 @@ interface Props { export const DashboardLayout = ({ children }: Props) => { const { data: haveRootAccess } = api.user.haveRootAccess.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: isUserSubscribed } = api.settings.isUserSubscribed.useQuery( + undefined, + { + enabled: isCloud === true, + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + }, + ); return ( <> {children} - {isCloud === true && ( + {isCloud === true && isUserSubscribed === true && ( )} diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 7ae0b6e85..e6a15af24 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -45,7 +45,7 @@ import { } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; import { TRPCError } from "@trpc/server"; -import { sql } from "drizzle-orm"; +import { eq, sql } from "drizzle-orm"; import { dump, load } from "js-yaml"; import { scheduledJobs, scheduleJob } from "node-schedule"; import { z } from "zod"; @@ -60,6 +60,8 @@ import { apiServerSchema, apiTraefikConfig, apiUpdateDockerCleanup, + projects, + server, } from "@/server/db/schema"; import { removeJob, schedule } from "@/server/utils/backup"; import packageInfo from "../../../package.json"; @@ -706,6 +708,18 @@ export const settingsRouter = createTRPCRouter({ isCloud: publicProcedure.query(async () => { return IS_CLOUD; }), + isUserSubscribed: publicProcedure.query(async ({ ctx }) => { + const haveServers = await db.query.server.findMany({ + where: eq(server.organizationId, ctx.session?.activeOrganizationId || ""), + }); + const haveProjects = await db.query.projects.findMany({ + where: eq( + projects.organizationId, + ctx.session?.activeOrganizationId || "", + ), + }); + return haveServers.length > 0 || haveProjects.length > 0; + }), health: publicProcedure.query(async () => { if (IS_CLOUD) { try { From 44ae4df1511dd49eb601ffd442ed6f959854e22b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:27:47 -0600 Subject: [PATCH 086/144] fix(settings): change user subscription query to protected procedure --- apps/dokploy/server/api/routers/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index e6a15af24..02678b990 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -708,7 +708,7 @@ export const settingsRouter = createTRPCRouter({ isCloud: publicProcedure.query(async () => { return IS_CLOUD; }), - isUserSubscribed: publicProcedure.query(async ({ ctx }) => { + isUserSubscribed: protectedProcedure.query(async ({ ctx }) => { const haveServers = await db.query.server.findMany({ where: eq(server.organizationId, ctx.session?.activeOrganizationId || ""), }); From d922568510d8ef377cdbfde2b9e93636dd47aeb9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:42:21 -0600 Subject: [PATCH 087/144] fix(redis): return newRedis object instead of true in redis router --- apps/dokploy/server/api/routers/redis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index a403f8768..ad1ade43d 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -80,7 +80,7 @@ export const redisRouter = createTRPCRouter({ type: "volume", }); - return true; + return newRedis; } catch (error) { throw error; } From 935d1686f281a4d07cde7f07ef0c89d96f517137 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:02:21 -0600 Subject: [PATCH 088/144] chore: add new branch for database migration fix in Dokploy workflow --- .github/workflows/dokploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml index afb4ba4d2..529cd8f7f 100644 --- a/.github/workflows/dokploy.yml +++ b/.github/workflows/dokploy.yml @@ -2,7 +2,7 @@ name: Dokploy Docker Build on: push: - branches: [main, canary] + branches: [main, canary, "fix/re-apply-database-migration-fix"] workflow_dispatch: env: From d6a0585baeb9be7f8c959d8bff66a64bc009cd90 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:03:37 -0600 Subject: [PATCH 089/144] chore(package): update dokploy version to v0.25.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 b919554e8..66d9f7edd 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.12", + "version": "v0.25.0", "private": true, "license": "Apache-2.0", "type": "module", From caf244120cafdcac1fbaa735d4a5e9210e893944 Mon Sep 17 00:00:00 2001 From: Typed SIGTERM Date: Sat, 30 Aug 2025 13:41:40 +0800 Subject: [PATCH 090/144] fix: print error when docker build fails --- packages/server/src/utils/providers/docker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utils/providers/docker.ts b/packages/server/src/utils/providers/docker.ts index 88c457767..8cca44ba8 100644 --- a/packages/server/src/utils/providers/docker.ts +++ b/packages/server/src/utils/providers/docker.ts @@ -42,7 +42,7 @@ export const buildDocker = async ( await mechanizeDockerContainer(application); writeStream.write("\nDocker Deployed: ✅\n"); } catch (error) { - writeStream.write("❌ Error"); + writeStream.write("❌ ${error}"); throw error; } finally { writeStream.end(); From 468feaa092b373011ecbd101cc37b17b17064877 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Sun, 31 Aug 2025 10:25:09 +1000 Subject: [PATCH 091/144] fix(ui): improve server schedule responsiveness for mobile --- .../deployments/show-deployments.tsx | 454 +++++++++--------- .../application/schedules/show-schedules.tsx | 428 ++++++++--------- 2 files changed, 442 insertions(+), 440 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index 5efa9e5f2..1dad1cf9f 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -7,11 +7,11 @@ import { StatusTooltip } from "@/components/shared/status-tooltip"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { api, type RouterOutputs } from "@/utils/api"; import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings"; @@ -20,238 +20,240 @@ import { RefreshToken } from "./refresh-token"; import { ShowDeployment } from "./show-deployment"; interface Props { - id: string; - type: - | "application" - | "compose" - | "schedule" - | "server" - | "backup" - | "previewDeployment" - | "volumeBackup"; - refreshToken?: string; - serverId?: string; + id: string; + type: + | "application" + | "compose" + | "schedule" + | "server" + | "backup" + | "previewDeployment" + | "volumeBackup"; + refreshToken?: string; + serverId?: string; } export const formatDuration = (seconds: number) => { - if (seconds < 60) return `${seconds}s`; - const minutes = Math.floor(seconds / 60); - const remainingSeconds = seconds % 60; - return `${minutes}m ${remainingSeconds}s`; + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `${minutes}m ${remainingSeconds}s`; }; export const ShowDeployments = ({ - id, - type, - refreshToken, - serverId, + id, + type, + refreshToken, + serverId, }: Props) => { - const [activeLog, setActiveLog] = useState< - RouterOutputs["deployment"]["all"][number] | null - >(null); - const { data: deployments, isLoading: isLoadingDeployments } = - api.deployment.allByType.useQuery( - { - id, - type, - }, - { - enabled: !!id, - refetchInterval: 1000, - }, - ); + const [activeLog, setActiveLog] = useState< + RouterOutputs["deployment"]["all"][number] | null + >(null); + const { data: deployments, isLoading: isLoadingDeployments } = + api.deployment.allByType.useQuery( + { + id, + type, + }, + { + enabled: !!id, + refetchInterval: 1000, + } + ); - const { mutateAsync: rollback, isLoading: isRollingBack } = - api.rollback.rollback.useMutation(); - const { mutateAsync: killProcess, isLoading: isKillingProcess } = - api.deployment.killProcess.useMutation(); + const { mutateAsync: rollback, isLoading: isRollingBack } = + api.rollback.rollback.useMutation(); + const { mutateAsync: killProcess, isLoading: isKillingProcess } = + api.deployment.killProcess.useMutation(); - const [url, setUrl] = React.useState(""); - useEffect(() => { - setUrl(document.location.origin); - }, []); + const [url, setUrl] = React.useState(""); + useEffect(() => { + setUrl(document.location.origin); + }, []); - return ( - - -
- Deployments - - See all the 10 last deployments for this {type} - -
-
- {(type === "application" || type === "compose") && ( - - )} - {type === "application" && ( - - - - )} -
-
- - {refreshToken && ( -
- - If you want to re-deploy this application use this URL in the - config of your git provider or docker - -
- Webhook URL: -
- - {`${url}/api/deploy${type === "compose" ? "/compose" : ""}/${refreshToken}`} - - {(type === "application" || type === "compose") && ( - - )} -
-
-
- )} + return ( + + +
+ Deployments + + See the last 10 deployments for this {type} + +
+
+ {(type === "application" || type === "compose") && ( + + )} + {type === "application" && ( + + + + )} +
+
+ + {refreshToken && ( +
+ + If you want to re-deploy this application use this URL in the + config of your git provider or docker + +
+ Webhook URL: +
+ + {`${url}/api/deploy${ + type === "compose" ? "/compose" : "" + }/${refreshToken}`} + + {(type === "application" || type === "compose") && ( + + )} +
+
+
+ )} - {isLoadingDeployments ? ( -
- - - Loading deployments... - -
- ) : deployments?.length === 0 ? ( -
- - - No deployments found - -
- ) : ( -
- {deployments?.map((deployment, index) => ( -
-
- - {index + 1}. {deployment.status} - - - - {deployment.title} - - {deployment.description && ( - - {deployment.description} - - )} -
-
-
- - {deployment.startedAt && deployment.finishedAt && ( - - - {formatDuration( - Math.floor( - (new Date(deployment.finishedAt).getTime() - - new Date(deployment.startedAt).getTime()) / - 1000, - ), - )} - - )} -
+ {isLoadingDeployments ? ( +
+ + + Loading deployments... + +
+ ) : deployments?.length === 0 ? ( +
+ + + No deployments found + +
+ ) : ( +
+ {deployments?.map((deployment, index) => ( +
+
+ + {index + 1}. {deployment.status} + + + + {deployment.title} + + {deployment.description && ( + + {deployment.description} + + )} +
+
+
+ + {deployment.startedAt && deployment.finishedAt && ( + + + {formatDuration( + Math.floor( + (new Date(deployment.finishedAt).getTime() - + new Date(deployment.startedAt).getTime()) / + 1000 + ) + )} + + )} +
-
- {deployment.pid && deployment.status === "running" && ( - { - await killProcess({ - deploymentId: deployment.deploymentId, - }) - .then(() => { - toast.success("Process killed successfully"); - }) - .catch(() => { - toast.error("Error killing process"); - }); - }} - > - - - )} - +
+ {deployment.pid && deployment.status === "running" && ( + { + await killProcess({ + deploymentId: deployment.deploymentId, + }) + .then(() => { + toast.success("Process killed successfully"); + }) + .catch(() => { + toast.error("Error killing process"); + }); + }} + > + + + )} + - {deployment?.rollback && - deployment.status === "done" && - type === "application" && ( - { - await rollback({ - rollbackId: deployment.rollback.rollbackId, - }) - .then(() => { - toast.success( - "Rollback initiated successfully", - ); - }) - .catch(() => { - toast.error("Error initiating rollback"); - }); - }} - > - - - )} -
-
-
- ))} -
- )} - setActiveLog(null)} - logPath={activeLog?.logPath || ""} - errorMessage={activeLog?.errorMessage || ""} - /> - - - ); + {deployment?.rollback && + deployment.status === "done" && + type === "application" && ( + { + await rollback({ + rollbackId: deployment.rollback.rollbackId, + }) + .then(() => { + toast.success( + "Rollback initiated successfully" + ); + }) + .catch(() => { + toast.error("Error initiating rollback"); + }); + }} + > + + + )} +
+
+
+ ))} +
+ )} + setActiveLog(null)} + logPath={activeLog?.logPath || ""} + errorMessage={activeLog?.errorMessage || ""} + /> +
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index 3ebc76def..339815343 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -1,243 +1,243 @@ import { - ClipboardList, - Clock, - Loader2, - Play, - Terminal, - Trash2, + ClipboardList, + Clock, + Loader2, + Play, + Terminal, + Trash2, } from "lucide-react"; import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; import { HandleSchedules } from "./handle-schedules"; interface Props { - id: string; - scheduleType?: "application" | "compose" | "server" | "dokploy-server"; + id: string; + scheduleType?: "application" | "compose" | "server" | "dokploy-server"; } export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { - const { - data: schedules, - isLoading: isLoadingSchedules, - refetch: refetchSchedules, - } = api.schedule.list.useQuery( - { - id: id || "", - scheduleType, - }, - { - enabled: !!id, - }, - ); + const { + data: schedules, + isLoading: isLoadingSchedules, + refetch: refetchSchedules, + } = api.schedule.list.useQuery( + { + id: id || "", + scheduleType, + }, + { + enabled: !!id, + } + ); - const utils = api.useUtils(); + const utils = api.useUtils(); - const { mutateAsync: deleteSchedule, isLoading: isDeleting } = - api.schedule.delete.useMutation(); + const { mutateAsync: deleteSchedule, isLoading: isDeleting } = + api.schedule.delete.useMutation(); - const { mutateAsync: runManually, isLoading } = - api.schedule.runManually.useMutation(); + const { mutateAsync: runManually, isLoading } = + api.schedule.runManually.useMutation(); - return ( - - -
-
- - Scheduled Tasks - - - Schedule tasks to run automatically at specified intervals. - -
+ return ( + + +
+
+ + Scheduled Tasks + + + Schedule tasks to run automatically at specified intervals. + +
- {schedules && schedules.length > 0 && ( - - )} -
-
- - {isLoadingSchedules ? ( -
- - - Loading scheduled tasks... - -
- ) : schedules && schedules.length > 0 ? ( -
- {schedules.map((schedule) => { - const serverId = - schedule.serverId || - schedule.application?.serverId || - schedule.compose?.serverId; - return ( -
-
-
- -
-
-
-

- {schedule.name} -

- - {schedule.enabled ? "Enabled" : "Disabled"} - -
-
- - Cron: {schedule.cronExpression} - - {schedule.scheduleType !== "server" && - schedule.scheduleType !== "dokploy-server" && ( - <> - - • - - - {schedule.shellType} - - - )} -
- {schedule.command && ( -
- - - {schedule.command} - -
- )} -
-
+ {schedules && schedules.length > 0 && ( + + )} +
+ + + {isLoadingSchedules ? ( +
+ + + Loading scheduled tasks... + +
+ ) : schedules && schedules.length > 0 ? ( +
+ {schedules.map((schedule) => { + const serverId = + schedule.serverId || + schedule.application?.serverId || + schedule.compose?.serverId; + return ( +
+
+
+ +
+
+
+

+ {schedule.name} +

+ + {schedule.enabled ? "Enabled" : "Disabled"} + +
+
+ + Cron: {schedule.cronExpression} + + {schedule.scheduleType !== "server" && + schedule.scheduleType !== "dokploy-server" && ( + <> + + • + + + {schedule.shellType} + + + )} +
+ {schedule.command && ( +
+ + + {schedule.command} + +
+ )} +
+
-
- - - +
+ + + - - - - - - Run Manual Schedule - - + await runManually({ + scheduleId: schedule.scheduleId, + }) + .then(async () => { + await new Promise((resolve) => + setTimeout(resolve, 1500) + ); + refetchSchedules(); + }) + .catch(() => { + toast.error("Error running schedule"); + }); + }} + > + + + + Run Manual Schedule + + - + - { - await deleteSchedule({ - scheduleId: schedule.scheduleId, - }) - .then(() => { - utils.schedule.list.invalidate({ - id, - scheduleType, - }); - toast.success("Schedule deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting schedule"); - }); - }} - > - - -
-
- ); - })} -
- ) : ( -
- -

- No scheduled tasks -

-

- Create your first scheduled task to automate your workflows -

- -
- )} - - - ); + { + await deleteSchedule({ + scheduleId: schedule.scheduleId, + }) + .then(() => { + utils.schedule.list.invalidate({ + id, + scheduleType, + }); + toast.success("Schedule deleted successfully"); + }) + .catch(() => { + toast.error("Error deleting schedule"); + }); + }} + > + + +
+
+ ); + })} +
+ ) : ( +
+ +

+ No scheduled tasks +

+

+ Create your first scheduled task to automate your workflows +

+ +
+ )} +
+
+ ); }; From a2841fdd30dce6f61c85513c3cad07e660a3a978 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Sun, 31 Aug 2025 10:27:12 +1000 Subject: [PATCH 092/144] fix(ui): flex-wrap for cron and shell type --- .../dashboard/application/schedules/show-schedules.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index 339815343..d235f6bc5 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -109,7 +109,7 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { {schedule.enabled ? "Enabled" : "Disabled"}
-
+
Date: Sun, 31 Aug 2025 00:30:08 +0000 Subject: [PATCH 093/144] [autofix.ci] apply automated fixes --- .../deployments/show-deployments.tsx | 456 +++++++++--------- .../application/schedules/show-schedules.tsx | 428 ++++++++-------- 2 files changed, 442 insertions(+), 442 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index 1dad1cf9f..13694a283 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -7,11 +7,11 @@ import { StatusTooltip } from "@/components/shared/status-tooltip"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { api, type RouterOutputs } from "@/utils/api"; import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings"; @@ -20,240 +20,240 @@ import { RefreshToken } from "./refresh-token"; import { ShowDeployment } from "./show-deployment"; interface Props { - id: string; - type: - | "application" - | "compose" - | "schedule" - | "server" - | "backup" - | "previewDeployment" - | "volumeBackup"; - refreshToken?: string; - serverId?: string; + id: string; + type: + | "application" + | "compose" + | "schedule" + | "server" + | "backup" + | "previewDeployment" + | "volumeBackup"; + refreshToken?: string; + serverId?: string; } export const formatDuration = (seconds: number) => { - if (seconds < 60) return `${seconds}s`; - const minutes = Math.floor(seconds / 60); - const remainingSeconds = seconds % 60; - return `${minutes}m ${remainingSeconds}s`; + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `${minutes}m ${remainingSeconds}s`; }; export const ShowDeployments = ({ - id, - type, - refreshToken, - serverId, + id, + type, + refreshToken, + serverId, }: Props) => { - const [activeLog, setActiveLog] = useState< - RouterOutputs["deployment"]["all"][number] | null - >(null); - const { data: deployments, isLoading: isLoadingDeployments } = - api.deployment.allByType.useQuery( - { - id, - type, - }, - { - enabled: !!id, - refetchInterval: 1000, - } - ); + const [activeLog, setActiveLog] = useState< + RouterOutputs["deployment"]["all"][number] | null + >(null); + const { data: deployments, isLoading: isLoadingDeployments } = + api.deployment.allByType.useQuery( + { + id, + type, + }, + { + enabled: !!id, + refetchInterval: 1000, + }, + ); - const { mutateAsync: rollback, isLoading: isRollingBack } = - api.rollback.rollback.useMutation(); - const { mutateAsync: killProcess, isLoading: isKillingProcess } = - api.deployment.killProcess.useMutation(); + const { mutateAsync: rollback, isLoading: isRollingBack } = + api.rollback.rollback.useMutation(); + const { mutateAsync: killProcess, isLoading: isKillingProcess } = + api.deployment.killProcess.useMutation(); - const [url, setUrl] = React.useState(""); - useEffect(() => { - setUrl(document.location.origin); - }, []); + const [url, setUrl] = React.useState(""); + useEffect(() => { + setUrl(document.location.origin); + }, []); - return ( - - -
- Deployments - - See the last 10 deployments for this {type} - -
-
- {(type === "application" || type === "compose") && ( - - )} - {type === "application" && ( - - - - )} -
-
- - {refreshToken && ( -
- - If you want to re-deploy this application use this URL in the - config of your git provider or docker - -
- Webhook URL: -
- - {`${url}/api/deploy${ - type === "compose" ? "/compose" : "" - }/${refreshToken}`} - - {(type === "application" || type === "compose") && ( - - )} -
-
-
- )} + return ( + + +
+ Deployments + + See the last 10 deployments for this {type} + +
+
+ {(type === "application" || type === "compose") && ( + + )} + {type === "application" && ( + + + + )} +
+
+ + {refreshToken && ( +
+ + If you want to re-deploy this application use this URL in the + config of your git provider or docker + +
+ Webhook URL: +
+ + {`${url}/api/deploy${ + type === "compose" ? "/compose" : "" + }/${refreshToken}`} + + {(type === "application" || type === "compose") && ( + + )} +
+
+
+ )} - {isLoadingDeployments ? ( -
- - - Loading deployments... - -
- ) : deployments?.length === 0 ? ( -
- - - No deployments found - -
- ) : ( -
- {deployments?.map((deployment, index) => ( -
-
- - {index + 1}. {deployment.status} - - - - {deployment.title} - - {deployment.description && ( - - {deployment.description} - - )} -
-
-
- - {deployment.startedAt && deployment.finishedAt && ( - - - {formatDuration( - Math.floor( - (new Date(deployment.finishedAt).getTime() - - new Date(deployment.startedAt).getTime()) / - 1000 - ) - )} - - )} -
+ {isLoadingDeployments ? ( +
+ + + Loading deployments... + +
+ ) : deployments?.length === 0 ? ( +
+ + + No deployments found + +
+ ) : ( +
+ {deployments?.map((deployment, index) => ( +
+
+ + {index + 1}. {deployment.status} + + + + {deployment.title} + + {deployment.description && ( + + {deployment.description} + + )} +
+
+
+ + {deployment.startedAt && deployment.finishedAt && ( + + + {formatDuration( + Math.floor( + (new Date(deployment.finishedAt).getTime() - + new Date(deployment.startedAt).getTime()) / + 1000, + ), + )} + + )} +
-
- {deployment.pid && deployment.status === "running" && ( - { - await killProcess({ - deploymentId: deployment.deploymentId, - }) - .then(() => { - toast.success("Process killed successfully"); - }) - .catch(() => { - toast.error("Error killing process"); - }); - }} - > - - - )} - +
+ {deployment.pid && deployment.status === "running" && ( + { + await killProcess({ + deploymentId: deployment.deploymentId, + }) + .then(() => { + toast.success("Process killed successfully"); + }) + .catch(() => { + toast.error("Error killing process"); + }); + }} + > + + + )} + - {deployment?.rollback && - deployment.status === "done" && - type === "application" && ( - { - await rollback({ - rollbackId: deployment.rollback.rollbackId, - }) - .then(() => { - toast.success( - "Rollback initiated successfully" - ); - }) - .catch(() => { - toast.error("Error initiating rollback"); - }); - }} - > - - - )} -
-
-
- ))} -
- )} - setActiveLog(null)} - logPath={activeLog?.logPath || ""} - errorMessage={activeLog?.errorMessage || ""} - /> - - - ); + {deployment?.rollback && + deployment.status === "done" && + type === "application" && ( + { + await rollback({ + rollbackId: deployment.rollback.rollbackId, + }) + .then(() => { + toast.success( + "Rollback initiated successfully", + ); + }) + .catch(() => { + toast.error("Error initiating rollback"); + }); + }} + > + + + )} +
+
+
+ ))} +
+ )} + setActiveLog(null)} + logPath={activeLog?.logPath || ""} + errorMessage={activeLog?.errorMessage || ""} + /> +
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index d235f6bc5..6369b7fdf 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -1,243 +1,243 @@ import { - ClipboardList, - Clock, - Loader2, - Play, - Terminal, - Trash2, + ClipboardList, + Clock, + Loader2, + Play, + Terminal, + Trash2, } from "lucide-react"; import { toast } from "sonner"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { ShowDeploymentsModal } from "../deployments/show-deployments-modal"; import { HandleSchedules } from "./handle-schedules"; interface Props { - id: string; - scheduleType?: "application" | "compose" | "server" | "dokploy-server"; + id: string; + scheduleType?: "application" | "compose" | "server" | "dokploy-server"; } export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { - const { - data: schedules, - isLoading: isLoadingSchedules, - refetch: refetchSchedules, - } = api.schedule.list.useQuery( - { - id: id || "", - scheduleType, - }, - { - enabled: !!id, - } - ); + const { + data: schedules, + isLoading: isLoadingSchedules, + refetch: refetchSchedules, + } = api.schedule.list.useQuery( + { + id: id || "", + scheduleType, + }, + { + enabled: !!id, + }, + ); - const utils = api.useUtils(); + const utils = api.useUtils(); - const { mutateAsync: deleteSchedule, isLoading: isDeleting } = - api.schedule.delete.useMutation(); + const { mutateAsync: deleteSchedule, isLoading: isDeleting } = + api.schedule.delete.useMutation(); - const { mutateAsync: runManually, isLoading } = - api.schedule.runManually.useMutation(); + const { mutateAsync: runManually, isLoading } = + api.schedule.runManually.useMutation(); - return ( - - -
-
- - Scheduled Tasks - - - Schedule tasks to run automatically at specified intervals. - -
+ return ( + + +
+
+ + Scheduled Tasks + + + Schedule tasks to run automatically at specified intervals. + +
- {schedules && schedules.length > 0 && ( - - )} -
-
- - {isLoadingSchedules ? ( -
- - - Loading scheduled tasks... - -
- ) : schedules && schedules.length > 0 ? ( -
- {schedules.map((schedule) => { - const serverId = - schedule.serverId || - schedule.application?.serverId || - schedule.compose?.serverId; - return ( -
-
-
- -
-
-
-

- {schedule.name} -

- - {schedule.enabled ? "Enabled" : "Disabled"} - -
-
- - Cron: {schedule.cronExpression} - - {schedule.scheduleType !== "server" && - schedule.scheduleType !== "dokploy-server" && ( - <> - - • - - - {schedule.shellType} - - - )} -
- {schedule.command && ( -
- - - {schedule.command} - -
- )} -
-
+ {schedules && schedules.length > 0 && ( + + )} +
+ + + {isLoadingSchedules ? ( +
+ + + Loading scheduled tasks... + +
+ ) : schedules && schedules.length > 0 ? ( +
+ {schedules.map((schedule) => { + const serverId = + schedule.serverId || + schedule.application?.serverId || + schedule.compose?.serverId; + return ( +
+
+
+ +
+
+
+

+ {schedule.name} +

+ + {schedule.enabled ? "Enabled" : "Disabled"} + +
+
+ + Cron: {schedule.cronExpression} + + {schedule.scheduleType !== "server" && + schedule.scheduleType !== "dokploy-server" && ( + <> + + • + + + {schedule.shellType} + + + )} +
+ {schedule.command && ( +
+ + + {schedule.command} + +
+ )} +
+
-
- - - +
+ + + - - - - - - Run Manual Schedule - - + await runManually({ + scheduleId: schedule.scheduleId, + }) + .then(async () => { + await new Promise((resolve) => + setTimeout(resolve, 1500), + ); + refetchSchedules(); + }) + .catch(() => { + toast.error("Error running schedule"); + }); + }} + > + + + + Run Manual Schedule + + - + - { - await deleteSchedule({ - scheduleId: schedule.scheduleId, - }) - .then(() => { - utils.schedule.list.invalidate({ - id, - scheduleType, - }); - toast.success("Schedule deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting schedule"); - }); - }} - > - - -
-
- ); - })} -
- ) : ( -
- -

- No scheduled tasks -

-

- Create your first scheduled task to automate your workflows -

- -
- )} - - - ); + { + await deleteSchedule({ + scheduleId: schedule.scheduleId, + }) + .then(() => { + utils.schedule.list.invalidate({ + id, + scheduleType, + }); + toast.success("Schedule deleted successfully"); + }) + .catch(() => { + toast.error("Error deleting schedule"); + }); + }} + > + + +
+
+ ); + })} +
+ ) : ( +
+ +

+ No scheduled tasks +

+

+ Create your first scheduled task to automate your workflows +

+ +
+ )} +
+
+ ); }; From 38abe032574c1e5468d588c9e0fa35283b96e045 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Sun, 31 Aug 2025 10:36:07 +1000 Subject: [PATCH 094/144] fix(ui): flex-wrap on schedule name and enabled --- .../dashboard/application/schedules/show-schedules.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx index 6369b7fdf..3209b6e03 100644 --- a/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx +++ b/apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx @@ -98,7 +98,7 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
-
+

{schedule.name}

@@ -226,7 +226,7 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { })}
) : ( -
+

No scheduled tasks From bc2b4f13695ca690309c09948de8f5abd414defe Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:16:55 -0600 Subject: [PATCH 095/144] feat(database): enhance password validation for database schemas and update input components for password visibility --- .../dashboard/project/add-database.tsx | 22 ++++++++-- .../shared/toggle-visibility-input.tsx | 19 +++------ apps/dokploy/components/ui/input.tsx | 40 ++++++++++++++----- packages/server/src/db/schema/mariadb.ts | 16 +++++++- packages/server/src/db/schema/mongo.ts | 8 +++- packages/server/src/db/schema/mysql.ts | 16 +++++++- packages/server/src/db/schema/postgres.ts | 8 +++- 7 files changed, 97 insertions(+), 32 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index 6b07baa81..104413908 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -83,7 +83,13 @@ const baseDatabaseSchema = z.object({ message: "App name supports lowercase letters, numbers, '-' and can only start and end letters, and does not support continuous '-'", }), - databasePassword: z.string(), + databasePassword: z + .string() + .min(1, "Password is required") + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), dockerImage: z.string(), description: z.string().nullable(), serverId: z.string().nullable(), @@ -112,7 +118,12 @@ const mySchema = z.discriminatedUnion("type", [ z .object({ type: z.literal("mysql"), - databaseRootPassword: z.string().default(""), + databaseRootPassword: z + .string() + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), databaseUser: z.string().default("mysql"), databaseName: z.string().default("mysql"), }) @@ -121,7 +132,12 @@ const mySchema = z.discriminatedUnion("type", [ .object({ type: z.literal("mariadb"), dockerImage: z.string().default("mariadb:4"), - databaseRootPassword: z.string().default(""), + databaseRootPassword: z + .string() + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), databaseUser: z.string().default("mariadb"), databaseName: z.string().default("mariadb"), }) diff --git a/apps/dokploy/components/shared/toggle-visibility-input.tsx b/apps/dokploy/components/shared/toggle-visibility-input.tsx index 1a9817379..aea173fbc 100644 --- a/apps/dokploy/components/shared/toggle-visibility-input.tsx +++ b/apps/dokploy/components/shared/toggle-visibility-input.tsx @@ -1,25 +1,16 @@ import copy from "copy-to-clipboard"; -import { Clipboard, EyeIcon, EyeOffIcon } from "lucide-react"; -import { useRef, useState } from "react"; +import { Clipboard } from "lucide-react"; +import { useRef } from "react"; import { toast } from "sonner"; import { Button } from "../ui/button"; import { Input, type InputProps } from "../ui/input"; export const ToggleVisibilityInput = ({ ...props }: InputProps) => { - const [isPasswordVisible, setIsPasswordVisible] = useState(false); const inputRef = useRef(null); - const togglePasswordVisibility = () => { - setIsPasswordVisible((prevVisibility) => !prevVisibility); - }; - return (

- + - + */}
); }; diff --git a/apps/dokploy/components/ui/input.tsx b/apps/dokploy/components/ui/input.tsx index df3ca4d07..87afd64e5 100644 --- a/apps/dokploy/components/ui/input.tsx +++ b/apps/dokploy/components/ui/input.tsx @@ -1,3 +1,4 @@ +import { EyeIcon, EyeOffIcon } from "lucide-react"; import * as React from "react"; import { cn } from "@/lib/utils"; @@ -8,18 +9,39 @@ export interface InputProps const Input = React.forwardRef( ({ className, errorMessage, type, ...props }, ref) => { + const [showPassword, setShowPassword] = React.useState(false); + const isPassword = type === "password"; + const inputType = isPassword ? (showPassword ? "text" : "password") : type; + return ( <> - + + {isPassword && ( + )} - ref={ref} - {...props} - /> +
{errorMessage && ( {errorMessage} diff --git a/packages/server/src/db/schema/mariadb.ts b/packages/server/src/db/schema/mariadb.ts index 039836d77..83fa94f14 100644 --- a/packages/server/src/db/schema/mariadb.ts +++ b/packages/server/src/db/schema/mariadb.ts @@ -94,8 +94,20 @@ const createSchema = createInsertSchema(mariadb, { createdAt: z.string(), databaseName: z.string().min(1), databaseUser: z.string().min(1), - databasePassword: z.string(), - databaseRootPassword: z.string().optional(), + databasePassword: z + .string() + .min(1, "Password is required") + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), + databaseRootPassword: z + .string() + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }) + .optional(), dockerImage: z.string().default("mariadb:6"), command: z.string().optional(), env: z.string().optional(), diff --git a/packages/server/src/db/schema/mongo.ts b/packages/server/src/db/schema/mongo.ts index eb6103a36..fed414bf6 100644 --- a/packages/server/src/db/schema/mongo.ts +++ b/packages/server/src/db/schema/mongo.ts @@ -89,7 +89,13 @@ const createSchema = createInsertSchema(mongo, { createdAt: z.string(), mongoId: z.string(), name: z.string().min(1), - databasePassword: z.string(), + databasePassword: z + .string() + .min(1, "Password is required") + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), databaseUser: z.string().min(1), dockerImage: z.string().default("mongo:15"), command: z.string().optional(), diff --git a/packages/server/src/db/schema/mysql.ts b/packages/server/src/db/schema/mysql.ts index 03d360b3d..361d5685d 100644 --- a/packages/server/src/db/schema/mysql.ts +++ b/packages/server/src/db/schema/mysql.ts @@ -92,8 +92,20 @@ const createSchema = createInsertSchema(mysql, { name: z.string().min(1), databaseName: z.string().min(1), databaseUser: z.string().min(1), - databasePassword: z.string(), - databaseRootPassword: z.string().optional(), + databasePassword: z + .string() + .min(1, "Password is required") + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), + databaseRootPassword: z + .string() + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }) + .optional(), dockerImage: z.string().default("mysql:8"), command: z.string().optional(), env: z.string().optional(), diff --git a/packages/server/src/db/schema/postgres.ts b/packages/server/src/db/schema/postgres.ts index df0202094..66a58d5c6 100644 --- a/packages/server/src/db/schema/postgres.ts +++ b/packages/server/src/db/schema/postgres.ts @@ -88,7 +88,13 @@ export const postgresRelations = relations(postgres, ({ one, many }) => ({ const createSchema = createInsertSchema(postgres, { postgresId: z.string(), name: z.string().min(1), - databasePassword: z.string(), + databasePassword: z + .string() + .min(1, "Password is required") + .regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, { + message: + "Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility", + }), databaseName: z.string().min(1), databaseUser: z.string().min(1), dockerImage: z.string().default("postgres:15"), From 5e1a164a54d43b7d6359258658e9f8e88f31a457 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:19:24 -0600 Subject: [PATCH 096/144] chore(pr-template): streamline checklist formatting and clarify issue closing instructions --- .github/pull_request_template.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 58825f900..0b849afc0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,16 +6,13 @@ Please describe in a short paragraph what this PR is about. Before submitting this PR, please make sure that: -- [ ] You created a dedicated branch based on the `canary` branch. -- [ ] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request -- [ ] You have tested this PR in your local instance. +- [] You created a dedicated branch based on the `canary` branch. +- [] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request +- [] You have tested this PR in your local instance. ## Issues related (if applicable) -Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER`, `fixes #ISSUE_NUMBER`, `resolves #ISSUE_NUMBER` - -Example: `closes #123` +closes #123 ## Screenshots (if applicable) -If you include a video or screenshot, would be awesome so we can see the changes in action. \ No newline at end of file From 6fc325fe95a7f29979e740e481bc830675e2cd88 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:36:27 -0600 Subject: [PATCH 097/144] feat(environment): implement environment management with create, duplicate, and delete functionalities; add environment schema and database migrations --- .../project/environment-management.tsx | 387 + .../dokploy/drizzle/0107_charming_chimera.sql | 26 + .../drizzle/0108_keen_doctor_faustus.sql | 111 + apps/dokploy/drizzle/meta/0107_snapshot.json | 6481 ++++++++++++++++ apps/dokploy/drizzle/meta/0108_snapshot.json | 6595 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 14 + .../pages/dashboard/project/[projectId].tsx | 5 +- apps/dokploy/server/api/root.ts | 2 + .../dokploy/server/api/routers/environment.ts | 113 + apps/dokploy/server/api/routers/project.ts | 144 +- packages/server/src/db/schema/application.ts | 10 + packages/server/src/db/schema/compose.ts | 11 + packages/server/src/db/schema/environment.ts | 84 + packages/server/src/db/schema/index.ts | 1 + packages/server/src/db/schema/mariadb.ts | 10 + packages/server/src/db/schema/mongo.ts | 10 + packages/server/src/db/schema/mysql.ts | 9 + packages/server/src/db/schema/postgres.ts | 10 + packages/server/src/db/schema/project.ts | 16 +- packages/server/src/db/schema/redis.ts | 10 + packages/server/src/index.ts | 1 + packages/server/src/services/environment.ts | 113 + packages/server/src/services/project.ts | 27 +- 23 files changed, 14108 insertions(+), 82 deletions(-) create mode 100644 apps/dokploy/components/dashboard/project/environment-management.tsx create mode 100644 apps/dokploy/drizzle/0107_charming_chimera.sql create mode 100644 apps/dokploy/drizzle/0108_keen_doctor_faustus.sql create mode 100644 apps/dokploy/drizzle/meta/0107_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0108_snapshot.json create mode 100644 apps/dokploy/server/api/routers/environment.ts create mode 100644 packages/server/src/db/schema/environment.ts create mode 100644 packages/server/src/services/environment.ts diff --git a/apps/dokploy/components/dashboard/project/environment-management.tsx b/apps/dokploy/components/dashboard/project/environment-management.tsx new file mode 100644 index 000000000..929d1f3e1 --- /dev/null +++ b/apps/dokploy/components/dashboard/project/environment-management.tsx @@ -0,0 +1,387 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { Copy, Plus, Settings, Trash2 } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { DateTooltip } from "@/components/shared/date-tooltip"; +import { DialogAction } from "@/components/shared/dialog-action"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { api } from "@/utils/api"; + +const createEnvironmentSchema = z.object({ + name: z.string().min(1, "Environment name is required"), + description: z.string().optional(), +}); + +const duplicateEnvironmentSchema = z.object({ + name: z.string().min(1, "Environment name is required"), + description: z.string().optional(), +}); + +type CreateEnvironment = z.infer; +type DuplicateEnvironment = z.infer; + +interface Props { + projectId: string; + children?: React.ReactNode; +} + +export const EnvironmentManagement = ({ projectId, children }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); + const [isDuplicateDialogOpen, setIsDuplicateDialogOpen] = useState(false); + const [selectedEnvironmentId, setSelectedEnvironmentId] = + useState(""); + + const utils = api.useUtils(); + + // Queries + const { data: environments, isLoading: environmentsLoading } = + api.environment.byProjectId.useQuery( + { projectId }, + { enabled: !!projectId }, + ); + + // Mutations + const createEnvironmentMutation = api.environment.create.useMutation(); + const duplicateEnvironmentMutation = api.environment.duplicate.useMutation(); + const deleteEnvironmentMutation = api.environment.remove.useMutation(); + + // Forms + const createForm = useForm({ + defaultValues: { + name: "", + description: "", + }, + resolver: zodResolver(createEnvironmentSchema), + }); + + const duplicateForm = useForm({ + defaultValues: { + name: "", + description: "", + }, + resolver: zodResolver(duplicateEnvironmentSchema), + }); + + const onCreateSubmit = async (formData: CreateEnvironment) => { + try { + await createEnvironmentMutation.mutateAsync({ + ...formData, + projectId, + }); + toast.success("Environment created successfully"); + utils.environment.byProjectId.invalidate({ projectId }); + setIsCreateDialogOpen(false); + createForm.reset(); + } catch (error) { + toast.error("Error creating environment"); + } + }; + + const onDuplicateSubmit = async (formData: DuplicateEnvironment) => { + if (!selectedEnvironmentId) return; + + try { + await duplicateEnvironmentMutation.mutateAsync({ + environmentId: selectedEnvironmentId, + name: formData.name, + description: formData.description, + }); + toast.success("Environment duplicated successfully"); + utils.environment.byProjectId.invalidate({ projectId }); + setIsDuplicateDialogOpen(false); + duplicateForm.reset(); + setSelectedEnvironmentId(""); + } catch (error) { + toast.error("Error duplicating environment"); + } + }; + + const handleDeleteEnvironment = async (environmentId: string) => { + try { + await deleteEnvironmentMutation.mutateAsync({ environmentId }); + toast.success("Environment deleted successfully"); + utils.environment.byProjectId.invalidate({ projectId }); + } catch (error) { + toast.error("Error deleting environment"); + } + }; + + const handleDuplicateClick = ( + environmentId: string, + environmentName: string, + ) => { + setSelectedEnvironmentId(environmentId); + duplicateForm.setValue("name", `${environmentName} (copy)`); + setIsDuplicateDialogOpen(true); + }; + + return ( + <> + + + {children ?? ( + + )} + + + + Environment Management + + Manage project environments. Each environment can have its own + configuration and settings. + + + +
+
+

Project Environments

+ +
+ + {environmentsLoading ? ( +
+
+ Loading environments... +
+
+ ) : environments && environments.length > 0 ? ( +
+ {environments.map((env) => ( +
+
+
+

{env.name}

+ {env.name === "production" && ( + Default + )} +
+ {env.description && ( +

+ {env.description} +

+ )} + + + Created + + +
+
+ + {env.name !== "production" && ( + + handleDeleteEnvironment(env.environmentId) + } + > + + + )} +
+
+ ))} +
+ ) : ( +
+
+ No environments found. The production environment should be + created automatically. +
+
+ )} +
+
+
+ + {/* Create Environment Dialog */} + + + + Create New Environment + + Create a new environment for this project. + + + +
+ + ( + + Environment Name + + + + + + )} + /> + + ( + + Description (Optional) + +