From 628f16e8cb5683bd29e0ba18fdb883808f29d0ae Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Tue, 3 Mar 2026 15:34:56 -0600 Subject: [PATCH 001/261] fix: update import statements to include file extensions for consistency --- apps/api/src/service.ts | 2 +- apps/api/src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/service.ts b/apps/api/src/service.ts index 495e30ca3..414ee7d9d 100644 --- a/apps/api/src/service.ts +++ b/apps/api/src/service.ts @@ -1,4 +1,4 @@ -import { logger } from "./logger"; +import { logger } from "./logger.js"; const baseUrl = process.env.INNGEST_BASE_URL ?? ""; const signingKey = process.env.INNGEST_SIGNING_KEY ?? ""; diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index 13708006a..b99ec492a 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -9,7 +9,7 @@ import { updateCompose, updatePreviewDeployment, } from "@dokploy/server"; -import type { DeployJob } from "./schema"; +import type { DeployJob } from "./schema.js"; export const deploy = async (job: DeployJob) => { try { From 91ebf3b6f5937c72970ab1c3627880d01b3b3af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Ors=C3=A1g?= Date: Fri, 3 Apr 2026 01:09:28 +0200 Subject: [PATCH 002/261] fix: upgrade ssh2 from 1.15.0 to ^1.16.0 (util.isDate removed in Node.js v23+) --- packages/server/package.json | 2 +- pnpm-lock.yaml | 75 +++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 7fe2eaba0..ad7d8d48d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -80,7 +80,7 @@ "semver": "7.7.3", "shell-quote": "^1.8.1", "slugify": "^1.6.6", - "ssh2": "1.15.0", + "ssh2": "^1.16.0", "toml": "3.0.0", "ws": "8.16.0", "yaml": "2.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb61d48e3..18bcc7f2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,10 +115,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))(better-call@2.0.2(zod@4.3.6)) '@codemirror/autocomplete': specifier: ^6.18.6 version: 6.20.0 @@ -277,7 +277,7 @@ importers: version: 5.1.1 better-auth: specifier: 1.5.4 - version: 1.5.4(febde88eaf587188179e6ecc47119e50) + version: 1.5.4(219be630f7f6fef2e235cef94eaddc25) bl: specifier: 6.0.11 version: 6.0.11 @@ -527,10 +527,10 @@ importers: version: 5.9.3 vite-tsconfig-paths: specifier: 4.3.2 - version: 4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)) + version: 4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) apps/schedules: dependencies: @@ -618,10 +618,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))(better-call@2.0.2(zod@4.3.6)) '@better-auth/utils': specifier: 0.3.1 version: 0.3.1 @@ -660,7 +660,7 @@ importers: version: 5.1.1 better-auth: specifier: 1.5.4 - version: 1.5.4(334901c35c1fcda64bb596793b2e4934) + version: 1.5.4(8933545d763d3f096150f97f9213a424) better-call: specifier: 2.0.2 version: 2.0.2(zod@4.3.6) @@ -746,8 +746,8 @@ importers: specifier: ^1.6.6 version: 1.6.6 ssh2: - specifier: 1.15.0 - version: 1.15.0 + specifier: ^1.16.0 + version: 1.17.0 toml: specifier: 3.0.0 version: 3.0.0 @@ -763,7 +763,7 @@ importers: devDependencies: '@better-auth/cli': specifier: 1.4.21 - version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)) '@types/adm-zip': specifier: ^0.5.7 version: 0.5.7 @@ -4167,6 +4167,7 @@ packages: '@xmldom/xmldom@0.8.11': resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} + deprecated: this version has critical issues, please update to the latest version '@xterm/addon-attach@0.10.0': resolution: {integrity: sha512-ES/XO8pC1tPHSkh4j7qzM8ajFt++u8KMvfRc9vKIbjHTDOxjl9IUVo+vcQgLn3FTCM3w2czTvBss8nMWlD83Cg==} @@ -7567,6 +7568,10 @@ packages: resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} engines: {node: '>=10.16.0'} + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + engines: {node: '>=10.16.0'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -8581,21 +8586,21 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) + better-auth: 1.5.4(219be630f7f6fef2e235cef94eaddc25) zod: 4.3.6 - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) + better-auth: 1.5.4(8933545d763d3f096150f97f9213a424) zod: 4.3.6 - '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': + '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1))': dependencies: '@babel/core': 7.29.0 '@babel/preset-react': 7.28.5(@babel/core@7.29.0) @@ -8607,7 +8612,7 @@ snapshots: '@mrleebo/prisma-ast': 0.13.1 '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) '@types/pg': 8.16.0 - better-auth: 1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac) + better-auth: 1.4.21(db78b83f9b5449d160708cdf9d272aa3) better-sqlite3: 12.6.2 c12: 3.3.3 chalk: 5.6.2 @@ -8741,24 +8746,24 @@ snapshots: '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) prisma: 7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) + better-auth: 1.5.4(219be630f7f6fef2e235cef94eaddc25) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 samlify: 2.10.2 zod: 4.3.6 - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) + better-auth: 1.5.4(8933545d763d3f096150f97f9213a424) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 @@ -12220,6 +12225,7 @@ snapshots: magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) + optional: true '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': dependencies: @@ -12228,7 +12234,6 @@ snapshots: magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) - optional: true '@vitest/pretty-format@4.0.18': dependencies: @@ -12433,7 +12438,7 @@ snapshots: before-after-hook@2.2.3: {} - better-auth@1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac): + better-auth@1.4.21(db78b83f9b5449d160708cdf9d272aa3): dependencies: '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) @@ -12459,9 +12464,9 @@ snapshots: prisma: 7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) - better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934): + better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) @@ -12496,7 +12501,7 @@ snapshots: transitivePeerDependencies: - '@cloudflare/workers-types' - better-auth@1.5.4(febde88eaf587188179e6ecc47119e50): + better-auth@1.5.4(8933545d763d3f096150f97f9213a424): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) @@ -13085,7 +13090,7 @@ snapshots: debug: 4.4.3 readable-stream: 3.6.2 split-ca: 1.0.1 - ssh2: 1.15.0 + ssh2: 1.17.0 transitivePeerDependencies: - supports-color @@ -15731,6 +15736,14 @@ snapshots: cpu-features: 0.0.10 nan: 2.25.0 + ssh2@1.17.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.25.0 + stackback@0.0.2: {} standard-as-callback@2.1.0: {} @@ -16248,13 +16261,13 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)): + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) + vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript @@ -16273,6 +16286,7 @@ snapshots: jiti: 1.21.7 tsx: 4.16.2 yaml: 2.8.1 + optional: true vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16288,7 +16302,6 @@ snapshots: jiti: 2.6.1 tsx: 4.16.2 yaml: 2.8.1 - optional: true vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16327,6 +16340,7 @@ snapshots: - terser - tsx - yaml + optional: true vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16365,7 +16379,6 @@ snapshots: - terser - tsx - yaml - optional: true w3c-keyname@2.2.8: {} From eafbd0353e067abfb289707fb44957fcc170dd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Ors=C3=A1g?= Date: Sat, 4 Apr 2026 17:18:03 +0200 Subject: [PATCH 003/261] fix: strictly use ssh2 1.16.0 package --- apps/dokploy/package.json | 2 +- packages/server/package.json | 2 +- pnpm-lock.yaml | 84 ++++++++++++++++-------------------- 3 files changed, 38 insertions(+), 50 deletions(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 09ec864c4..67478303b 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -145,7 +145,7 @@ "shell-quote": "^1.8.1", "slugify": "^1.6.6", "sonner": "^1.7.4", - "ssh2": "1.15.0", + "ssh2": "~1.16.0", "stripe": "17.2.0", "superjson": "^2.2.2", "swagger-ui-react": "^5.31.2", diff --git a/packages/server/package.json b/packages/server/package.json index ad7d8d48d..1f519b494 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -80,7 +80,7 @@ "semver": "7.7.3", "shell-quote": "^1.8.1", "slugify": "^1.6.6", - "ssh2": "^1.16.0", + "ssh2": "~1.16.0", "toml": "3.0.0", "ws": "8.16.0", "yaml": "2.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18bcc7f2b..daece688d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,10 +115,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6)) '@codemirror/autocomplete': specifier: ^6.18.6 version: 6.20.0 @@ -277,7 +277,7 @@ importers: version: 5.1.1 better-auth: specifier: 1.5.4 - version: 1.5.4(219be630f7f6fef2e235cef94eaddc25) + version: 1.5.4(febde88eaf587188179e6ecc47119e50) bl: specifier: 6.0.11 version: 6.0.11 @@ -411,8 +411,8 @@ importers: specifier: ^1.7.4 version: 1.7.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) ssh2: - specifier: 1.15.0 - version: 1.15.0 + specifier: ~1.16.0 + version: 1.16.0 stripe: specifier: 17.2.0 version: 17.2.0 @@ -527,10 +527,10 @@ importers: version: 5.9.3 vite-tsconfig-paths: specifier: 4.3.2 - version: 4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)) vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) apps/schedules: dependencies: @@ -618,10 +618,10 @@ importers: version: 2.0.30(zod@4.3.6) '@better-auth/api-key': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934)) '@better-auth/sso': specifier: 1.5.4 - version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))(better-call@2.0.2(zod@4.3.6)) + version: 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6)) '@better-auth/utils': specifier: 0.3.1 version: 0.3.1 @@ -660,7 +660,7 @@ importers: version: 5.1.1 better-auth: specifier: 1.5.4 - version: 1.5.4(8933545d763d3f096150f97f9213a424) + version: 1.5.4(334901c35c1fcda64bb596793b2e4934) better-call: specifier: 2.0.2 version: 2.0.2(zod@4.3.6) @@ -746,8 +746,8 @@ importers: specifier: ^1.6.6 version: 1.6.6 ssh2: - specifier: ^1.16.0 - version: 1.17.0 + specifier: ~1.16.0 + version: 1.16.0 toml: specifier: 3.0.0 version: 3.0.0 @@ -763,7 +763,7 @@ importers: devDependencies: '@better-auth/cli': specifier: 1.4.21 - version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)) + version: 1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)) '@types/adm-zip': specifier: ^0.5.7 version: 0.5.7 @@ -7564,12 +7564,8 @@ packages: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} - ssh2@1.15.0: - resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} - engines: {node: '>=10.16.0'} - - ssh2@1.17.0: - resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + ssh2@1.16.0: + resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} engines: {node: '>=10.16.0'} stackback@0.0.2: @@ -8586,21 +8582,21 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(219be630f7f6fef2e235cef94eaddc25) + better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) zod: 4.3.6 - '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))': + '@better-auth/api-key@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.4(8933545d763d3f096150f97f9213a424) + better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) zod: 4.3.6 - '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1))': + '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(drizzle-kit@0.31.9)(jose@6.1.3)(kysely@0.28.11)(mongodb@7.1.0)(mysql2@3.15.3)(nanostores@1.1.1)(next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': dependencies: '@babel/core': 7.29.0 '@babel/preset-react': 7.28.5(@babel/core@7.29.0) @@ -8612,7 +8608,7 @@ snapshots: '@mrleebo/prisma-ast': 0.13.1 '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) '@types/pg': 8.16.0 - better-auth: 1.4.21(db78b83f9b5449d160708cdf9d272aa3) + better-auth: 1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac) better-sqlite3: 12.6.2 c12: 3.3.3 chalk: 5.6.2 @@ -8746,24 +8742,24 @@ snapshots: '@prisma/client': 5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)) prisma: 7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(219be630f7f6fef2e235cef94eaddc25) + better-auth: 1.5.4(334901c35c1fcda64bb596793b2e4934) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 samlify: 2.10.2 zod: 4.3.6 - '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(8933545d763d3f096150f97f9213a424))(better-call@2.0.2(zod@4.3.6))': + '@better-auth/sso@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(better-auth@1.5.4(febde88eaf587188179e6ecc47119e50))(better-call@2.0.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.4(8933545d763d3f096150f97f9213a424) + better-auth: 1.5.4(febde88eaf587188179e6ecc47119e50) better-call: 2.0.2(zod@4.3.6) fast-xml-parser: 5.5.1 jose: 6.1.3 @@ -12225,7 +12221,6 @@ snapshots: magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) - optional: true '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1))': dependencies: @@ -12234,6 +12229,7 @@ snapshots: magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) + optional: true '@vitest/pretty-format@4.0.18': dependencies: @@ -12438,7 +12434,7 @@ snapshots: before-after-hook@2.2.3: {} - better-auth@1.4.21(db78b83f9b5449d160708cdf9d272aa3): + better-auth@1.4.21(b1bc00b9e18c5e6af4e13b03fc4304ac): dependencies: '@better-auth/core': 1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.21(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) @@ -12464,9 +12460,9 @@ snapshots: prisma: 7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) - better-auth@1.5.4(219be630f7f6fef2e235cef94eaddc25): + better-auth@1.5.4(334901c35c1fcda64bb596793b2e4934): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) @@ -12501,7 +12497,7 @@ snapshots: transitivePeerDependencies: - '@cloudflare/workers-types' - better-auth@1.5.4(8933545d763d3f096150f97f9213a424): + better-auth@1.5.4(febde88eaf587188179e6ecc47119e50): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.18.0)(postgres@3.4.4)(prisma@7.4.1(@types/react@18.3.5)(better-sqlite3@12.6.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))) @@ -13090,7 +13086,7 @@ snapshots: debug: 4.4.3 readable-stream: 3.6.2 split-ca: 1.0.1 - ssh2: 1.17.0 + ssh2: 1.16.0 transitivePeerDependencies: - supports-color @@ -15728,15 +15724,7 @@ snapshots: sqlstring@2.3.3: {} - ssh2@1.15.0: - dependencies: - asn1: 0.2.6 - bcrypt-pbkdf: 1.0.2 - optionalDependencies: - cpu-features: 0.0.10 - nan: 2.25.0 - - ssh2@1.17.0: + ssh2@1.16.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 @@ -16261,13 +16249,13 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1)): + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1) + vite: 7.3.1(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript @@ -16286,7 +16274,6 @@ snapshots: jiti: 1.21.7 tsx: 4.16.2 yaml: 2.8.1 - optional: true vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16302,6 +16289,7 @@ snapshots: jiti: 2.6.1 tsx: 4.16.2 yaml: 2.8.1 + optional: true vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@1.21.7)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16340,7 +16328,6 @@ snapshots: - terser - tsx - yaml - optional: true vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(jiti@2.6.1)(tsx@4.16.2)(yaml@2.8.1): dependencies: @@ -16379,6 +16366,7 @@ snapshots: - terser - tsx - yaml + optional: true w3c-keyname@2.2.8: {} From 9f07f8e9e1a128e27a7473ee228f8bc76352648f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D1=80=D0=B0=D0=BF=D0=B5=D0=B7=D0=B8=D0=BD=20=D0=90?= =?UTF-8?q?=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=90=D0=BB=D0=B5=D0=BA=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Mon, 13 Apr 2026 19:57:31 +0300 Subject: [PATCH 004/261] fix: prevent serverIp from being overwritten on every user registration --- packages/server/src/lib/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 69f9321a6..a4cc2f729 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -196,7 +196,7 @@ const { handler, api } = betterAuth({ where: eq(schema.member.role, "owner"), }); - if (!IS_CLOUD) { + if (!IS_CLOUD && !isAdminPresent) { await updateWebServerSettings({ serverIp: await getPublicIpWithFallback(), }); From f8eb2ba4ba6cc30f285d0ac85c2b788425e2cff9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:11:21 -0600 Subject: [PATCH 005/261] feat: add containers tab to compose services Add a Containers tab to the compose service page that lists all containers with their state, status, and container ID. Each container has a dropdown menu with lifecycle actions: View Logs, Restart, Start, Stop, and Kill. - Add containerStart, containerStop, containerKill functions to docker service - Add corresponding tRPC procedures with server ownership checks and audit logging - Update containerRestart to support remote servers via serverId - Create ShowComposeContainers component with table view and action menu - Add Containers tab between Deployments and Backups, gated by docker.read permission --- .../containers/show-compose-containers.tsx | 272 ++++++++++++++++++ .../services/compose/[composeId].tsx | 19 ++ apps/dokploy/server/api/routers/docker.ts | 91 +++++- packages/server/src/services/docker.ts | 67 ++++- 4 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx new file mode 100644 index 000000000..4372efdbd --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx @@ -0,0 +1,272 @@ +import { Loader2, MoreHorizontal, RefreshCw } from "lucide-react"; +import dynamic from "next/dynamic"; +import { useState } from "react"; +import { toast } from "sonner"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { api } from "@/utils/api"; + +const DockerLogsId = dynamic( + () => + import("@/components/dashboard/docker/logs/docker-logs-id").then( + (e) => e.DockerLogsId, + ), + { + ssr: false, + }, +); + +interface Props { + appName: string; + serverId?: string; + appType: "stack" | "docker-compose"; +} + +export const ShowComposeContainers = ({ + appName, + appType, + serverId, +}: Props) => { + const { data, isPending, refetch } = + api.docker.getContainersByAppNameMatch.useQuery( + { + appName, + appType, + serverId, + }, + { + enabled: !!appName, + }, + ); + + return ( + + +
+ Containers + + Inspect each container in this compose and run basic lifecycle + actions. + +
+ +
+ + {isPending ? ( +
+ +
+ ) : !data || data.length === 0 ? ( +
+ + No containers found. Deploy the compose to see containers here. + +
+ ) : ( +
+ + + + Name + State + Status + Container ID + + + + + {data.map((container) => ( + refetch()} + /> + ))} + +
+
+ )} +
+
+ ); +}; + +interface ContainerRowProps { + container: { + containerId: string; + name: string; + state: string; + status: string; + }; + serverId?: string; + onActionComplete: () => void; +} + +const ContainerRow = ({ + container, + serverId, + onActionComplete, +}: ContainerRowProps) => { + const [logsOpen, setLogsOpen] = useState(false); + const [actionLoading, setActionLoading] = useState(null); + + const restartMutation = api.docker.restartContainer.useMutation(); + const startMutation = api.docker.startContainer.useMutation(); + const stopMutation = api.docker.stopContainer.useMutation(); + const killMutation = api.docker.killContainer.useMutation(); + + const handleAction = async ( + action: string, + mutationFn: typeof restartMutation, + ) => { + setActionLoading(action); + try { + await mutationFn.mutateAsync({ + containerId: container.containerId, + serverId, + }); + toast.success(`Container ${action} successfully`); + onActionComplete(); + } catch (error) { + toast.error( + `Failed to ${action} container: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } finally { + setActionLoading(null); + } + }; + + return ( + + {container.name} + + + {container.state} + + + {container.status} + + {container.containerId} + + + + + + + + + Actions + + e.preventDefault()} + > + View Logs + + + + handleAction("restart", restartMutation)} + > + Restart + + handleAction("start", startMutation)} + > + Start + + handleAction("stop", stopMutation)} + > + Stop + + handleAction("kill", killMutation)} + > + Kill + + + + + + View Logs + + Logs for {container.name} + + +
+ +
+
+
+
+
+ ); +}; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index 781dd7795..73c7cea42 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -24,6 +24,7 @@ import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-c 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 { ShowComposeContainers } from "@/components/dashboard/compose/containers/show-compose-containers"; import { ShowDockerLogsCompose } from "@/components/dashboard/compose/logs/show"; import { ShowDockerLogsStack } from "@/components/dashboard/compose/logs/show-stack"; import { UpdateCompose } from "@/components/dashboard/compose/update-compose"; @@ -60,6 +61,7 @@ type TabState = | "advanced" | "deployments" | "domains" + | "containers" | "monitoring" | "volumeBackups"; @@ -231,6 +233,11 @@ const Service = ( Deployments )} + {permissions?.docker.read && ( + + Containers + + )} {permissions?.service.create && ( Backups )} @@ -298,6 +305,18 @@ const Service = ( )} + {permissions?.docker.read && ( + +
+ +
+
+ )} + {permissions?.monitoring.read && (
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts index dd322e6c3..838ab83a0 100644 --- a/apps/dokploy/server/api/routers/docker.ts +++ b/apps/dokploy/server/api/routers/docker.ts @@ -1,6 +1,9 @@ import { + containerKill, containerRemove, containerRestart, + containerStart, + containerStop, findServerById, getConfig, getContainers, @@ -42,17 +45,101 @@ export const dockerRouter = createTRPCRouter({ .string() .min(1) .regex(containerIdRegex, "Invalid container id."), + serverId: z.string().optional(), }), ) .mutation(async ({ input, ctx }) => { - const result = await containerRestart(input.containerId); + if (input.serverId) { + const server = await findServerById(input.serverId); + if (server.organizationId !== ctx.session?.activeOrganizationId) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await containerRestart(input.containerId, input.serverId); await audit(ctx, { action: "start", resourceType: "docker", resourceId: input.containerId, resourceName: input.containerId, }); - return result; + }), + + startContainer: withPermission("docker", "read") + .input( + z.object({ + containerId: z + .string() + .min(1) + .regex(containerIdRegex, "Invalid container id."), + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + const server = await findServerById(input.serverId); + if (server.organizationId !== ctx.session?.activeOrganizationId) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await containerStart(input.containerId, input.serverId); + await audit(ctx, { + action: "start", + resourceType: "docker", + resourceId: input.containerId, + resourceName: input.containerId, + }); + }), + + stopContainer: withPermission("docker", "read") + .input( + z.object({ + containerId: z + .string() + .min(1) + .regex(containerIdRegex, "Invalid container id."), + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + const server = await findServerById(input.serverId); + if (server.organizationId !== ctx.session?.activeOrganizationId) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await containerStop(input.containerId, input.serverId); + await audit(ctx, { + action: "stop", + resourceType: "docker", + resourceId: input.containerId, + resourceName: input.containerId, + }); + }), + + killContainer: withPermission("docker", "read") + .input( + z.object({ + containerId: z + .string() + .min(1) + .regex(containerIdRegex, "Invalid container id."), + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + const server = await findServerById(input.serverId); + if (server.organizationId !== ctx.session?.activeOrganizationId) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await containerKill(input.containerId, input.serverId); + await audit(ctx, { + action: "stop", + resourceType: "docker", + resourceId: input.containerId, + resourceName: input.containerId, + }); }), removeContainer: withPermission("docker", "read") diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index eb218b1e1..6c7e379c0 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -417,21 +417,64 @@ export const getContainerLogs = async ( } }; -export const containerRestart = async (containerId: string) => { - try { - const { stdout, stderr } = await execAsync( - `docker container restart ${containerId}`, - ); +export const containerRestart = async ( + containerId: string, + serverId?: string, +) => { + const command = `docker container restart ${containerId}`; + const { stderr } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); - if (stderr) { - console.error(`Error: ${stderr}`); - return; - } + if (stderr) { + console.error(`Error: ${stderr}`); + throw new Error(stderr); + } +}; - const config = JSON.parse(stdout); +export const containerStart = async ( + containerId: string, + serverId?: string, +) => { + const command = `docker container start ${containerId}`; + const { stderr } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); - return config; - } catch {} + if (stderr) { + console.error(`Error: ${stderr}`); + throw new Error(stderr); + } +}; + +export const containerStop = async ( + containerId: string, + serverId?: string, +) => { + const command = `docker container stop ${containerId}`; + const { stderr } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); + + if (stderr) { + console.error(`Error: ${stderr}`); + throw new Error(stderr); + } +}; + +export const containerKill = async ( + containerId: string, + serverId?: string, +) => { + const command = `docker container kill ${containerId}`; + const { stderr } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); + + if (stderr) { + console.error(`Error: ${stderr}`); + throw new Error(stderr); + } }; export const containerRemove = async ( From ddf570a8078d7b4a54ffbf21ad6d5d60b7c110ed Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 02:15:37 +0000 Subject: [PATCH 006/261] [autofix.ci] apply automated fixes --- .../compose/containers/show-compose-containers.tsx | 8 ++------ .../[environmentId]/services/compose/[composeId].tsx | 4 +--- packages/server/src/services/docker.ts | 10 ++-------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx index 4372efdbd..d156e8e1f 100644 --- a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx +++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx @@ -86,9 +86,7 @@ export const ShowComposeContainers = ({ onClick={() => refetch()} disabled={isPending} > - + @@ -253,9 +251,7 @@ const ContainerRow = ({ View Logs - - Logs for {container.name} - + Logs for {container.name}
)} {permissions?.docker.read && ( - - Containers - + Containers )} {permissions?.service.create && ( Backups diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index 6c7e379c0..e49adbb94 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -447,10 +447,7 @@ export const containerStart = async ( } }; -export const containerStop = async ( - containerId: string, - serverId?: string, -) => { +export const containerStop = async (containerId: string, serverId?: string) => { const command = `docker container stop ${containerId}`; const { stderr } = serverId ? await execAsyncRemote(serverId, command) @@ -462,10 +459,7 @@ export const containerStop = async ( } }; -export const containerKill = async ( - containerId: string, - serverId?: string, -) => { +export const containerKill = async (containerId: string, serverId?: string) => { const command = `docker container kill ${containerId}`; const { stderr } = serverId ? await execAsyncRemote(serverId, command) From 00c708483ed31030e1b0a9d6b1b28fccbf886525 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:31:58 -0600 Subject: [PATCH 007/261] fix: use service.read permission for compose container actions Change restartContainer, startContainer, stopContainer, and killContainer endpoints to use service.read instead of docker.read so members with access to the compose can use container lifecycle actions. --- .../[environmentId]/services/compose/[composeId].tsx | 4 ++-- apps/dokploy/server/api/routers/docker.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index 5f8fa3f44..b6652db84 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -233,7 +233,7 @@ const Service = ( Deployments )} - {permissions?.docker.read && ( + {permissions?.service.read && ( Containers )} {permissions?.service.create && ( @@ -303,7 +303,7 @@ const Service = (
)} - {permissions?.docker.read && ( + {permissions?.service.read && (
Date: Mon, 13 Apr 2026 20:32:11 -0600 Subject: [PATCH 008/261] refactor: remove duplicate import of ShowComposeContainers component Eliminate redundant import statement for ShowComposeContainers in the compose service page, streamlining the code and improving readability. --- .../[environmentId]/services/compose/[composeId].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index b6652db84..ed116de43 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -22,9 +22,9 @@ import { ShowSchedules } from "@/components/dashboard/application/schedules/show 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 { ShowComposeContainers } from "@/components/dashboard/compose/containers/show-compose-containers"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; -import { ShowComposeContainers } from "@/components/dashboard/compose/containers/show-compose-containers"; import { ShowDockerLogsCompose } from "@/components/dashboard/compose/logs/show"; import { ShowDockerLogsStack } from "@/components/dashboard/compose/logs/show-stack"; import { UpdateCompose } from "@/components/dashboard/compose/update-compose"; From a48306a2c67190fcf42d0b985ec71bc0b2b1c2c0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:34:06 -0600 Subject: [PATCH 009/261] fix: address PR review feedback - Use "kill" audit action for killContainer instead of "stop" - Pass undefined instead of empty string for optional serverId --- .../[environmentId]/services/compose/[composeId].tsx | 2 +- apps/dokploy/server/api/routers/docker.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index ed116de43..ba575706d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -307,7 +307,7 @@ const Service = (
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts index d319e5a16..cadda0286 100644 --- a/apps/dokploy/server/api/routers/docker.ts +++ b/apps/dokploy/server/api/routers/docker.ts @@ -135,7 +135,7 @@ export const dockerRouter = createTRPCRouter({ } await containerKill(input.containerId, input.serverId); await audit(ctx, { - action: "stop", + action: "kill", resourceType: "docker", resourceId: input.containerId, resourceName: input.containerId, From 385850f354a997a0470105520d1e66b82539027d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:36:04 -0600 Subject: [PATCH 010/261] fix: update audit action for container termination Change the audit action from "kill" to "stop" for the containerKill function to better reflect the operation being performed. This aligns the logging with the intended action and improves clarity in audit records. --- apps/dokploy/server/api/routers/docker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts index cadda0286..d319e5a16 100644 --- a/apps/dokploy/server/api/routers/docker.ts +++ b/apps/dokploy/server/api/routers/docker.ts @@ -135,7 +135,7 @@ export const dockerRouter = createTRPCRouter({ } await containerKill(input.containerId, input.serverId); await audit(ctx, { - action: "kill", + action: "stop", resourceType: "docker", resourceId: input.containerId, resourceName: input.containerId, From 638b3dd54632b156f05b53f49b8466caa12d9fd5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:48:17 -0600 Subject: [PATCH 011/261] feat: add context menu to service cards Right-click on service cards to quickly Start, Deploy, Stop, or Delete a service without navigating into it. Uses shadcn/ui ContextMenu component built on @radix-ui/react-context-menu. Delete action shows a confirmation dialog. LibSQL services are excluded since they lack standard mutation endpoints. --- apps/dokploy/components/ui/context-menu.tsx | 198 +++++++++ apps/dokploy/package.json | 1 + .../environment/[environmentId].tsx | 375 +++++++++++++----- pnpm-lock.yaml | 30 ++ 4 files changed, 503 insertions(+), 101 deletions(-) create mode 100644 apps/dokploy/components/ui/context-menu.tsx diff --git a/apps/dokploy/components/ui/context-menu.tsx b/apps/dokploy/components/ui/context-menu.tsx new file mode 100644 index 000000000..2ff9f812e --- /dev/null +++ b/apps/dokploy/components/ui/context-menu.tsx @@ -0,0 +1,198 @@ +import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"; +import { Check, ChevronRight, Circle } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const ContextMenu = ContextMenuPrimitive.Root; + +const ContextMenuTrigger = ContextMenuPrimitive.Trigger; + +const ContextMenuGroup = ContextMenuPrimitive.Group; + +const ContextMenuPortal = ContextMenuPrimitive.Portal; + +const ContextMenuSub = ContextMenuPrimitive.Sub; + +const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; + +const ContextMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)); +ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName; + +const ContextMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName; + +const ContextMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName; + +const ContextMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName; + +const ContextMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)); +ContextMenuCheckboxItem.displayName = + ContextMenuPrimitive.CheckboxItem.displayName; + +const ContextMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName; + +const ContextMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName; + +const ContextMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName; + +const ContextMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +ContextMenuShortcut.displayName = "ContextMenuShortcut"; + +export { + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, + ContextMenuItem, + ContextMenuCheckboxItem, + ContextMenuRadioItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuGroup, + ContextMenuPortal, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuRadioGroup, +}; diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f7eff0aba..eadbe989d 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -67,6 +67,7 @@ "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index a91138f66..08b130b7c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -12,6 +12,7 @@ import { Loader2, Play, PlusIcon, + RefreshCw, Search, ServerIcon, SquareTerminal, @@ -68,6 +69,14 @@ import { CommandInput, CommandItem, } from "@/components/ui/command"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuTrigger, +} from "@/components/ui/context-menu"; import { Dialog, DialogContent, @@ -424,6 +433,7 @@ const EnvironmentPage = ( const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); const [deleteVolumes, setDeleteVolumes] = useState(false); const [selectedServerId, setSelectedServerId] = useState("all"); + const [serviceToDelete, setServiceToDelete] = useState(null); const handleSelectAll = () => { if (selectedServices.length === filteredServices.length) { @@ -814,6 +824,91 @@ const EnvironmentPage = ( setIsBulkActionLoading(false); }; + const getServiceActions = (service: Services) => { + switch (service.type) { + case "application": + return applicationActions; + case "compose": + return composeActions; + case "postgres": + return postgresActions; + case "mysql": + return mysqlActions; + case "mariadb": + return mariadbActions; + case "redis": + return redisActions; + case "mongo": + return mongoActions; + default: + return null; + } + }; + + const getServiceIdKey = (service: Services) => { + switch (service.type) { + case "application": + return "applicationId"; + case "compose": + return "composeId"; + case "postgres": + return "postgresId"; + case "mysql": + return "mysqlId"; + case "mariadb": + return "mariadbId"; + case "redis": + return "redisId"; + case "mongo": + return "mongoId"; + default: + return null; + } + }; + + const handleServiceAction = async ( + service: Services, + action: "start" | "stop" | "deploy", + ) => { + const actions = getServiceActions(service); + const idKey = getServiceIdKey(service); + if (!actions || !idKey) return; + + try { + await actions[action].mutateAsync({ + [idKey]: service.id, + } as any); + toast.success( + `Service ${service.name} ${action === "deploy" ? "queued for deployment" : `${action}ed`} successfully`, + ); + refetch(); + } catch (error) { + toast.error( + `Error ${action === "stop" ? "stopp" : action}ing service ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + }; + + const handleServiceDelete = async (service: Services) => { + const actions = getServiceActions(service); + const idKey = getServiceIdKey(service); + if (!actions || !idKey) return; + + try { + await actions.delete.mutateAsync({ + [idKey]: service.id, + } as any); + await utils.environment.one.invalidate({ environmentId }); + toast.success(`Service ${service.name} deleted successfully`); + refetch(); + } catch (error) { + toast.error( + `Error deleting service ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + setServiceToDelete(null); + }; + // Get unique servers from services const availableServers = useMemo(() => { if (!applications) return []; @@ -1472,110 +1567,156 @@ const EnvironmentPage = (
{filteredServices?.map((service) => ( - - - {service.serverId && ( -
- -
- )} -
- -
- -
- handleServiceSelect(service.id, e) - } + + + -
- -
-
- - - -
-
- - {service.name} - - {service.description && ( - - {service.description} - - )} -
- - - {service.type === "postgres" && ( - - )} - {service.type === "redis" && ( - - )} - {service.type === "mariadb" && ( - - )} - {service.type === "mongo" && ( - - )} - {service.type === "mysql" && ( - - )} - {service.type === "application" && - (service.icon ? ( - // biome-ignore lint/performance/noImgElement: application icon is data URL - {service.name} - ) : ( - - ))} - {service.type === "compose" && ( - - )} - {service.type === "libsql" && ( - - )} - -
-
-
- -
- {service.serverName && ( -
- - - {service.serverName} - + + {service.serverId && ( +
+
)} - - Created - -
- - - +
+ +
+ +
+ handleServiceSelect(service.id, e) + } + > +
+ +
+
+ + + +
+
+ + {service.name} + + {service.description && ( + + {service.description} + + )} +
+ + + {service.type === "postgres" && ( + + )} + {service.type === "redis" && ( + + )} + {service.type === "mariadb" && ( + + )} + {service.type === "mongo" && ( + + )} + {service.type === "mysql" && ( + + )} + {service.type === "application" && + (service.icon ? ( + // biome-ignore lint/performance/noImgElement: application icon is data URL + {service.name} + ) : ( + + ))} + {service.type === "compose" && ( + + )} + {service.type === "libsql" && ( + + )} + +
+
+
+ +
+ {service.serverName && ( +
+ + + {service.serverName} + +
+ )} + + Created + +
+
+ + + + {service.type !== "libsql" && ( + + + {service.name} + + + + handleServiceAction(service, "start") + } + > + + Start + + + handleServiceAction(service, "deploy") + } + > + + Deploy + + + handleServiceAction(service, "stop") + } + > + + Stop + + + setServiceToDelete(service)} + > + + Delete + + + )} + ))}
@@ -1586,6 +1727,38 @@ const EnvironmentPage = (
+ + {/* Single Service Delete Dialog */} + !open && setServiceToDelete(null)} + > + + + Delete Service + + Are you sure you want to delete{" "} + {serviceToDelete?.name}? + This action cannot be undone. + + + + + + + +
); }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f962c675..02c06680b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -176,6 +176,9 @@ importers: '@radix-ui/react-collapsible': specifier: ^1.1.11 version: 1.1.12(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-context-menu': + specifier: ^2.2.16 + version: 2.2.16(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.1.14 version: 1.1.15(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2801,6 +2804,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + peerDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-context@1.0.0': resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} peerDependencies: @@ -10633,6 +10649,20 @@ snapshots: optionalDependencies: '@types/react': 18.3.5 + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.5)(react@18.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.5)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.5)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + '@radix-ui/react-context@1.0.0(react@18.2.0)': dependencies: '@babel/runtime': 7.28.6 From 7f25ddca44ccb70827bfeac05c79822fa5bc4a8e Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 20:51:22 -0600 Subject: [PATCH 012/261] fix: add loading feedback and invalidation to context menu actions Use toast.promise for loading/success/error states and invalidate environment query after actions complete to update service status. --- .../environment/[environmentId].tsx | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index 08b130b7c..051415a74 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -874,19 +874,34 @@ const EnvironmentPage = ( const idKey = getServiceIdKey(service); if (!actions || !idKey) return; - try { - await actions[action].mutateAsync({ - [idKey]: service.id, - } as any); - toast.success( - `Service ${service.name} ${action === "deploy" ? "queued for deployment" : `${action}ed`} successfully`, - ); - refetch(); - } catch (error) { - toast.error( - `Error ${action === "stop" ? "stopp" : action}ing service ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, - ); - } + const actionLabels = { + start: { loading: "Starting", success: "started", error: "starting" }, + stop: { loading: "Stopping", success: "stopped", error: "stopping" }, + deploy: { + loading: "Deploying", + success: "queued for deployment", + error: "deploying", + }, + }; + + const labels = actionLabels[action]; + + toast.promise( + (async () => { + await actions[action].mutateAsync({ + [idKey]: service.id, + } as any); + })(), + { + loading: `${labels.loading} ${service.name}...`, + success: () => { + utils.environment.one.invalidate({ environmentId }); + return `${service.name} ${labels.success} successfully`; + }, + error: (error) => + `Error ${labels.error} ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, + }, + ); }; const handleServiceDelete = async (service: Services) => { @@ -894,18 +909,22 @@ const EnvironmentPage = ( const idKey = getServiceIdKey(service); if (!actions || !idKey) return; - try { - await actions.delete.mutateAsync({ - [idKey]: service.id, - } as any); - await utils.environment.one.invalidate({ environmentId }); - toast.success(`Service ${service.name} deleted successfully`); - refetch(); - } catch (error) { - toast.error( - `Error deleting service ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, - ); - } + toast.promise( + (async () => { + await actions.delete.mutateAsync({ + [idKey]: service.id, + } as any); + })(), + { + loading: `Deleting ${service.name}...`, + success: () => { + utils.environment.one.invalidate({ environmentId }); + return `${service.name} deleted successfully`; + }, + error: (error) => + `Error deleting ${service.name}: ${error instanceof Error ? error.message : "Unknown error"}`, + }, + ); setServiceToDelete(null); }; From 9af745ce6701f712461635ca50e2d2a442661b0d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 13 Apr 2026 21:56:53 -0600 Subject: [PATCH 013/261] feat: add view mounts, view config, and terminal to container actions Add a new "View Mounts" action to the container dropdown that displays volume and bind mounts in a formatted table (type, source, destination, mode, read/write). Also add "View Config" and "Terminal" actions to the compose containers tab which previously only had logs and lifecycle actions. --- .../containers/show-compose-containers.tsx | 17 +++ .../docker/mounts/show-container-mounts.tsx | 112 ++++++++++++++++++ .../dashboard/docker/show/columns.tsx | 5 + 3 files changed, 134 insertions(+) create mode 100644 apps/dokploy/components/dashboard/docker/mounts/show-container-mounts.tsx diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx index d156e8e1f..68260bc2d 100644 --- a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx +++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx @@ -36,6 +36,9 @@ import { TableRow, } from "@/components/ui/table"; import { api } from "@/utils/api"; +import { ShowContainerConfig } from "@/components/dashboard/docker/config/show-container-config"; +import { ShowContainerMounts } from "@/components/dashboard/docker/mounts/show-container-mounts"; +import { DockerTerminalModal } from "@/components/dashboard/docker/terminal/docker-terminal-modal"; const DockerLogsId = dynamic( () => @@ -217,6 +220,20 @@ const ContainerRow = ({ View Logs + + + + Terminal + { + const { data } = api.docker.getConfig.useQuery( + { + containerId, + serverId, + }, + { + enabled: !!containerId, + }, + ); + + const mounts: Mount[] = data?.Mounts ?? []; + + return ( + + + e.preventDefault()} + > + View Mounts + + + + + Container Mounts + + Volume and bind mounts for this container + + +
+ {mounts.length === 0 ? ( +
+ No mounts found for this container. +
+ ) : ( + + + + Type + Source + Destination + Mode + Read/Write + + + + {mounts.map((mount, index) => ( + + + {mount.Type} + + + {mount.Name || mount.Source} + + + {mount.Destination} + + + {mount.Mode || "-"} + + + + {mount.RW ? "RW" : "RO"} + + + + ))} + +
+ )} +
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/docker/show/columns.tsx b/apps/dokploy/components/dashboard/docker/show/columns.tsx index 33c104d97..a51cb8c62 100644 --- a/apps/dokploy/components/dashboard/docker/show/columns.tsx +++ b/apps/dokploy/components/dashboard/docker/show/columns.tsx @@ -10,6 +10,7 @@ import { } from "@/components/ui/dropdown-menu"; import { ShowContainerConfig } from "../config/show-container-config"; import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs"; +import { ShowContainerMounts } from "../mounts/show-container-mounts"; import { RemoveContainerDialog } from "../remove/remove-container"; import { DockerTerminalModal } from "../terminal/docker-terminal-modal"; import { UploadFileModal } from "../upload/upload-file-modal"; @@ -123,6 +124,10 @@ export const columns: ColumnDef[] = [ containerId={container.containerId} serverId={container.serverId || ""} /> + Date: Mon, 13 Apr 2026 22:04:46 -0600 Subject: [PATCH 014/261] feat: add container networks view to dashboard Integrate a new component, ShowContainerNetworks, to display network details for each container in the dashboard. This includes a dialog that shows network information such as IP address, gateway, and MAC address, enhancing the container management capabilities. --- .../containers/show-compose-containers.tsx | 5 + .../networks/show-container-networks.tsx | 119 ++++++++++++++++++ .../dashboard/docker/show/columns.tsx | 5 + 3 files changed, 129 insertions(+) create mode 100644 apps/dokploy/components/dashboard/docker/networks/show-container-networks.tsx diff --git a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx index 68260bc2d..7787d00e7 100644 --- a/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx +++ b/apps/dokploy/components/dashboard/compose/containers/show-compose-containers.tsx @@ -38,6 +38,7 @@ import { import { api } from "@/utils/api"; import { ShowContainerConfig } from "@/components/dashboard/docker/config/show-container-config"; import { ShowContainerMounts } from "@/components/dashboard/docker/mounts/show-container-mounts"; +import { ShowContainerNetworks } from "@/components/dashboard/docker/networks/show-container-networks"; import { DockerTerminalModal } from "@/components/dashboard/docker/terminal/docker-terminal-modal"; const DockerLogsId = dynamic( @@ -228,6 +229,10 @@ const ContainerRow = ({ containerId={container.containerId} serverId={serverId || ""} /> + { + const { data } = api.docker.getConfig.useQuery( + { + containerId, + serverId, + }, + { + enabled: !!containerId, + }, + ); + + const networks: Record = + data?.NetworkSettings?.Networks ?? {}; + const entries = Object.entries(networks); + + return ( + + + e.preventDefault()} + > + View Networks + + + + + Container Networks + + Networks attached to this container + + +
+ {entries.length === 0 ? ( +
+ No networks found for this container. +
+ ) : ( + + + + Network + IP Address + Gateway + MAC Address + Aliases + + + + {entries.map(([name, network]) => ( + + + {name} + + + {network.IPAddress + ? `${network.IPAddress}/${network.IPPrefixLen}` + : "-"} + + + {network.Gateway || "-"} + + + {network.MacAddress || "-"} + + + {network.Aliases?.join(", ") || "-"} + + + ))} + +
+ )} +
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/docker/show/columns.tsx b/apps/dokploy/components/dashboard/docker/show/columns.tsx index a51cb8c62..fa06f4d70 100644 --- a/apps/dokploy/components/dashboard/docker/show/columns.tsx +++ b/apps/dokploy/components/dashboard/docker/show/columns.tsx @@ -11,6 +11,7 @@ import { import { ShowContainerConfig } from "../config/show-container-config"; import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs"; import { ShowContainerMounts } from "../mounts/show-container-mounts"; +import { ShowContainerNetworks } from "../networks/show-container-networks"; import { RemoveContainerDialog } from "../remove/remove-container"; import { DockerTerminalModal } from "../terminal/docker-terminal-modal"; import { UploadFileModal } from "../upload/upload-file-modal"; @@ -128,6 +129,10 @@ export const columns: ColumnDef[] = [ containerId={container.containerId} serverId={container.serverId || ""} /> + Date: Wed, 15 Apr 2026 18:32:20 -0600 Subject: [PATCH 015/261] feat: add OpenAPI sync to MCP and CLI repositories Implement workflows to sync the OpenAPI specification to both the MCP and CLI repositories. This includes cloning the repositories, updating the openapi.json file, and committing the changes with relevant metadata. The process ensures that the OpenAPI documentation is consistently updated across multiple platforms. --- .github/workflows/sync-openapi-docs.yml | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/sync-openapi-docs.yml b/.github/workflows/sync-openapi-docs.yml index 549af945b..1a6b1e87b 100644 --- a/.github/workflows/sync-openapi-docs.yml +++ b/.github/workflows/sync-openapi-docs.yml @@ -68,3 +68,45 @@ jobs: echo "✅ OpenAPI synced to website successfully" + - name: Sync to MCP repository + run: | + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo + + cd mcp-repo + + cp -f ../openapi.json openapi.json + + git config user.name "Dokploy Bot" + git config user.email "bot@dokploy.com" + + git add openapi.json + git commit -m "chore: sync OpenAPI specification [skip ci]" \ + -m "Source: ${{ github.repository }}@${{ github.sha }}" \ + -m "Updated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" \ + --allow-empty + + git push + + echo "✅ OpenAPI synced to MCP repository successfully" + + - name: Sync to CLI repository + run: | + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git cli-repo + + cd cli-repo + + cp -f ../openapi.json openapi.json + + git config user.name "Dokploy Bot" + git config user.email "bot@dokploy.com" + + git add openapi.json + git commit -m "chore: sync OpenAPI specification [skip ci]" \ + -m "Source: ${{ github.repository }}@${{ github.sha }}" \ + -m "Updated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" \ + --allow-empty + + git push + + echo "✅ OpenAPI synced to CLI repository successfully" + From 599b97da51c5d5d5fbade78f5e587a8fe0e11552 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Wed, 15 Apr 2026 18:50:54 -0600 Subject: [PATCH 016/261] feat: add version synchronization workflow for MCP and CLI repositories Implement a GitHub Actions workflow to automatically sync the version from the Dokploy repository to the MCP and CLI repositories upon release. This includes cloning the repositories, updating the package.json version, and committing the changes with relevant metadata, ensuring consistent versioning across platforms. --- .github/workflows/sync-version.yml | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/sync-version.yml diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml new file mode 100644 index 000000000..c75988793 --- /dev/null +++ b/.github/workflows/sync-version.yml @@ -0,0 +1,68 @@ +name: Sync version to MCP and CLI repos + +on: + release: + types: [published] + +jobs: + sync-version: + name: Sync version to external repos + runs-on: ubuntu-latest + steps: + - name: Checkout Dokploy repository + uses: actions/checkout@v4 + + - name: Get version + id: get_version + run: | + VERSION=$(jq -r .version apps/dokploy/package.json) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version: $VERSION" + + - name: Sync version to MCP repository + run: | + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo + + cd mcp-repo + + if [ -f package.json ]; then + jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp + mv package.json.tmp package.json + fi + + git config user.name "Dokploy Bot" + git config user.email "bot@dokploy.com" + + git add -A + git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \ + -m "Source: ${{ github.repository }}@${{ github.sha }}" \ + -m "Release: ${{ github.event.release.html_url }}" \ + --allow-empty + + git push + + echo "MCP repo synced to version ${{ steps.get_version.outputs.version }}" + + - name: Sync version to CLI repository + run: | + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git cli-repo + + cd cli-repo + + if [ -f package.json ]; then + jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp + mv package.json.tmp package.json + fi + + git config user.name "Dokploy Bot" + git config user.email "bot@dokploy.com" + + git add -A + git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \ + -m "Source: ${{ github.repository }}@${{ github.sha }}" \ + -m "Release: ${{ github.event.release.html_url }}" \ + --allow-empty + + git push + + echo "CLI repo synced to version ${{ steps.get_version.outputs.version }}" From 04b9f56333f70618b8f99d23c5293c1d355e6d3d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Wed, 15 Apr 2026 20:55:37 -0600 Subject: [PATCH 017/261] chore: enhance version synchronization workflow for MCP and CLI repositories Update the GitHub Actions workflow to include regeneration of tools from the latest OpenAPI specification and ensure the latest openapi.json is copied to the CLI repository. This improves the consistency and accuracy of the versioning and API documentation across both repositories. --- .github/workflows/sync-version.yml | 49 ++++++++++++++++++------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml index c75988793..4ef94f1a9 100644 --- a/.github/workflows/sync-version.yml +++ b/.github/workflows/sync-version.yml @@ -21,27 +21,30 @@ jobs: - name: Sync version to MCP repository run: | - git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo - - cd mcp-repo - - if [ -f package.json ]; then + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo + cd mcp-repo + + # Bump version jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp mv package.json.tmp package.json - fi - - git config user.name "Dokploy Bot" - git config user.email "bot@dokploy.com" - - git add -A - git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \ - -m "Source: ${{ github.repository }}@${{ github.sha }}" \ - -m "Release: ${{ github.event.release.html_url }}" \ - --allow-empty - - git push - - echo "MCP repo synced to version ${{ steps.get_version.outputs.version }}" + + # Regenerate tools from latest OpenAPI spec + npm install -g pnpm + pnpm install + pnpm run fetch-openapi + pnpm run generate + + git config user.name "Dokploy Bot" + git config user.email "bot@dokploy.com" + + git add -A + git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \ + -m "Source: ${{ github.repository }}@${{ github.sha }}" \ + -m "Release: ${{ github.event.release.html_url }}" \ + --allow-empty + + git push + - name: Sync version to CLI repository run: | @@ -49,11 +52,18 @@ jobs: cd cli-repo + # Bump version if [ -f package.json ]; then jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp mv package.json.tmp package.json fi + # Copy latest openapi spec and regenerate commands + cp ../openapi.json ./openapi.json + npm install -g pnpm + pnpm install + pnpm run generate + git config user.name "Dokploy Bot" git config user.email "bot@dokploy.com" @@ -66,3 +76,4 @@ jobs: git push echo "CLI repo synced to version ${{ steps.get_version.outputs.version }}" + From b060f809322d87bae21417cd722b2b85388910e3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Thu, 16 Apr 2026 12:21:17 -0600 Subject: [PATCH 018/261] feat: add no tags message to tag selector component Enhance the TagSelector component to display a message when no tags are created, prompting users to add tags. This improves user experience by providing clear feedback in the UI. --- apps/dokploy/components/shared/tag-selector.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/dokploy/components/shared/tag-selector.tsx b/apps/dokploy/components/shared/tag-selector.tsx index ae16d00a6..19063d08c 100644 --- a/apps/dokploy/components/shared/tag-selector.tsx +++ b/apps/dokploy/components/shared/tag-selector.tsx @@ -116,6 +116,14 @@ export function TagSelector({
+ {tags.length === 0 && ( +
+ + No tags created yet. + + +
+ )} {tags.map((tag) => { const isSelected = selectedTags.includes(tag.id); From ea1238b1d1b225030fee8b357550479789cf1484 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 13:59:50 -0600 Subject: [PATCH 019/261] feat: resend verification email on sign-in and improve email template - Add `sendOnSignIn: true` to emailVerification config so unverified users receive a new verification email when they attempt to sign in - Create styled verification email template matching the invoice email design - Extract `sendVerificationEmail` helper to keep auth.ts clean - Show friendly message on login when email is not verified --- apps/dokploy/pages/index.tsx | 10 ++ .../server/src/emails/emails/verify-email.tsx | 104 ++++++++++++++++++ packages/server/src/lib/auth.ts | 16 +-- .../verification/send-verification-email.tsx | 25 +++++ 4 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 packages/server/src/emails/emails/verify-email.tsx diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index aab788ad9..426c6c0a9 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -82,6 +82,16 @@ export default function Home({ IS_CLOUD }: Props) { }); if (error) { + const isEmailNotVerified = + error.code === "EMAIL_NOT_VERIFIED" || + error.message?.toLowerCase().includes("email not verified"); + if (isEmailNotVerified) { + const msg = + "Your email is not verified. We've sent a new verification link to your email."; + toast.info(msg); + setError(msg); + return; + } toast.error(error.message); setError(error.message || "An error occurred while logging in"); return; diff --git a/packages/server/src/emails/emails/verify-email.tsx b/packages/server/src/emails/emails/verify-email.tsx new file mode 100644 index 000000000..3a32db77d --- /dev/null +++ b/packages/server/src/emails/emails/verify-email.tsx @@ -0,0 +1,104 @@ +import { + Body, + Button, + Container, + Head, + Heading, + Html, + Img, + Link, + Preview, + Section, + Tailwind, + Text, +} from "@react-email/components"; + +export type TemplateProps = { + userName: string; + verificationUrl: string; +}; + +export const VerifyEmailTemplate = ({ + userName = "User", + verificationUrl = "https://app.dokploy.com/verify", +}: TemplateProps) => { + const previewText = "Verify your email address to get started with Dokploy"; + return ( + + + {previewText} + + + + {/* Header */} +
+ Dokploy +
+ + {/* Body */} +
+ + Verify Your Email + + + Hello {userName}, thank you for signing up for Dokploy. Please + verify your email address to activate your account. + + + {/* CTA Button */} +
+ +
+ + + If the button above doesn't work, copy and paste the following + link into your browser: + + + {verificationUrl} + +
+ + {/* Footer */} +
+ + This is an automated email from{" "} + + Dokploy Cloud + + . If you didn't create an account, you can safely ignore this + email. + +
+
+ +
+ + ); +}; + +export default VerifyEmailTemplate; diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index a4cc2f729..d6753dee6 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -21,7 +21,10 @@ import { updateWebServerSettings, } from "../services/web-server-settings"; import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot"; -import { sendEmail } from "../verification/send-verification-email"; +import { + sendEmail, + sendVerificationEmail, +} from "../verification/send-verification-email"; import { getPublicIpWithFallback } from "../wss/utils"; import { ac, adminRole, memberRole, ownerRole } from "./access-control"; @@ -73,7 +76,7 @@ const { handler, api } = betterAuth({ }, }, logger: { - disabled: process.env.NODE_ENV === "production", + disabled: false, }, async trustedOrigins() { try { @@ -106,14 +109,13 @@ const { handler, api } = betterAuth({ emailVerification: { sendOnSignUp: true, autoSignInAfterVerification: true, + sendOnSignIn: true, sendVerificationEmail: async ({ user, url }) => { if (IS_CLOUD) { - await sendEmail({ + await sendVerificationEmail({ + userName: user.name || "User", email: user.email, - subject: "Verify your email", - text: ` -

Click the link to verify your email: Verify Email

- `, + verificationUrl: url, }); } }, diff --git a/packages/server/src/verification/send-verification-email.tsx b/packages/server/src/verification/send-verification-email.tsx index d446e4b72..098789a2b 100644 --- a/packages/server/src/verification/send-verification-email.tsx +++ b/packages/server/src/verification/send-verification-email.tsx @@ -1,4 +1,7 @@ +import { renderAsync } from "@react-email/components"; +import VerifyEmailTemplate from "../emails/emails/verify-email"; import { sendEmailNotification } from "../utils/notifications/utils"; + export const sendEmail = async ({ email, subject, @@ -26,3 +29,25 @@ export const sendEmail = async ({ return true; }; + +export const sendVerificationEmail = async ({ + userName, + email, + verificationUrl, +}: { + userName: string; + email: string; + verificationUrl: string; +}) => { + const html = await renderAsync( + VerifyEmailTemplate({ + userName: userName || "User", + verificationUrl, + }), + ); + await sendEmail({ + email, + subject: "Verify your email", + text: html, + }); +}; From fda367b2c51bbe6930823cf711883899cdf95494 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 14:01:46 -0600 Subject: [PATCH 020/261] fix: update logger configuration to disable in production environment Change the logger's disabled property to be dependent on the NODE_ENV variable, ensuring logging is disabled in production for improved performance and security. --- packages/server/src/lib/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index d6753dee6..65dd1b01d 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -76,7 +76,7 @@ const { handler, api } = betterAuth({ }, }, logger: { - disabled: false, + disabled: process.env.NODE_ENV === "production", }, async trustedOrigins() { try { From 0cae8330e2905b15cc69d83bb7fcd25b21df460b Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 14:42:14 -0600 Subject: [PATCH 021/261] chore: adjust version bump timing in synchronization workflow Update the GitHub Actions workflow to bump the version in package.json after installing dependencies, ensuring that the version is not overwritten by pnpm install. This change enhances the reliability of version synchronization for both MCP and CLI repositories. --- .github/workflows/sync-version.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml index 4ef94f1a9..aad018634 100644 --- a/.github/workflows/sync-version.yml +++ b/.github/workflows/sync-version.yml @@ -23,26 +23,26 @@ jobs: run: | git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo cd mcp-repo - - # Bump version - jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp - mv package.json.tmp package.json - + # Regenerate tools from latest OpenAPI spec npm install -g pnpm pnpm install pnpm run fetch-openapi pnpm run generate - + + # Bump version after install so pnpm install doesn't overwrite it + jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp + mv package.json.tmp package.json + git config user.name "Dokploy Bot" git config user.email "bot@dokploy.com" - + git add -A git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \ -m "Source: ${{ github.repository }}@${{ github.sha }}" \ -m "Release: ${{ github.event.release.html_url }}" \ --allow-empty - + git push @@ -52,18 +52,18 @@ jobs: cd cli-repo - # Bump version - if [ -f package.json ]; then - jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp - mv package.json.tmp package.json - fi - # Copy latest openapi spec and regenerate commands cp ../openapi.json ./openapi.json npm install -g pnpm pnpm install pnpm run generate + # Bump version after install so pnpm install doesn't overwrite it + if [ -f package.json ]; then + jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp + mv package.json.tmp package.json + fi + git config user.name "Dokploy Bot" git config user.email "bot@dokploy.com" From e7c581476ea8d2d7c679e6038d491f7408f71ecb Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 14:44:04 -0600 Subject: [PATCH 022/261] feat: add workflow dispatch trigger to version synchronization workflow Enhance the GitHub Actions workflow by adding a workflow_dispatch trigger, allowing manual execution of the version synchronization process. This provides greater flexibility in managing version updates for MCP and CLI repositories. --- .github/workflows/sync-version.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml index aad018634..5b97d2435 100644 --- a/.github/workflows/sync-version.yml +++ b/.github/workflows/sync-version.yml @@ -3,6 +3,7 @@ name: Sync version to MCP and CLI repos on: release: types: [published] + workflow_dispatch: jobs: sync-version: From 958372c5f93cf63e0d5f2e2b7ea372606dcfee1d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 14:46:20 -0600 Subject: [PATCH 023/261] chore: update paths in version synchronization workflow for MCP and CLI repositories Modify the GitHub Actions workflow to clone the MCP and CLI repositories into temporary directories instead of the current directory. This change improves the organization of the workflow and ensures that the latest OpenAPI specification is correctly referenced during the synchronization process. --- .github/workflows/sync-version.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml index 5b97d2435..1f28265ca 100644 --- a/.github/workflows/sync-version.yml +++ b/.github/workflows/sync-version.yml @@ -22,8 +22,8 @@ jobs: - name: Sync version to MCP repository run: | - git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo - cd mcp-repo + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git /tmp/mcp-repo + cd /tmp/mcp-repo # Regenerate tools from latest OpenAPI spec npm install -g pnpm @@ -49,12 +49,12 @@ jobs: - name: Sync version to CLI repository run: | - git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git cli-repo + git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git /tmp/cli-repo - cd cli-repo + cd /tmp/cli-repo # Copy latest openapi spec and regenerate commands - cp ../openapi.json ./openapi.json + cp ${{ github.workspace }}/openapi.json ./openapi.json npm install -g pnpm pnpm install pnpm run generate From 425fef6e282afbad8a2704ceb408bff003d789a6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 14:49:14 -0600 Subject: [PATCH 024/261] fix: remove 'v' prefix from version in synchronization workflow Update the version retrieval command in the GitHub Actions workflow to strip the 'v' prefix from the version number in package.json. This change ensures that the version format is consistent for downstream processes. --- .github/workflows/sync-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-version.yml b/.github/workflows/sync-version.yml index 1f28265ca..be19a2bb1 100644 --- a/.github/workflows/sync-version.yml +++ b/.github/workflows/sync-version.yml @@ -16,7 +16,7 @@ jobs: - name: Get version id: get_version run: | - VERSION=$(jq -r .version apps/dokploy/package.json) + VERSION=$(jq -r .version apps/dokploy/package.json | sed 's/^v//') echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Version: $VERSION" From f7b576cbf3a9fa6be5b9e262950bee4a3c5f9c6f Mon Sep 17 00:00:00 2001 From: Sancho Godinho Date: Sat, 18 Apr 2026 04:23:15 +0530 Subject: [PATCH 025/261] Fix typo in custom entrypoint description --- .../components/dashboard/application/domains/handle-domain.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx index 655f9d147..5edcbe3a7 100644 --- a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx @@ -666,7 +666,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
Custom Entrypoint - Use custom entrypoint for domina + Use custom entrypoint for domain
"web" and/or "websecure" is used by default.
From 6f0ed89ce7d07eff4fdaeb494fef50a67327934d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 17 Apr 2026 21:36:37 -0600 Subject: [PATCH 026/261] feat: add dashboard home page with overview and recent deployments Adds a new /dashboard/home landing with welcome header, KPI cards (deploys/24h, build, CPU, memory) and a recent deployments list. Home is now the post-login landing and the destination for permission fallback redirects across the app. Projects remains accessible from the sidebar. --- .../components/dashboard/home/show-home.tsx | 223 ++++++++++++++++++ .../components/dashboard/search-command.tsx | 2 +- .../welcome-stripe/welcome-subscription.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 7 + apps/dokploy/components/layouts/user-nav.tsx | 2 +- .../proprietary/sso/sign-in-with-sso.tsx | 2 +- apps/dokploy/pages/_error.tsx | 2 +- apps/dokploy/pages/dashboard/deployments.tsx | 2 +- apps/dokploy/pages/dashboard/docker.tsx | 2 +- apps/dokploy/pages/dashboard/home.tsx | 53 +++++ apps/dokploy/pages/dashboard/monitoring.tsx | 4 +- .../environment/[environmentId].tsx | 4 +- .../services/application/[applicationId].tsx | 2 +- .../services/compose/[composeId].tsx | 2 +- .../services/libsql/[libsqlId].tsx | 2 +- .../services/mariadb/[mariadbId].tsx | 2 +- .../services/mongo/[mongoId].tsx | 2 +- .../services/mysql/[mysqlId].tsx | 2 +- .../services/postgres/[postgresId].tsx | 2 +- .../services/redis/[redisId].tsx | 2 +- apps/dokploy/pages/dashboard/requests.tsx | 2 +- apps/dokploy/pages/dashboard/schedules.tsx | 2 +- .../pages/dashboard/settings/billing.tsx | 2 +- .../pages/dashboard/settings/cluster.tsx | 2 +- .../pages/dashboard/settings/invoices.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 2 +- apps/dokploy/pages/dashboard/swarm.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/index.tsx | 10 +- apps/dokploy/pages/invitation.tsx | 2 +- apps/dokploy/pages/register.tsx | 2 +- 31 files changed, 317 insertions(+), 34 deletions(-) create mode 100644 apps/dokploy/components/dashboard/home/show-home.tsx create mode 100644 apps/dokploy/pages/dashboard/home.tsx diff --git a/apps/dokploy/components/dashboard/home/show-home.tsx b/apps/dokploy/components/dashboard/home/show-home.tsx new file mode 100644 index 000000000..6e40d794a --- /dev/null +++ b/apps/dokploy/components/dashboard/home/show-home.tsx @@ -0,0 +1,223 @@ +import { formatDistanceToNow } from "date-fns"; +import { Plus, Rocket } from "lucide-react"; +import Link from "next/link"; +import { useMemo } from "react"; +import { Button } from "@/components/ui/button"; +import { api } from "@/utils/api"; + +type DeploymentStatus = "idle" | "running" | "done" | "error"; + +const statusDotClass: Record = { + done: "bg-muted-foreground/60", + running: "bg-amber-500", + error: "bg-red-500", + idle: "bg-muted-foreground/30", +}; + +function getServiceInfo(d: any) { + const app = d.application; + const comp = d.compose; + if (app?.environment?.project && app.environment) { + return { + name: app.name as string, + environment: app.environment.name as string, + projectName: app.environment.project.name as string, + href: `/dashboard/project/${app.environment.project.projectId}/environment/${app.environment.environmentId}/services/application/${app.applicationId}`, + }; + } + if (comp?.environment?.project && comp.environment) { + return { + name: comp.name as string, + environment: comp.environment.name as string, + projectName: comp.environment.project.name as string, + href: `/dashboard/project/${comp.environment.project.projectId}/environment/${comp.environment.environmentId}/services/compose/${comp.composeId}`, + }; + } + return null; +} + +function StatCard({ + label, + value, + delta, +}: { + label: string; + value: string; + delta?: string; +}) { + return ( +
+ + {label} + +
+ {value} + {delta && ( + + {delta} + + )} +
+
+ ); +} + +export const ShowHome = () => { + const { data: auth } = api.user.get.useQuery(); + const { data: projects } = api.project.all.useQuery(); + const { data: servers } = api.server.all.useQuery(); + const { data: permissions } = api.user.getPermissions.useQuery(); + const canReadDeployments = !!permissions?.deployment.read; + const { data: deployments } = api.deployment.allCentralized.useQuery( + undefined, + { + enabled: canReadDeployments, + refetchInterval: 10000, + }, + ); + + const firstName = auth?.user?.firstName?.trim(); + + const { totals, serverCount } = useMemo(() => { + let applications = 0; + let compose = 0; + let databases = 0; + const dbKeys = [ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", + ] as const; + for (const p of projects ?? []) { + for (const env of p.environments ?? []) { + applications += env.applications?.length ?? 0; + compose += env.compose?.length ?? 0; + for (const key of dbKeys) { + databases += (env as any)[key]?.length ?? 0; + } + } + } + return { + totals: { + projects: projects?.length ?? 0, + applications, + compose, + databases, + services: applications + compose + databases, + }, + serverCount: servers?.length ?? 0, + }; + }, [projects, servers]); + + const recentDeployments = useMemo(() => { + if (!deployments) return []; + return [...deployments] + .sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ) + .slice(0, 10); + }, [deployments]); + + return ( +
+
+
+

+ {firstName ? `Welcome back, ${firstName}` : "Welcome back"} +

+

+ {totals.services} services across {serverCount}{" "} + {serverCount === 1 ? "server" : "servers"} · {totals.projects}{" "} + {totals.projects === 1 ? "project" : "projects"} +

+
+ +
+ +
+ + + + +
+ +
+
+
+ +

Recent deployments

+
+ {canReadDeployments && ( + + view all → + + )} +
+ {!canReadDeployments ? ( +
+ You do not have permission to view deployments. +
+ ) : recentDeployments.length === 0 ? ( +
+ No deployments yet. +
+ ) : ( +
    + {recentDeployments.map((d) => { + const info = getServiceInfo(d); + if (!info) return null; + const status = (d.status ?? "idle") as DeploymentStatus; + return ( +
  • + + +
    + + {info.name} + + + {info.projectName} · {info.environment} + +
    + + {status} + + + {formatDistanceToNow(new Date(d.createdAt), { + addSuffix: true, + })} + + + logs → + + +
  • + ); + })} +
+ )} +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 9946a54a9..841728dc1 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -167,7 +167,7 @@ export const SearchCommand = () => {