From d02976476ace6a14c9b57371992ebf626bbed59f Mon Sep 17 00:00:00 2001 From: Marukome0743 Date: Mon, 28 Jul 2025 19:56:44 +0900 Subject: [PATCH 01/29] refactor: lint apps/components/layouts files --- .../components/layouts/onboarding-layout.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 11 +++----- .../components/layouts/update-server.tsx | 10 +++---- apps/dokploy/components/layouts/user-nav.tsx | 26 +++++++++---------- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/apps/dokploy/components/layouts/onboarding-layout.tsx b/apps/dokploy/components/layouts/onboarding-layout.tsx index 270c906c4..fff5413e0 100644 --- a/apps/dokploy/components/layouts/onboarding-layout.tsx +++ b/apps/dokploy/components/layouts/onboarding-layout.tsx @@ -1,6 +1,6 @@ -import { cn } from "@/lib/utils"; import Link from "next/link"; import type React from "react"; +import { cn } from "@/lib/utils"; import { GithubIcon } from "../icons/data-tools-icons"; import { Logo } from "../shared/logo"; import { Button } from "../ui/button"; diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 15390baf9..d1d4ae273 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,4 +1,5 @@ "use client"; +import type { inferRouterOutputs } from "@trpc/server"; import { Activity, BarChartHorizontalBigIcon, @@ -29,10 +30,10 @@ import { User, Users, } from "lucide-react"; +import Link from "next/link"; import { usePathname } from "next/navigation"; -import type * as React from "react"; import { useEffect, useState } from "react"; - +import { toast } from "sonner"; import { Breadcrumb, BreadcrumbItem, @@ -77,10 +78,6 @@ import { authClient } from "@/lib/auth-client"; import { cn } from "@/lib/utils"; import type { AppRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import type { inferRouterOutputs } from "@trpc/server"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { toast } from "sonner"; import { AddOrganization } from "../dashboard/organization/handle-organization"; import { DialogAction } from "../shared/dialog-action"; import { Logo } from "../shared/logo"; @@ -770,9 +767,7 @@ export default function Page({ children }: Props) { setIsLoaded(true); }, []); - const router = useRouter(); const pathname = usePathname(); - const _currentPath = router.pathname; const { data: auth } = api.user.get.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); diff --git a/apps/dokploy/components/layouts/update-server.tsx b/apps/dokploy/components/layouts/update-server.tsx index 42cac69f4..6f01682c0 100644 --- a/apps/dokploy/components/layouts/update-server.tsx +++ b/apps/dokploy/components/layouts/update-server.tsx @@ -1,8 +1,7 @@ -import { api } from "@/utils/api"; import type { IUpdateData } from "@dokploy/server/index"; import { Download } from "lucide-react"; -import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; +import { api } from "@/utils/api"; import UpdateServer from "../dashboard/settings/web-server/update-server"; import { Button } from "../ui/button"; import { @@ -11,6 +10,7 @@ import { TooltipProvider, TooltipTrigger, } from "../ui/tooltip"; + const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; export const UpdateServerButton = () => { @@ -18,7 +18,6 @@ export const UpdateServerButton = () => { latestVersion: null, updateAvailable: false, }); - const _router = useRouter(); const { data: isCloud } = api.settings.isCloud.useQuery(); const { mutateAsync: getUpdateData } = api.settings.getUpdateData.useMutation(); @@ -26,9 +25,6 @@ export const UpdateServerButton = () => { const checkUpdatesIntervalRef = useRef(null); - if (isCloud) { - return null; - } useEffect(() => { // Handling of automatic check for server updates if (isCloud) { @@ -77,7 +73,7 @@ export const UpdateServerButton = () => { }; }, []); - return updateData.updateAvailable ? ( + return !isCloud && updateData.updateAvailable ? (
{ )} ) : ( - <> - {data?.role === "owner" && ( - { - router.push("/dashboard/settings/servers"); - }} - > - Servers - - )} - + data?.role === "owner" && ( + { + router.push("/dashboard/settings/servers"); + }} + > + Servers + + ) )} {isCloud && data?.role === "owner" && ( From f26c1c0da60be54fd5db28749ea62f415a3a5698 Mon Sep 17 00:00:00 2001 From: Marukome0743 Date: Mon, 28 Jul 2025 20:32:07 +0900 Subject: [PATCH 02/29] refactor: lint apps/docker/__test__ files --- apps/dokploy/__test__/compose/compose.test.ts | 2 +- apps/dokploy/__test__/compose/config/config-root.test.ts | 3 +-- .../dokploy/__test__/compose/config/config-service.test.ts | 6 ++++-- apps/dokploy/__test__/compose/config/config.test.ts | 3 +-- apps/dokploy/__test__/compose/network/network-root.test.ts | 3 +-- .../__test__/compose/network/network-service.test.ts | 6 ++++-- apps/dokploy/__test__/compose/network/network.test.ts | 6 +++--- apps/dokploy/__test__/compose/secrets/secret-root.test.ts | 3 +-- .../__test__/compose/secrets/secret-services.test.ts | 6 ++++-- apps/dokploy/__test__/compose/secrets/secret.test.ts | 2 +- .../compose/service/service-container-name.test.ts | 3 +-- .../__test__/compose/service/service-depends-on.test.ts | 3 +-- .../__test__/compose/service/service-extends.test.ts | 3 +-- .../dokploy/__test__/compose/service/service-links.test.ts | 3 +-- .../dokploy/__test__/compose/service/service-names.test.ts | 3 +-- apps/dokploy/__test__/compose/service/service.test.ts | 2 +- .../__test__/compose/service/sevice-volumes-from.test.ts | 3 +-- apps/dokploy/__test__/compose/volume/volume-2.test.ts | 7 +++++-- apps/dokploy/__test__/compose/volume/volume-root.test.ts | 3 +-- .../__test__/compose/volume/volume-services.test.ts | 6 ++++-- apps/dokploy/__test__/compose/volume/volume.test.ts | 2 +- apps/dokploy/__test__/deploy/github.test.ts | 2 +- .../__test__/drop/{drop.test.test.ts => drop.test.ts} | 6 +++--- apps/dokploy/__test__/requests/request.test.ts | 1 + apps/dokploy/__test__/traefik/traefik.test.ts | 4 +--- 25 files changed, 45 insertions(+), 46 deletions(-) rename apps/dokploy/__test__/drop/{drop.test.test.ts => drop.test.ts} (99%) diff --git a/apps/dokploy/__test__/compose/compose.test.ts b/apps/dokploy/__test__/compose/compose.test.ts index 9d4ba20f5..69d3a5212 100644 --- a/apps/dokploy/__test__/compose/compose.test.ts +++ b/apps/dokploy/__test__/compose/compose.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllProperties } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToAllProperties } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-root.test.ts b/apps/dokploy/__test__/compose/config/config-root.test.ts index 4b40c073e..668e17902 100644 --- a/apps/dokploy/__test__/compose/config/config-root.test.ts +++ b/apps/dokploy/__test__/compose/config/config-root.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToConfigsRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToConfigsRoot, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-service.test.ts b/apps/dokploy/__test__/compose/config/config-service.test.ts index de014eb5e..246872f09 100644 --- a/apps/dokploy/__test__/compose/config/config-service.test.ts +++ b/apps/dokploy/__test__/compose/config/config-service.test.ts @@ -1,6 +1,8 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToConfigsInServices } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { + addSuffixToConfigsInServices, + generateRandomHash, +} from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config.test.ts b/apps/dokploy/__test__/compose/config/config.test.ts index aed3350f5..2d5feeb9a 100644 --- a/apps/dokploy/__test__/compose/config/config.test.ts +++ b/apps/dokploy/__test__/compose/config/config.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToAllConfigs } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToAllConfigs, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network-root.test.ts b/apps/dokploy/__test__/compose/network/network-root.test.ts index 980502fff..c55f6fa86 100644 --- a/apps/dokploy/__test__/compose/network/network-root.test.ts +++ b/apps/dokploy/__test__/compose/network/network-root.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToNetworksRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToNetworksRoot, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network-service.test.ts b/apps/dokploy/__test__/compose/network/network-service.test.ts index ee07d9de9..3cf46d4ab 100644 --- a/apps/dokploy/__test__/compose/network/network-service.test.ts +++ b/apps/dokploy/__test__/compose/network/network-service.test.ts @@ -1,6 +1,8 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNetworks } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { + addSuffixToServiceNetworks, + generateRandomHash, +} from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network.test.ts b/apps/dokploy/__test__/compose/network/network.test.ts index 39cf03958..7ba1c6a83 100644 --- a/apps/dokploy/__test__/compose/network/network.test.ts +++ b/apps/dokploy/__test__/compose/network/network.test.ts @@ -1,10 +1,10 @@ -import { generateRandomHash } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { addSuffixToAllNetworks, + addSuffixToNetworksRoot, addSuffixToServiceNetworks, + generateRandomHash, } from "@dokploy/server"; -import { addSuffixToNetworksRoot } from "@dokploy/server"; -import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts index 1b1898c59..b8cef56e4 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToSecretsRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToSecretsRoot, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts index 5206bbbaf..e12f611d0 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts @@ -1,6 +1,8 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToSecretsInServices } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { + addSuffixToSecretsInServices, + generateRandomHash, +} from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret.test.ts b/apps/dokploy/__test__/compose/secrets/secret.test.ts index d874dc5e7..3ff524ad7 100644 --- a/apps/dokploy/__test__/compose/secrets/secret.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllSecrets } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToAllSecrets } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-container-name.test.ts b/apps/dokploy/__test__/compose/service/service-container-name.test.ts index bcb51fd04..6ad45c588 100644 --- a/apps/dokploy/__test__/compose/service/service-container-name.test.ts +++ b/apps/dokploy/__test__/compose/service/service-container-name.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts index b27414be5..14a5789c4 100644 --- a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts +++ b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-extends.test.ts b/apps/dokploy/__test__/compose/service/service-extends.test.ts index 8309a32fd..0b7e92c53 100644 --- a/apps/dokploy/__test__/compose/service/service-extends.test.ts +++ b/apps/dokploy/__test__/compose/service/service-extends.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-links.test.ts b/apps/dokploy/__test__/compose/service/service-links.test.ts index 5f9b01ab2..6c8cde39e 100644 --- a/apps/dokploy/__test__/compose/service/service-links.test.ts +++ b/apps/dokploy/__test__/compose/service/service-links.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-names.test.ts b/apps/dokploy/__test__/compose/service/service-names.test.ts index 936a32ecc..c65299b03 100644 --- a/apps/dokploy/__test__/compose/service/service-names.test.ts +++ b/apps/dokploy/__test__/compose/service/service-names.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service.test.ts b/apps/dokploy/__test__/compose/service/service.test.ts index c6050f75a..38895e073 100644 --- a/apps/dokploy/__test__/compose/service/service.test.ts +++ b/apps/dokploy/__test__/compose/service/service.test.ts @@ -1,8 +1,8 @@ +import type { ComposeSpecification } from "@dokploy/server"; import { addSuffixToAllServiceNames, addSuffixToServiceNames, } from "@dokploy/server"; -import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts index 8066a6dd7..8aa8296e8 100644 --- a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts +++ b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToServiceNames } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-2.test.ts b/apps/dokploy/__test__/compose/volume/volume-2.test.ts index 61cba82d3..6aa9d01d3 100644 --- a/apps/dokploy/__test__/compose/volume/volume-2.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-2.test.ts @@ -1,6 +1,9 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToAllVolumes, addSuffixToVolumesRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { + addSuffixToAllVolumes, + addSuffixToVolumesRoot, + generateRandomHash, +} from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-root.test.ts b/apps/dokploy/__test__/compose/volume/volume-root.test.ts index d91cb64d3..80db1f0cc 100644 --- a/apps/dokploy/__test__/compose/volume/volume-root.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-root.test.ts @@ -1,6 +1,5 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToVolumesRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToVolumesRoot, generateRandomHash } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-services.test.ts b/apps/dokploy/__test__/compose/volume/volume-services.test.ts index 04a1a45ae..0e9cb018f 100644 --- a/apps/dokploy/__test__/compose/volume/volume-services.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-services.test.ts @@ -1,6 +1,8 @@ -import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToVolumesInServices } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { + addSuffixToVolumesInServices, + generateRandomHash, +} from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume.test.ts b/apps/dokploy/__test__/compose/volume/volume.test.ts index 6c4344762..6f8e76708 100644 --- a/apps/dokploy/__test__/compose/volume/volume.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllVolumes } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; +import { addSuffixToAllVolumes } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/deploy/github.test.ts b/apps/dokploy/__test__/deploy/github.test.ts index 18d7619ab..03805b08d 100644 --- a/apps/dokploy/__test__/deploy/github.test.ts +++ b/apps/dokploy/__test__/deploy/github.test.ts @@ -1,5 +1,5 @@ -import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]"; import { describe, expect, it } from "vitest"; +import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]"; describe("GitHub Webhook Skip CI", () => { const mockGithubHeaders = { diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.ts similarity index 99% rename from apps/dokploy/__test__/drop/drop.test.test.ts rename to apps/dokploy/__test__/drop/drop.test.ts index 8fda40e51..3945e6a0c 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.ts @@ -1,12 +1,12 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { paths } from "@dokploy/server/constants"; -const { APPLICATIONS_PATH } = paths(); import type { ApplicationNested } from "@dokploy/server"; import { unzipDrop } from "@dokploy/server"; +import { paths } from "@dokploy/server/constants"; import AdmZip from "adm-zip"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +const { APPLICATIONS_PATH } = paths(); vi.mock("@dokploy/server/constants", async (importOriginal) => { const actual = await importOriginal(); return { @@ -142,7 +142,7 @@ describe("unzipDrop using real zip files", () => { const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code"); const zip = new AdmZip("./__test__/drop/zips/single-file.zip"); console.log(`Output Path: ${outputPath}`); - const zipBuffer = zip.toBuffer(); + const zipBuffer = zip.toBuffer() as Buffer; const file = new File([zipBuffer], "single.zip"); await unzipDrop(file, baseApp); const files = await fs.readdir(outputPath, { withFileTypes: true }); diff --git a/apps/dokploy/__test__/requests/request.test.ts b/apps/dokploy/__test__/requests/request.test.ts index 997bd9ec5..53ca8d777 100644 --- a/apps/dokploy/__test__/requests/request.test.ts +++ b/apps/dokploy/__test__/requests/request.test.ts @@ -1,5 +1,6 @@ import { parseRawConfig, processLogs } from "@dokploy/server"; import { describe, expect, it } from "vitest"; + const sampleLogEntry = `{"ClientAddr":"172.19.0.1:56732","ClientHost":"172.19.0.1","ClientPort":"56732","ClientUsername":"-","DownstreamContentSize":0,"DownstreamStatus":304,"Duration":14729375,"OriginContentSize":0,"OriginDuration":14051833,"OriginStatus":304,"Overhead":677542,"RequestAddr":"s222-umami-c381af.traefik.me","RequestContentSize":0,"RequestCount":122,"RequestHost":"s222-umami-c381af.traefik.me","RequestMethod":"GET","RequestPath":"/dashboard?_rsc=1rugv","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"s222-umami-60e104-47-web@docker","ServiceAddr":"10.0.1.15:3000","ServiceName":"s222-umami-60e104-47-web@docker","ServiceURL":{"Scheme":"http","Opaque":"","User":null,"Host":"10.0.1.15:3000","Path":"","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"StartLocal":"2024-08-25T04:34:37.306691884Z","StartUTC":"2024-08-25T04:34:37.306691884Z","entryPointName":"web","level":"info","msg":"","time":"2024-08-25T04:34:37Z"}`; describe("processLogs", () => { diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index c1517d530..2bea75fb7 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -1,6 +1,4 @@ -import type { Domain } from "@dokploy/server"; -import type { Redirect } from "@dokploy/server"; -import type { ApplicationNested } from "@dokploy/server"; +import type { ApplicationNested, Domain, Redirect } from "@dokploy/server"; import { createRouterConfig } from "@dokploy/server"; import { expect, test } from "vitest"; From 30c2c7afb014f981e66df80f59e3991872257d74 Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Wed, 23 Jul 2025 20:12:21 +0200 Subject: [PATCH 03/29] feat(dashboard): generate user fallback avatar using user email. Allow user to select the default avatar. --- .../dashboard/settings/profile/profile-form.tsx | 17 ++++++++++++++++- apps/dokploy/components/layouts/user-nav.tsx | 3 ++- apps/dokploy/lib/utils.ts | 12 ++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 49fdfd2dd..eaa48a2a6 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -1,3 +1,4 @@ +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -19,7 +20,7 @@ import { import { Input } from "@/components/ui/input"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Switch } from "@/components/ui/switch"; -import { generateSHA256Hash } from "@/lib/utils"; +import { generateSHA256Hash, getFallbackAvatarInitials } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { Loader2, User } from "lucide-react"; @@ -257,6 +258,20 @@ export const ProfileForm = () => { value={field.value} className="flex flex-row flex-wrap gap-2 max-xl:justify-center" > + + + + + + + + {getFallbackAvatarInitials(data?.user?.email)} + + + {availableAvatars.map((image) => ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 05c601f6e..a339f05f4 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -17,6 +17,7 @@ import { } from "@/components/ui/select"; import { authClient } from "@/lib/auth-client"; import { Languages } from "@/lib/languages"; +import { getFallbackAvatarInitials } from "@/lib/utils"; import { api } from "@/utils/api"; import useLocale from "@/utils/hooks/use-locale"; import { ChevronsUpDown } from "lucide-react"; @@ -46,7 +47,7 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - CN + {getFallbackAvatarInitials(data?.user?.email)}
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index b763e9ee9..cf9f2cc94 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -27,3 +27,15 @@ export function formatTimestamp(timestamp: string | number) { return "Fecha inválida"; } } + +export function getFallbackAvatarInitials(email: string | undefined): string { + if (typeof email === "undefined") return "CN"; + + const [emailUsername = ""] = email.split('@'); + const parts = emailUsername.split(/[\._-]+/).filter(Boolean); + if (parts.length >= 2) { + // @ts-ignore we are sure parts[0] and parts[1] exist + return (parts[0]?.charAt(0) + parts[1].charAt(0)).toUpperCase(); + } + return emailUsername.slice(0, 2).toUpperCase(); +} From f8261b53642e000aa60bc2fc86bc30bceec997aa Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Mon, 28 Jul 2025 19:20:05 +0200 Subject: [PATCH 04/29] feat(dashboard): use username instead of email for the generation of the fallback avatar image --- .../dashboard/settings/profile/profile-form.tsx | 2 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- apps/dokploy/lib/utils.ts | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index eaa48a2a6..794291b7d 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -268,7 +268,7 @@ export const ProfileForm = () => { - {getFallbackAvatarInitials(data?.user?.email)} + {getFallbackAvatarInitials(data?.user?.name)} diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index a339f05f4..aa7f4ddd9 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -47,7 +47,7 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - {getFallbackAvatarInitials(data?.user?.email)} + {getFallbackAvatarInitials(data?.user?.name)}
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index cf9f2cc94..dc0dfe351 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -28,14 +28,11 @@ export function formatTimestamp(timestamp: string | number) { } } -export function getFallbackAvatarInitials(email: string | undefined): string { - if (typeof email === "undefined") return "CN"; - - const [emailUsername = ""] = email.split('@'); - const parts = emailUsername.split(/[\._-]+/).filter(Boolean); - if (parts.length >= 2) { - // @ts-ignore we are sure parts[0] and parts[1] exist - return (parts[0]?.charAt(0) + parts[1].charAt(0)).toUpperCase(); +export function getFallbackAvatarInitials(fullName: string | undefined): string { + if (typeof fullName === "undefined" || fullName === "") return "CN"; + const [ name = "", surname = "" ] = fullName.split(" "); + if (surname === "") { + return (name.substring(0,2)).toUpperCase(); } - return emailUsername.slice(0, 2).toUpperCase(); + return (name.charAt(0) + surname.charAt(0)).toUpperCase(); } From 9bc6411c98e0fc8ecf6465c92d9831126186b726 Mon Sep 17 00:00:00 2001 From: rainwashed <60084007+rainwashed@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:05:30 -0400 Subject: [PATCH 05/29] fix: github app creation name conflicting with already existing Dokploy-Time names appended a 5-char random string to the name creation as to prevent conflicts with other existing Dokploy GitHub apps. --- .../settings/git/github/add-github-provider.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx index af7d58544..3b22bc7af 100644 --- a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx @@ -24,12 +24,14 @@ export const AddGithubProvider = () => { const [isOrganization, setIsOrganization] = useState(false); const [organizationName, setOrganization] = useState(""); + const randomString = () => Math.random().toString(36).slice(2, 8); + useEffect(() => { const url = document.location.origin; const manifest = JSON.stringify( { redirect_url: `${origin}/api/providers/github/setup?organizationId=${activeOrganization?.id}&userId=${session?.user?.id}`, - name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`, + name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}-${randomString()}`, url: origin, hook_attributes: { url: `${url}/api/deploy/github`, @@ -118,11 +120,10 @@ export const AddGithubProvider = () => { : "https://github.com/settings/installations" } className={`text-muted-foreground text-sm hover:underline duration-300 - ${ - isOrganization && !organizationName - ? "pointer-events-none opacity-50" - : "" - }`} + ${isOrganization && !organizationName + ? "pointer-events-none opacity-50" + : "" + }`} target="_blank" rel="noopener noreferrer" > From f9210d3165a6ac08848ae3ab5a0e4681ad92e9a1 Mon Sep 17 00:00:00 2001 From: Daniele Pintore Date: Mon, 28 Jul 2025 23:39:06 +0200 Subject: [PATCH 06/29] lint: formatted changes using biome --- .../dashboard/settings/profile/profile-form.tsx | 10 +++++++--- apps/dokploy/components/layouts/user-nav.tsx | 4 +++- apps/dokploy/lib/utils.ts | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 794291b7d..89014601e 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -267,9 +267,13 @@ export const ProfileForm = () => { /> - - {getFallbackAvatarInitials(data?.user?.name)} - + + + {getFallbackAvatarInitials( + data?.user?.name, + )} + + {availableAvatars.map((image) => ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index aa7f4ddd9..7a906aa6a 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -47,7 +47,9 @@ export const UserNav = () => { src={data?.user?.image || ""} alt={data?.user?.image || ""} /> - {getFallbackAvatarInitials(data?.user?.name)} + + {getFallbackAvatarInitials(data?.user?.name)} +
Account diff --git a/apps/dokploy/lib/utils.ts b/apps/dokploy/lib/utils.ts index dc0dfe351..f01faa4ec 100644 --- a/apps/dokploy/lib/utils.ts +++ b/apps/dokploy/lib/utils.ts @@ -28,11 +28,13 @@ export function formatTimestamp(timestamp: string | number) { } } -export function getFallbackAvatarInitials(fullName: string | undefined): string { - if (typeof fullName === "undefined" || fullName === "") return "CN"; - const [ name = "", surname = "" ] = fullName.split(" "); - if (surname === "") { - return (name.substring(0,2)).toUpperCase(); - } - return (name.charAt(0) + surname.charAt(0)).toUpperCase(); +export function getFallbackAvatarInitials( + fullName: string | undefined, +): string { + if (typeof fullName === "undefined" || fullName === "") return "CN"; + const [name = "", surname = ""] = fullName.split(" "); + if (surname === "") { + return name.substring(0, 2).toUpperCase(); + } + return (name.charAt(0) + surname.charAt(0)).toUpperCase(); } From e8f36f8ba5c273b41829bbb49cbd3be7ba68f9cd Mon Sep 17 00:00:00 2001 From: A-D-E Date: Wed, 30 Jul 2025 14:52:25 +0200 Subject: [PATCH 07/29] The getGitlabBranches function was only returning the first 20 branches due to GitLab's default API pagination limit. This prevented users from accessing branches in repositories with more than 20 branches. Changes: - Add pagination loop to fetch all branches across multiple pages - Set per_page to 100 (GitLab's maximum) for efficiency - Add safety check using x-total header to prevent unnecessary requests - Follow the same pagination pattern as validateGitlabProvider function Fixes issue where branch selection was limited to first 20 branches in repositories with many branches. --- packages/server/src/utils/providers/gitlab.ts | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 4f9e011a1..65c280e0e 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -303,22 +303,41 @@ export const getGitlabBranches = async (input: { const gitlabProvider = await findGitlabById(input.gitlabId); - const branchesResponse = await fetch( - `https://gitlab.com/api/v4/projects/${input.id}/repository/branches`, - { - headers: { - Authorization: `Bearer ${gitlabProvider.accessToken}`, - }, - }, - ); + const allBranches = []; + let page = 1; + const perPage = 100; // GitLab's max per page is 100 - if (!branchesResponse.ok) { - throw new Error(`Failed to fetch branches: ${branchesResponse.statusText}`); + while (true) { + const branchesResponse = await fetch( + `https://gitlab.com/api/v4/projects/${input.id}/repository/branches?page=${page}&per_page=${perPage}`, + { + headers: { + Authorization: `Bearer ${gitlabProvider.accessToken}`, + }, + }, + ); + + if (!branchesResponse.ok) { + throw new Error(`Failed to fetch branches: ${branchesResponse.statusText}`); + } + + const branches = await branchesResponse.json(); + + if (branches.length === 0) { + break; + } + + allBranches.push(...branches); + page++; + + // Check if we've reached the total using headers (optional optimization) + const total = branchesResponse.headers.get("x-total"); + if (total && allBranches.length >= Number.parseInt(total)) { + break; + } } - const branches = await branchesResponse.json(); - - return branches as { + return allBranches as { id: string; name: string; commit: { From 794e03460fddcbebf56638f310d9f086bbbeee8e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:34:57 -0600 Subject: [PATCH 08/29] refactor(application): update application handling to support multiple app names and improve data structure --- .../dashboard/swarm/applications/show-applications.tsx | 6 +++--- apps/dokploy/server/api/routers/swarm.ts | 9 +++++++-- packages/server/src/services/docker.ts | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx b/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx index 626e2a282..6734991f1 100644 --- a/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx +++ b/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx @@ -1,3 +1,4 @@ +import { Layers, Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -8,7 +9,6 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { api } from "@/utils/api"; -import { Layers, Loader2 } from "lucide-react"; import { type ApplicationList, columns } from "./columns"; import { DataTable } from "./data-table"; @@ -20,10 +20,10 @@ export const ShowNodeApplications = ({ serverId }: Props) => { const { data: NodeApps, isLoading: NodeAppsLoading } = api.swarm.getNodeApps.useQuery({ serverId }); - let applicationList = ""; + let applicationList: string[] = []; if (NodeApps && NodeApps.length > 0) { - applicationList = NodeApps.map((app) => app.Name).join(" "); + applicationList = NodeApps.map((app) => app.Name); } const { data: NodeAppDetails, isLoading: NodeAppDetailsLoading } = diff --git a/apps/dokploy/server/api/routers/swarm.ts b/apps/dokploy/server/api/routers/swarm.ts index 997eba310..1c3abdf37 100644 --- a/apps/dokploy/server/api/routers/swarm.ts +++ b/apps/dokploy/server/api/routers/swarm.ts @@ -1,10 +1,10 @@ import { + findServerById, getApplicationInfo, getNodeApplications, getNodeInfo, getSwarmNodes, } from "@dokploy/server"; -import { findServerById } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { createTRPCRouter, protectedProcedure } from "../trpc"; @@ -55,7 +55,12 @@ export const swarmRouter = createTRPCRouter({ getAppInfos: protectedProcedure .input( z.object({ - appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."), + appName: z + .string() + .min(1) + .regex(containerIdRegex, "Invalid app name.") + .array() + .min(1), serverId: z.string().optional(), }), ) diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index 2e315d008..2194c89c6 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -441,13 +441,13 @@ export const getNodeApplications = async (serverId?: string) => { }; export const getApplicationInfo = async ( - appName: string, + appNames: string[], serverId?: string, ) => { try { let stdout = ""; let stderr = ""; - const command = `docker service ps ${appName} --format '{{json .}}' --no-trunc`; + const command = `docker service ps ${appNames.join(" ")} --format '{{json .}}' --no-trunc`; if (serverId) { const result = await execAsyncRemote(serverId, command); From 295b6df5e1557673a383fe6ebc4c0950ae241fea Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:15:29 -0600 Subject: [PATCH 09/29] refactor(compose): reorganize imports and simplify command execution for starting Docker Compose --- packages/server/src/services/compose.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 8b3bd3b0f..bb1b2e8a0 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -1,8 +1,12 @@ import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import { db } from "@dokploy/server/db"; -import { type apiCreateCompose, compose } from "@dokploy/server/db/schema"; -import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; +import { + type apiCreateCompose, + buildAppName, + cleanAppName, + compose, +} from "@dokploy/server/db/schema"; import { buildCompose, getBuildComposeCommand, @@ -516,19 +520,20 @@ export const startCompose = async (composeId: string) => { const compose = await findComposeById(composeId); try { const { COMPOSE_PATH } = paths(!!compose.serverId); + + const projectPath = join(COMPOSE_PATH, compose.appName, "code"); + const path = + compose.sourceType === "raw" ? "docker-compose.yml" : compose.composePath; + const baseCommand = `docker compose -p ${compose.appName} -f ${path} up -d`; if (compose.composeType === "docker-compose") { if (compose.serverId) { await execAsyncRemote( compose.serverId, - `cd ${join( - COMPOSE_PATH, - compose.appName, - "code", - )} && docker compose -p ${compose.appName} up -d`, + `cd ${projectPath} && ${baseCommand}`, ); } else { - await execAsync(`docker compose -p ${compose.appName} up -d`, { - cwd: join(COMPOSE_PATH, compose.appName, "code"), + await execAsync(baseCommand, { + cwd: projectPath, }); } } From 0f5cf37757ede1b4736fb3ac7d6a6298683bec7a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:27:35 -0600 Subject: [PATCH 10/29] refactor(backup): consolidate utility imports and add local backup cleanup after S3 upload --- packages/server/src/utils/volume-backups/backup.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/volume-backups/backup.ts b/packages/server/src/utils/volume-backups/backup.ts index 3b074f998..cc613ffa9 100644 --- a/packages/server/src/utils/volume-backups/backup.ts +++ b/packages/server/src/utils/volume-backups/backup.ts @@ -2,8 +2,7 @@ import path from "node:path"; import { paths } from "@dokploy/server/constants"; import { findComposeById } from "@dokploy/server/services/compose"; import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups"; -import { normalizeS3Path } from "../backups/utils"; -import { getS3Credentials } from "../backups/utils"; +import { getS3Credentials, normalizeS3Path } from "../backups/utils"; export const backupVolume = async ( volumeBackup: Awaited>, @@ -37,6 +36,9 @@ export const backupVolume = async ( echo "Starting upload to S3..." ${rcloneCommand} echo "Upload to S3 done ✅" + echo "Cleaning up local backup file..." + rm "${volumeBackupPath}/${backupFileName}" + echo "Local backup file cleaned up ✅" `; if (!turnOff) { From 0bcc59f90faf37feb3982622ea7f3900198f0bb6 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 2 Aug 2025 06:19:09 +0000 Subject: [PATCH 11/29] [autofix.ci] apply automated fixes --- .../settings/git/github/add-github-provider.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx index 3b22bc7af..83a4c8b42 100644 --- a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx @@ -120,10 +120,11 @@ export const AddGithubProvider = () => { : "https://github.com/settings/installations" } className={`text-muted-foreground text-sm hover:underline duration-300 - ${isOrganization && !organizationName - ? "pointer-events-none opacity-50" - : "" - }`} + ${ + isOrganization && !organizationName + ? "pointer-events-none opacity-50" + : "" + }`} target="_blank" rel="noopener noreferrer" > From 144c74e7f7fc91e1612fa7e30f883c1fa96a2c1c Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sat, 2 Aug 2025 20:38:28 +0900 Subject: [PATCH 12/29] docs: polish `README.md` --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bd27474e0..fb2ee82a5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- Dokploy - Open Source Alternative to Vercel, Heroku and Netlify. + Dokploy - Open Source Alternative to Vercel, Heroku and Netlify.

@@ -13,7 +13,7 @@ Dokploy is a free, self-hostable Platform as a Service (PaaS) that simplifies the deployment and management of applications and databases. -### Features +## ✨ Features Dokploy includes multiple features to make your life easier. @@ -43,7 +43,7 @@ curl -sSL https://dokploy.com/install.sh | sh For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). -## Sponsors +## ♥️ Sponsors 🙏 We're deeply grateful to all our sponsors who make Dokploy possible! Your support helps cover the costs of hosting, testing, and developing new features. @@ -95,7 +95,6 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). ### Community Backers 🤝 - #### Organizations: [Sponsors on Open Collective](https://opencollective.com/dokploy) @@ -107,15 +106,15 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). ### Contributors 🤝 - + Contributors -## Video Tutorial +## 📺 Video Tutorial Watch the video -## Contributing +## 🤝 Contributing Check out the [Contributing Guide](CONTRIBUTING.md) for more information. From 39d46a51b34031ce5d78c2b6bda5dd36046ba1fc Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 13:08:46 -0600 Subject: [PATCH 13/29] chore: update Railpack version to 0.2.2 in Dockerfile and related scripts --- Dockerfile | 2 +- packages/server/src/setup/server-setup.ts | 9 ++++----- packages/server/src/utils/builders/railpack.ts | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4d18a99ab..11310b18e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,7 +58,7 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ && pnpm install -g tsx # Install Railpack -ARG RAILPACK_VERSION=0.0.64 +ARG RAILPACK_VERSION=0.2.2 RUN curl -sSL https://railpack.com/install.sh | bash # Install buildpacks diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts index 1a4554084..a9ca1c371 100644 --- a/packages/server/src/setup/server-setup.ts +++ b/packages/server/src/setup/server-setup.ts @@ -6,18 +6,17 @@ import { } from "@dokploy/server/services/deployment"; import { findServerById } from "@dokploy/server/services/server"; import { + getDefaultMiddlewares, + getDefaultServerTraefikConfig, TRAEFIK_HTTP3_PORT, TRAEFIK_PORT, TRAEFIK_SSL_PORT, TRAEFIK_VERSION, - getDefaultMiddlewares, - getDefaultServerTraefikConfig, } from "@dokploy/server/setup/traefik-setup"; +import slug from "slugify"; import { Client } from "ssh2"; import { recreateDirectory } from "../utils/filesystem/directory"; -import slug from "slugify"; - export const slugify = (text: string | undefined) => { if (!text) { return ""; @@ -609,7 +608,7 @@ const installRailpack = () => ` if command_exists railpack; then echo "Railpack already installed ✅" else - export RAILPACK_VERSION=0.0.64 + export RAILPACK_VERSION=0.2.2 bash -c "$(curl -fsSL https://railpack.com/install.sh)" echo "Railpack version $RAILPACK_VERSION installed ✅" fi diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts index 991720f3a..acbf7b97c 100644 --- a/packages/server/src/utils/builders/railpack.ts +++ b/packages/server/src/utils/builders/railpack.ts @@ -1,7 +1,6 @@ import { createHash } from "node:crypto"; import type { WriteStream } from "node:fs"; import { nanoid } from "nanoid"; -import type { ApplicationNested } from "."; import { parseEnvironmentKeyValuePair, prepareEnvironmentVariables, @@ -9,6 +8,7 @@ import { import { getBuildAppDirectory } from "../filesystem/directory"; import { execAsync } from "../process/execAsync"; import { spawnAsync } from "../process/spawnAsync"; +import type { ApplicationNested } from "."; const calculateSecretsHash = (envVariables: string[]): string => { const hash = createHash("sha256"); @@ -75,7 +75,7 @@ export const buildRailpack = async ( ] : []), "--build-arg", - "BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.0.64", + "BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.2.2", "-f", `${buildAppDirectory}/railpack-plan.json`, "--output", From 346eb249264660f675bab479b274901d38033237 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 13:20:00 -0600 Subject: [PATCH 14/29] feat(dashboard): add manual input option for service name selection in domain handling --- .../application/domains/handle-domain.tsx | 185 +++++++++++------- biome.json | 3 +- 2 files changed, 119 insertions(+), 69 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx index 9069542d9..9d7a074f9 100644 --- a/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/handle-domain.tsx @@ -1,3 +1,10 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { DatabaseZap, Dices, RefreshCw } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import z from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { @@ -34,14 +41,6 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; - -import { zodResolver } from "@hookform/resolvers/zod"; -import { DatabaseZap, Dices, RefreshCw } from "lucide-react"; -import Link from "next/link"; -import z from "zod"; export type CacheType = "fetch" | "cache"; @@ -123,6 +122,7 @@ interface Props { export const AddDomain = ({ id, type, domainId = "", children }: Props) => { const [isOpen, setIsOpen] = useState(false); const [cacheType, setCacheType] = useState("cache"); + const [isManualInput, setIsManualInput] = useState(false); const utils = api.useUtils(); const { data, refetch } = api.domain.one.useQuery( @@ -325,46 +325,126 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => { Service Name
- + ) : ( + + + + )} + {!isManualInput && ( + <> + + + + + + +

+ Fetch: Will clone the repository and + load the services +

+
+
+
+ + + + + + +

+ Cache: If you previously deployed this + compose, it will read the services + from the last deployment/fetch from + the repository +

+
+
+
+ + )} { className="max-w-[10rem]" >

- Fetch: Will clone the repository and load - the services -

-
-
-
- - - - - - -

- Cache: If you previously deployed this - compose, it will read the services from - the last deployment/fetch from the - repository + {isManualInput + ? "Switch to service selection" + : "Enter service name manually"}

diff --git a/biome.json b/biome.json index a26024c86..519deba7b 100644 --- a/biome.json +++ b/biome.json @@ -27,7 +27,8 @@ "noUnsafeOptionalChaining": "off", "noUnusedImports": "error", "noUnusedFunctionParameters": "error", - "noUnusedVariables": "error" + "noUnusedVariables": "error", + "useHookAtTopLevel": "off" }, "style": { "noNonNullAssertion": "off", From 0755de03c26ed817d6126caa76150b9a9a9ca1c1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 16:06:46 -0600 Subject: [PATCH 15/29] feat(mount): refactor updateMount logic and add updateFileMount function for handling file mounts --- packages/server/src/services/mount.ts | 42 +++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/server/src/services/mount.ts b/packages/server/src/services/mount.ts index 91d67a211..d64fef6f1 100644 --- a/packages/server/src/services/mount.ts +++ b/packages/server/src/services/mount.ts @@ -2,18 +2,22 @@ import path from "node:path"; import { paths } from "@dokploy/server/constants"; import { db } from "@dokploy/server/db"; import { - type ServiceType, type apiCreateMount, mounts, + type ServiceType, } from "@dokploy/server/db/schema"; import { createFile, + encodeBase64, getCreateFileCommand, } from "@dokploy/server/utils/docker/utils"; import { removeFileOrDirectory } from "@dokploy/server/utils/filesystem/directory"; -import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync"; +import { + execAsync, + execAsyncRemote, +} from "@dokploy/server/utils/process/execAsync"; import { TRPCError } from "@trpc/server"; -import { type SQL, eq, sql } from "drizzle-orm"; +import { eq, type SQL, sql } from "drizzle-orm"; export type Mount = typeof mounts.$inferSelect; @@ -123,7 +127,7 @@ export const updateMount = async ( mountId: string, mountData: Partial, ) => { - return await db.transaction(async (tx) => { + const mount = await db.transaction(async (tx) => { const mount = await tx .update(mounts) .set({ @@ -140,13 +144,13 @@ export const updateMount = async ( }); } - if (mount.type === "file") { - await deleteFileMount(mountId); - await createFileMount(mountId); - } - return await findMountById(mountId); }); + + if (mount.type === "file") { + await updateFileMount(mountId); + } + return mount; }; export const findMountsByApplicationId = async ( @@ -198,6 +202,26 @@ export const deleteMount = async (mountId: string) => { return deletedMount[0]; }; +export const updateFileMount = async (mountId: string) => { + const mount = await findMountById(mountId); + if (!mount || !mount.filePath) return; + const basePath = await getBaseFilesPath(mountId); + const fullPath = path.join(basePath, mount.filePath); + + try { + const serverId = await getServerId(mount); + const encodedContent = encodeBase64(mount.content || ""); + const command = `echo "${encodedContent}" | base64 -d > ${fullPath}`; + if (serverId) { + await execAsyncRemote(serverId, command); + } else { + await execAsync(command); + } + } catch { + console.log("Error updating file mount"); + } +}; + export const deleteFileMount = async (mountId: string) => { const mount = await findMountById(mountId); if (!mount.filePath) return; From 201f07c084b43562e110274317f1679c89606552 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 16:13:51 -0600 Subject: [PATCH 16/29] fix(volumes): adjust layout for volume display and improve conditional rendering --- .../advanced/volumes/show-volumes.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index 2a2d2c032..d3803c42a 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -1,3 +1,5 @@ +import { Package, Trash2 } from "lucide-react"; +import { toast } from "sonner"; import { AlertBlock } from "@/components/shared/alert-block"; import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; @@ -9,11 +11,10 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { Package, Trash2 } from "lucide-react"; -import { toast } from "sonner"; import type { ServiceType } from "../show-resources"; import { AddVolumes } from "./add-volumes"; import { UpdateVolume } from "./update-volume"; + interface Props { id: string; type: ServiceType | "compose"; @@ -80,7 +81,7 @@ export const ShowVolumes = ({ id, type }: Props) => { className="flex w-full flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-10 border rounded-lg p-4" > {/* */} -
+
Mount Type @@ -112,21 +113,21 @@ export const ShowVolumes = ({ id, type }: Props) => {
)} - {mount.type === "file" ? ( + {mount.type === "file" && (
File Path {mount.filePath}
- ) : ( -
- Mount Path - - {mount.mountPath} - -
)} + +
+ Mount Path + + {mount.mountPath} + +
Date: Sat, 2 Aug 2025 18:40:40 -0600 Subject: [PATCH 17/29] fix(backups): change backup file extension from .dump.gz to .sql.gz for consistency --- packages/server/src/utils/backups/compose.ts | 2 +- packages/server/src/utils/backups/index.ts | 9 ++++----- packages/server/src/utils/backups/mongo.ts | 2 +- packages/server/src/utils/restore/utils.ts | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts index 2fb808198..f260ffa08 100644 --- a/packages/server/src/utils/backups/compose.ts +++ b/packages/server/src/utils/backups/compose.ts @@ -17,7 +17,7 @@ export const runComposeBackup = async ( const project = await findProjectById(projectId); const { prefix, databaseType } = backup; const destination = backup.destination; - const backupFileName = `${new Date().toISOString()}.dump.gz`; + const backupFileName = `${new Date().toISOString()}.sql.gz`; const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`; const deployment = await createDeploymentBackup({ backupId: backup.backupId, diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index 5b74d92fc..6ce8f9e55 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -1,7 +1,11 @@ import path from "node:path"; +import { member } from "@dokploy/server/db/schema"; +import type { BackupSchedule } from "@dokploy/server/services/backup"; import { getAllServers } from "@dokploy/server/services/server"; +import { eq } from "drizzle-orm"; import { scheduleJob } from "node-schedule"; import { db } from "../../db/index"; +import { startLogCleanup } from "../access-log/handler"; import { cleanUpDockerBuilder, cleanUpSystemPrune, @@ -11,11 +15,6 @@ import { sendDockerCleanupNotifications } from "../notifications/docker-cleanup" import { execAsync, execAsyncRemote } from "../process/execAsync"; import { getS3Credentials, scheduleBackup } from "./utils"; -import { member } from "@dokploy/server/db/schema"; -import type { BackupSchedule } from "@dokploy/server/services/backup"; -import { eq } from "drizzle-orm"; -import { startLogCleanup } from "../access-log/handler"; - export const initCronJobs = async () => { console.log("Setting up cron jobs...."); diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts index e626efa01..6a74f1d10 100644 --- a/packages/server/src/utils/backups/mongo.ts +++ b/packages/server/src/utils/backups/mongo.ts @@ -14,7 +14,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { const project = await findProjectById(projectId); const { prefix } = backup; const destination = backup.destination; - const backupFileName = `${new Date().toISOString()}.dump.gz`; + const backupFileName = `${new Date().toISOString()}.sql.gz`; const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`; const deployment = await createDeploymentBackup({ backupId: backup.backupId, diff --git a/packages/server/src/utils/restore/utils.ts b/packages/server/src/utils/restore/utils.ts index 17be49c97..c46077238 100644 --- a/packages/server/src/utils/restore/utils.ts +++ b/packages/server/src/utils/restore/utils.ts @@ -81,7 +81,7 @@ const getMongoSpecificCommand = ( backupFile: string, ): string => { const tempDir = "/tmp/dokploy-restore"; - const fileName = backupFile.split("/").pop() || "backup.dump.gz"; + const fileName = backupFile.split("/").pop() || "backup.sql.gz"; const decompressedName = fileName.replace(".gz", ""); return ` rm -rf ${tempDir} && \ From 0b9eaac39011409c8ab68f252c2572a7c54ba4bd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 19:24:11 -0600 Subject: [PATCH 18/29] refactor(domain): enhance middleware handling for Traefik routers and improve path validation --- packages/server/src/utils/docker/domain.ts | 42 ++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index c12083345..0ce138d70 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -313,40 +313,46 @@ export const createDomainLabels = ( `traefik.http.routers.${routerName}.service=${routerName}`, ]; - // Validate stripPath - it should only be used when path is defined and not "/" - if (stripPath) { - if (!path || path === "/") { - console.warn( - `stripPath is enabled but path is not defined or is "/" for domain ${host}`, - ); - } else { - const middlewareName = `stripprefix-${appName}-${uniqueConfigKey}`; + // Collect middlewares for this router + const middlewares: string[] = []; + + // Add HTTPS redirect for web entrypoint (must be first) + if (entrypoint === "web" && https) { + middlewares.push("redirect-to-https@file"); + } + + // Add stripPath middleware if needed + if (stripPath && path && path !== "/") { + const middlewareName = `stripprefix-${appName}-${uniqueConfigKey}`; + // Only define middleware once (on web entrypoint) + if (entrypoint === "web") { labels.push( `traefik.http.middlewares.${middlewareName}.stripprefix.prefixes=${path}`, ); } + middlewares.push(middlewareName); } - // Validate internalPath - ensure it's a valid path format - if (internalPath && internalPath !== "/") { - if (!internalPath.startsWith("/")) { - console.warn( - `internalPath "${internalPath}" should start with "/" and not be empty for domain ${host}`, - ); - } else { - const middlewareName = `addprefix-${appName}-${uniqueConfigKey}`; + // Add internalPath middleware if needed + if (internalPath && internalPath !== "/" && internalPath.startsWith("/")) { + const middlewareName = `addprefix-${appName}-${uniqueConfigKey}`; + // Only define middleware once (on web entrypoint) + if (entrypoint === "web") { labels.push( `traefik.http.middlewares.${middlewareName}.addprefix.prefix=${internalPath}`, ); } + middlewares.push(middlewareName); } - if (entrypoint === "web" && https) { + // Apply middlewares to router if any exist + if (middlewares.length > 0) { labels.push( - `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, + `traefik.http.routers.${routerName}.middlewares=${middlewares.join(",")}`, ); } + // Add TLS configuration for websecure if (entrypoint === "websecure") { if (certificateType === "letsencrypt") { labels.push( From e08fe1dbeaea88970856b05746cd30ddfd146c5a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 19:31:21 -0600 Subject: [PATCH 19/29] test(labels): add comprehensive tests for middleware handling in createDomainLabels function --- .../__test__/compose/domain/labels.test.ts | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 172bff2af..9a75e0a84 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -108,4 +108,136 @@ describe("createDomainLabels", () => { "traefik.http.services.test-app-1-web.loadbalancer.server.port=3000", ); }); + + it("should add stripPath middleware when stripPath is enabled", async () => { + const stripPathDomain = { + ...baseDomain, + path: "/api", + stripPath: true, + }; + const labels = await createDomainLabels(appName, stripPathDomain, "web"); + + expect(labels).toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(labels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1", + ); + }); + + it("should add internalPath middleware when internalPath is set", async () => { + const internalPathDomain = { + ...baseDomain, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels( + appName, + internalPathDomain, + "web", + ); + const websecureLabels = await createDomainLabels( + appName, + internalPathDomain, + "websecure", + ); + + // Middleware definition should only appear in web entrypoint + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // Both routers should reference the middleware + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=addprefix-test-app-1", + ); + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1", + ); + }); + + it("should combine HTTPS redirect with internalPath middleware in correct order", async () => { + const combinedDomain = { + ...baseDomain, + https: true, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels(appName, combinedDomain, "web"); + const websecureLabels = await createDomainLabels( + appName, + combinedDomain, + "websecure", + ); + + // Web entrypoint should have both middlewares with redirect first + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1", + ); + + // Websecure should only have the addprefix middleware + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1", + ); + + // Middleware definition should only appear once (in web) + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + }); + + it("should combine all middlewares in correct order", async () => { + const fullDomain = { + ...baseDomain, + https: true, + path: "/api", + stripPath: true, + internalPath: "/hello", + }; + const webLabels = await createDomainLabels(appName, fullDomain, "web"); + + // Should have all middleware definitions (only in web) + expect(webLabels).toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(webLabels).toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // Should have middlewares in correct order: redirect, stripprefix, addprefix + expect(webLabels).toContain( + "traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1", + ); + }); + + it("should not add middleware definitions for websecure entrypoint", async () => { + const internalPathDomain = { + ...baseDomain, + path: "/api", + stripPath: true, + internalPath: "/hello", + }; + const websecureLabels = await createDomainLabels( + appName, + internalPathDomain, + "websecure", + ); + + // Should not contain any middleware definitions + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api", + ); + expect(websecureLabels).not.toContain( + "traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello", + ); + + // But should reference the middlewares + expect(websecureLabels).toContain( + "traefik.http.routers.test-app-1-websecure.middlewares=stripprefix-test-app-1,addprefix-test-app-1", + ); + }); }); From c21c88d89f39802fb56ed2c92415c233358d88de Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 19:37:10 -0600 Subject: [PATCH 20/29] chore(package): bump version from v0.24.5 to v0.24.6 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 2418644be..2f2fc43d2 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.5", + "version": "v0.24.6", "private": true, "license": "Apache-2.0", "type": "module", From 2790895642017f5fc7e07a16d4d3835a02d79aa8 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 21:20:50 -0600 Subject: [PATCH 21/29] refactor(gitea, gitlab): remove unused parameters and fetch entities by ID --- packages/server/src/utils/providers/gitea.ts | 4 ++-- packages/server/src/utils/providers/gitlab.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/src/utils/providers/gitea.ts b/packages/server/src/utils/providers/gitea.ts index bbd8ed1b4..21265bf37 100644 --- a/packages/server/src/utils/providers/gitea.ts +++ b/packages/server/src/utils/providers/gitea.ts @@ -3,8 +3,8 @@ import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; import type { Compose } from "@dokploy/server/services/compose"; import { - type Gitea, findGiteaById, + type Gitea, updateGitea, } from "@dokploy/server/services/gitea"; import type { InferResultType } from "@dokploy/server/types/with"; @@ -118,7 +118,6 @@ export const getGiteaCloneCommand = async ( giteaOwner, giteaRepository, serverId, - gitea, enableSubmodules, } = entity; @@ -145,6 +144,7 @@ export const getGiteaCloneCommand = async ( // Use paths(true) for remote operations const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); await refreshGiteaToken(giteaId); + const gitea = await findGiteaById(giteaId); const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 774cd818f..65a09d248 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -112,7 +112,6 @@ export const cloneGitlabRepository = async ( appName, gitlabBranch, gitlabId, - gitlab, gitlabPathNamespace, enableSubmodules, } = entity; @@ -125,6 +124,7 @@ export const cloneGitlabRepository = async ( } await refreshGitlabToken(gitlabId); + const gitlab = await findGitlabById(gitlabId); const requirements = getErrorCloneRequirements(entity); @@ -187,7 +187,6 @@ export const getGitlabCloneCommand = async ( gitlabBranch, gitlabId, serverId, - gitlab, enableSubmodules, } = entity; @@ -235,6 +234,7 @@ export const getGitlabCloneCommand = async ( const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); await refreshGitlabToken(gitlabId); + const gitlab = await findGitlabById(gitlabId); const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); await recreateDirectory(outputPath); From b9f18cddf7d3262d0b6f9bd8838ceac793125f01 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 2 Aug 2025 21:24:33 -0600 Subject: [PATCH 22/29] refactor(gitlab): reorder token refresh and provider fetching for improved clarity --- packages/server/src/utils/providers/gitlab.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 65a09d248..f68bd3be7 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -371,9 +371,9 @@ export const cloneRawGitlabRepository = async (entity: Compose) => { }); } - const gitlabProvider = await findGitlabById(gitlabId); const { COMPOSE_PATH } = paths(); await refreshGitlabToken(gitlabId); + const gitlabProvider = await findGitlabById(gitlabId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); await recreateDirectory(outputPath); @@ -419,9 +419,9 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => { message: "Gitlab Provider not found", }); } - const gitlabProvider = await findGitlabById(gitlabId); const { COMPOSE_PATH } = paths(true); await refreshGitlabToken(gitlabId); + const gitlabProvider = await findGitlabById(gitlabId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace); From 607c505c4bbfa6fb6978fc89330b4bd925d77232 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 3 Aug 2025 01:18:18 -0600 Subject: [PATCH 23/29] refactor(traefik): update Traefik initialization to support standalone and service modes, enhance port handling with protocol specification --- .../servers/actions/show-traefik-actions.tsx | 5 +- .../web-server/manage-traefik-ports.tsx | 80 +++++-- apps/dokploy/server/api/routers/settings.ts | 201 +++++++++--------- apps/dokploy/setup.ts | 16 +- packages/server/src/services/settings.ts | 167 +++++++++++++++ packages/server/src/setup/traefik-setup.ts | 164 +++++++++----- 6 files changed, 447 insertions(+), 186 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx index c0c45e147..38039cc6b 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx @@ -1,5 +1,6 @@ +import { useTranslation } from "next-i18next"; +import { toast } from "sonner"; import { Button } from "@/components/ui/button"; - import { DropdownMenu, DropdownMenuContent, @@ -10,8 +11,6 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { api } from "@/utils/api"; -import { useTranslation } from "next-i18next"; -import { toast } from "sonner"; import { EditTraefikEnv } from "../../web-server/edit-traefik-env"; import { ManageTraefikPorts } from "../../web-server/manage-traefik-ports"; import { ShowModalLogs } from "../../web-server/show-modal-logs"; diff --git a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx index a5cfb6308..282f1fddd 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx @@ -1,3 +1,11 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ArrowRightLeft, Plus, Trash2 } from "lucide-react"; +import { useTranslation } from "next-i18next"; +import type React from "react"; +import { useEffect, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; @@ -19,15 +27,15 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { ArrowRightLeft, Plus, Trash2 } from "lucide-react"; -import { useTranslation } from "next-i18next"; -import type React from "react"; -import { useEffect, useState } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; interface Props { children: React.ReactNode; @@ -37,6 +45,7 @@ interface Props { const PortSchema = z.object({ targetPort: z.number().min(1, "Target port is required"), publishedPort: z.number().min(1, "Published port is required"), + protocol: z.enum(["tcp", "udp", "sctp"]), }); const TraefikPortsSchema = z.object({ @@ -75,12 +84,17 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { useEffect(() => { if (currentPorts) { - form.reset({ ports: currentPorts }); + form.reset({ + ports: currentPorts.map((port) => ({ + ...port, + protocol: port.protocol as "tcp" | "udp" | "sctp", + })), + }); } }, [currentPorts, form]); const handleAddPort = () => { - append({ targetPort: 0, publishedPort: 0 }); + append({ targetPort: 0, publishedPort: 0, protocol: "tcp" }); }; const onSubmit = async (data: TraefikPortsForm) => { @@ -96,7 +110,9 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { return ( <> -
setOpen(true)}>{children}
+ @@ -143,8 +159,8 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
{fields.map((field, index) => ( - - + + { ); }} value={field.value || ""} - className="w-full dark:bg-black" placeholder="e.g. 8080" /> @@ -200,7 +215,6 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { ); }} value={field.value || ""} - className="w-full dark:bg-black" placeholder="e.g. 80" /> @@ -208,6 +222,42 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { )} /> + ( + + + Protocol + + + + + + + )} + />