From 22c7c6e6fb0d86082973dcd57680abe83d2f34f5 Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sun, 3 Aug 2025 18:25:51 +0900 Subject: [PATCH 01/12] ci(pull-request): use strategy matrix --- .github/workflows/pull-request.yml | 39 +++++++----------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e9591f3cc..6c74dbc02 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -4,9 +4,15 @@ on: pull_request: branches: [main, canary] +permissions: + contents: read + jobs: - lint-and-typecheck: + pr-check: runs-on: ubuntu-latest + strategy: + matrix: + job: [build, test, typecheck] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 @@ -15,32 +21,5 @@ jobs: node-version: 20.16.0 cache: "pnpm" - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm typecheck - - build-and-test: - needs: lint-and-typecheck - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.16.0 - cache: "pnpm" - - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm build - - parallel-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.16.0 - cache: "pnpm" - - run: pnpm install --frozen-lockfile - - run: pnpm run server:build - - run: pnpm test + - run: pnpm server:build + - run: pnpm ${{ matrix.job }} From 1d94c85c2b8e4f21ebef824165c79ac1289e8137 Mon Sep 17 00:00:00 2001 From: depado Date: Tue, 5 Aug 2025 14:53:35 +0200 Subject: [PATCH 02/12] fix(setup): properly handle dokploy-traefik container absence --- packages/server/src/setup/traefik-setup.ts | 23 ++++++++-------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index cf10d7fa1..ccdfa30f8 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -89,21 +89,14 @@ export const initializeStandaloneTraefik = async ({ const docker = await getRemoteDocker(serverId); try { const container = docker.getContainer(containerName); - try { - await container.remove({ force: true }); - await new Promise((resolve) => setTimeout(resolve, 5000)); - await docker.createContainer(settings); - const newContainer = docker.getContainer(containerName); - await newContainer.start(); - console.log("Traefik Started ✅"); - } catch (error) { - console.error("Error in initializeStandaloneTraefik", error); - } - } catch (error) { - await docker.createContainer(settings); - console.error("Error in initializeStandaloneTraefik", error); - throw error; - } + await container.remove({ force: true }); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } catch {} + + await docker.createContainer(settings); + const newContainer = docker.getContainer(containerName); + await newContainer.start(); + console.log("Traefik Started ✅"); }; export const initializeTraefikService = async ({ From 883e1d1bfe823f8d1b85beb6c6b3a8ea2cc70148 Mon Sep 17 00:00:00 2001 From: Aeriit Date: Sat, 9 Aug 2025 11:05:37 -0400 Subject: [PATCH 03/12] fix(traefik): on setup support serverId as parameter and input --- packages/server/src/services/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index 613a97b0d..fc8e57e43 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -393,7 +393,7 @@ export const readPorts = async ( export const writeTraefikSetup = async ( input: TraefikOptions, - serverId?: string, + serverId = input.serverId, ) => { const resourceType = await getDockerResourceType("dokploy-traefik", serverId); if (resourceType === "service") { From 112a1dedec3806cd07968c943a8b30fe87a3e990 Mon Sep 17 00:00:00 2001 From: Bob Mannino Date: Sun, 10 Aug 2025 14:34:06 +0100 Subject: [PATCH 04/12] feat: add keyboard shortcuts to application page --- apps/dokploy/hooks/use-keyboard-nav.tsx | 77 +++++++++++++++++++ .../services/application/[applicationId].tsx | 2 + 2 files changed, 79 insertions(+) create mode 100644 apps/dokploy/hooks/use-keyboard-nav.tsx diff --git a/apps/dokploy/hooks/use-keyboard-nav.tsx b/apps/dokploy/hooks/use-keyboard-nav.tsx new file mode 100644 index 000000000..318f06914 --- /dev/null +++ b/apps/dokploy/hooks/use-keyboard-nav.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; + +const SHORTCUTS = { + g: "general", + e: "environment", + u: "domains", + p: "preview-deployments", + s: "schedules", + v: "volume-backups", + d: "deployments", + l: "logs", + m: "monitoring", + a: "advanced", +}; + +/** + * Use this to register keyboard shortcuts for the application page. Each + * shortcut must be prefixed with `g` (like GitHub). + * + * - `g g` "General", + * - `g e` "Environment", + * - `g u` "Domains", + * - `g p` "Preview Deployments", + * - `g s` "Schedules", + * - `g v` "Volume Backups", + * - `g d` "Deployments", + * - `g l` "Logs", + * - `g m` "Monitoring", + * - `g a` "Advanced" + */ +export function UseKeyboardNavForApplications() { + const [isModPressed, setModPressed] = useState(false); + const [timer, setTimer] = useState(null); + + const sp = useSearchParams(); + const router = useRouter(); + const pathname = usePathname(); + + const updateSearchParam = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(sp.toString()); + params.set(name, value); + + return params.toString(); + }, + [sp], + ); + + useEffect(() => { + const handleKeyDown = ({ key }: KeyboardEvent) => { + if (isModPressed) { + if (timer) clearTimeout(timer); + setModPressed(false); + + if (key in SHORTCUTS) { + const tab = SHORTCUTS[key]; + router.push( + `${pathname}?${updateSearchParam("tab", tab.toLowerCase())}`, + ); + } + } else { + if (key === "g") { + setModPressed(true); + setTimer(setTimeout(() => setModPressed(false), 5000)); + } + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isModPressed, timer, updateSearchParam, router, pathname]); + + return null; +} diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 209f6f65f..104b1ff7b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -51,6 +51,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; +import { UseKeyboardNavForApplications } from "@/hooks/use-keyboard-nav"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; @@ -91,6 +92,7 @@ const Service = ( return (
+ Date: Sun, 10 Aug 2025 15:03:41 -0600 Subject: [PATCH 05/12] fix(traefik): streamline serverId handling in writeTraefikSetup function --- packages/server/src/services/settings.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index fc8e57e43..e4402892f 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -391,22 +391,22 @@ export const readPorts = async ( ); }; -export const writeTraefikSetup = async ( - input: TraefikOptions, - serverId = input.serverId, -) => { - const resourceType = await getDockerResourceType("dokploy-traefik", serverId); +export const writeTraefikSetup = async (input: TraefikOptions) => { + const resourceType = await getDockerResourceType( + "dokploy-traefik", + input.serverId, + ); if (resourceType === "service") { await initializeTraefikService({ env: input.env, additionalPorts: input.additionalPorts, - serverId: serverId, + serverId: input.serverId, }); } else { await initializeStandaloneTraefik({ env: input.env, additionalPorts: input.additionalPorts, - serverId: serverId, + serverId: input.serverId, }); } }; From 4b1146ab6be1412a3570c1fa260d1205af5a05c0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:58:15 -0600 Subject: [PATCH 06/12] remove the "isWildcard" column from the "domain" table in the database schema --- .../compose/advanced/add-isolation.tsx | 195 ++++++++++++++++++ .../compose/general/compose-file-editor.tsx | 15 +- .../compose/general/show-utilities.tsx | 46 ----- .../services/compose/[composeId].tsx | 31 +-- 4 files changed, 217 insertions(+), 70 deletions(-) create mode 100644 apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx delete mode 100644 apps/dokploy/components/dashboard/compose/general/show-utilities.tsx diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx new file mode 100644 index 000000000..55cb0d906 --- /dev/null +++ b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx @@ -0,0 +1,195 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { AlertTriangle } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { AlertBlock } from "@/components/shared/alert-block"; +import { CodeEditor } from "@/components/shared/code-editor"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, +} from "@/components/ui/form"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import { api } from "@/utils/api"; + +interface Props { + composeId: string; +} + +// Schema for Isolated Deployment +const isolatedSchema = z.object({ + isolatedDeployment: z.boolean().optional(), +}); + +type IsolatedSchema = z.infer; + +export const IsolatedDeploymentTab = ({ composeId }: Props) => { + const utils = api.useUtils(); + const [compose, setCompose] = useState(""); + const { mutateAsync, error, isError } = + api.compose.isolatedDeployment.useMutation(); + + const { mutateAsync: updateCompose } = api.compose.update.useMutation(); + + const { data, refetch } = api.compose.one.useQuery( + { composeId }, + { enabled: !!composeId }, + ); + + const form = useForm({ + defaultValues: { + isolatedDeployment: false, + }, + resolver: zodResolver(isolatedSchema), + }); + + useEffect(() => { + randomizeCompose(); + if (data) { + form.reset({ + isolatedDeployment: data?.isolatedDeployment || false, + }); + } + }, [form, form.reset, form.formState.isSubmitSuccessful, data]); + + const onSubmit = async (formData: IsolatedSchema) => { + await updateCompose({ + composeId, + isolatedDeployment: formData?.isolatedDeployment || false, + }) + .then(async (_data) => { + await randomizeCompose(); + await refetch(); + toast.success("Compose updated"); + }) + .catch(() => { + toast.error("Error updating the compose"); + }); + }; + + const randomizeCompose = async () => { + await mutateAsync({ + composeId, + suffix: data?.appName || "", + }).then(async (data) => { + await utils.project.all.invalidate(); + setCompose(data); + }); + }; + + return ( + + + Enable Isolated Deployment + + Configure isolated deployment to the compose file. + + + +
+
+ + This feature creates an isolated environment for your deployment + by adding unique prefixes to all resources. It establishes a + dedicated network based on your compose file's name, ensuring your + services run in isolation. This prevents conflicts when running + multiple instances of the same template or services with identical + names. + +
+
+

+ Resources that will be isolated: +

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

+ Generating compose preview... +

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

- Resources that will be isolated: -

-
    -
  • Docker volumes
  • -
  • Docker networks
  • -
-
-
-
- {isError && {error?.message}} -
- - {isError && ( -
- - - {error?.message} - -
- )} - -
-
- ( - -
- - Enable Isolated Deployment ({data?.appName}) - - - Enable isolated deployment to the compose file. - -
- - - -
- )} - /> -
- -
- -
-
-
- -
-							
-						
-
-
- - - ); -}; From 231b8ed19d9c1c82654bc9b0d0d58e0157e86832 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:43:03 -0600 Subject: [PATCH 10/12] remove: eliminate Docker volumes from isolated deployment resources list --- .../components/dashboard/compose/advanced/add-isolation.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx index cea716858..5b6e04154 100644 --- a/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx +++ b/apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx @@ -129,7 +129,6 @@ export const IsolatedDeploymentTab = ({ composeId }: Props) => { Resources that will be isolated:
    -
  • Docker volumes
  • Docker networks
From ef6dcaf363f38a00b1ea7005d35e7012952e75bd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:23:15 -0600 Subject: [PATCH 11/12] chore(package): bump version to v0.24.10 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 481c4956c..2758bbced 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.24.9", + "version": "v0.24.10", "private": true, "license": "Apache-2.0", "type": "module", From 9763dce0450a5f59556c1ece6a8758c93a8b66ae Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:26:20 -0600 Subject: [PATCH 12/12] fix(swarm): adjust validation for containerId to allow empty array --- apps/dokploy/server/api/routers/swarm.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dokploy/server/api/routers/swarm.ts b/apps/dokploy/server/api/routers/swarm.ts index 91409a75c..cd3b042e9 100644 --- a/apps/dokploy/server/api/routers/swarm.ts +++ b/apps/dokploy/server/api/routers/swarm.ts @@ -67,8 +67,7 @@ export const swarmRouter = createTRPCRouter({ .string() .min(1) .regex(containerIdRegex, "Invalid app name.") - .array() - .min(1), + .array(), serverId: z.string().optional(), }), )