From 903789bfe557e32d535fa10aafef292ba68a9ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?= Date: Mon, 24 Aug 2026 11:40:38 -0400 Subject: [PATCH] Merge pull request #5160 from bestmaa/codex/fix-5145-traefik-empty-reconnect fix(traefik): skip empty service reconnect (cherry picked from commit d1273e2fe9d58ba72e55a3c7fe6c6d8b37326e8c) [skip ci] --- .../traefik/reconnect-services.test.ts | 71 +++++++++++++++++++ packages/server/src/services/settings.ts | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 apps/dokploy/__test__/traefik/reconnect-services.test.ts diff --git a/apps/dokploy/__test__/traefik/reconnect-services.test.ts b/apps/dokploy/__test__/traefik/reconnect-services.test.ts new file mode 100644 index 000000000..a60b0504b --- /dev/null +++ b/apps/dokploy/__test__/traefik/reconnect-services.test.ts @@ -0,0 +1,71 @@ +import { reconnectServicesToTraefik } from "@dokploy/server/services/settings"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + findMany: vi.fn(), + execAsync: vi.fn(), + execAsyncRemote: vi.fn(), +})); + +vi.mock("@dokploy/server/db", () => ({ + db: { + query: { + compose: { + findMany: mocks.findMany, + }, + }, + }, +})); + +vi.mock("@dokploy/server/utils/process/execAsync", () => ({ + execAsync: mocks.execAsync, + execAsyncRemote: mocks.execAsyncRemote, +})); + +describe("reconnectServicesToTraefik", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.findMany.mockResolvedValue([]); + }); + + it("does not execute an empty local command when no isolated deployments exist", async () => { + await reconnectServicesToTraefik(); + + expect(mocks.execAsync).not.toHaveBeenCalled(); + }); + + it("does not execute an empty remote command when no isolated deployments exist", async () => { + await reconnectServicesToTraefik("server-id"); + + expect(mocks.execAsyncRemote).not.toHaveBeenCalled(); + }); + + it("reconnects isolated deployments to the local Traefik network", async () => { + mocks.findMany.mockResolvedValue([ + { appName: "first-compose" }, + { appName: "second-compose" }, + ]); + + await reconnectServicesToTraefik(); + + expect(mocks.execAsync).toHaveBeenCalledOnce(); + expect(mocks.execAsync).toHaveBeenCalledWith( + 'docker network connect first-compose $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1\n' + + 'docker network connect second-compose $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1\n', + ); + expect(mocks.execAsyncRemote).not.toHaveBeenCalled(); + }); + + it("reconnects isolated deployments on a remote server", async () => { + mocks.findMany.mockResolvedValue([{ appName: "remote-compose" }]); + + await reconnectServicesToTraefik("server-id"); + + expect(mocks.execAsyncRemote).toHaveBeenCalledOnce(); + expect(mocks.execAsyncRemote).toHaveBeenCalledWith( + "server-id", + 'docker network connect remote-compose $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1\n', + ); + expect(mocks.execAsync).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index ecfb7f6de..d1783dff7 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -485,7 +485,7 @@ export const reconnectServicesToTraefik = async (serverId?: string) => { ), }); - if (!composeResult) { + if (composeResult.length === 0) { return; } let commands = "";