diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx index 99c7a385..eaa9851f 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx @@ -13,7 +13,11 @@ import Link from "next/link"; import { AddManager } from "./manager/add-manager"; import { AddWorker } from "./workers/add-worker"; -export const AddNode = () => { +interface Props { + serverId?: string; +} + +export const AddNode = ({ serverId }: Props) => { return ( @@ -53,10 +57,10 @@ export const AddNode = () => { Manager - + - + diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx index d6bce459..055c3f1c 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx @@ -9,8 +9,12 @@ import copy from "copy-to-clipboard"; import { CopyIcon } from "lucide-react"; import { toast } from "sonner"; -export const AddManager = () => { - const { data } = api.cluster.addManager.useQuery(); +interface Props { + serverId?: string; +} + +export const AddManager = ({ serverId }: Props) => { + const { data } = api.cluster.addManager.useQuery({ serverId }); return ( <> diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx new file mode 100644 index 00000000..32339e66 --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx @@ -0,0 +1,30 @@ +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { useState } from "react"; +import { ShowNodes } from "./show-nodes"; + +interface Props { + serverId: string; +} + +export const ShowNodesModal = ({ serverId }: Props) => { + const [isOpen, setIsOpen] = useState(false); + + return ( + + + e.preventDefault()} + > + Show Nodes + + + +
+ +
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx index b84c3b63..46cf3058 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx @@ -32,13 +32,25 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; -import { Boxes, HelpCircle, LockIcon, MoreHorizontal } from "lucide-react"; +import { + Boxes, + HelpCircle, + LockIcon, + MoreHorizontal, + Loader2, +} from "lucide-react"; import { toast } from "sonner"; import { AddNode } from "./add-node"; import { ShowNodeData } from "./show-node-data"; -export const ShowNodes = () => { - const { data, isLoading, refetch } = api.cluster.getNodes.useQuery(); +interface Props { + serverId?: string; +} + +export const ShowNodes = ({ serverId }: Props) => { + const { data, isLoading, refetch } = api.cluster.getNodes.useQuery({ + serverId, + }); const { data: registry } = api.registry.all.useQuery(); const { mutateAsync: deleteNode } = api.cluster.removeWorker.useMutation(); @@ -58,14 +70,17 @@ export const ShowNodes = () => { {haveAtLeastOneRegistry && (
- +
)} - {haveAtLeastOneRegistry ? ( + {isLoading ? ( +
+ +
+ ) : haveAtLeastOneRegistry ? (
- {isLoading &&
Loading...
} A list of your managers / workers. @@ -137,6 +152,7 @@ export const ShowNodes = () => { onClick={async () => { await deleteNode({ nodeId: node.ID, + serverId, }) .then(() => { refetch(); diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx index 50750f7e..05f9838e 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx @@ -9,8 +9,12 @@ import copy from "copy-to-clipboard"; import { CopyIcon } from "lucide-react"; import { toast } from "sonner"; -export const AddWorker = () => { - const { data } = api.cluster.addWorker.useQuery(); +interface Props { + serverId?: string; +} + +export const AddWorker = ({ serverId }: Props) => { + const { data } = api.cluster.addWorker.useQuery({ serverId }); return (
diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 3f3ff4e9..4b651663 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -42,6 +42,7 @@ import { ShowMonitoringModal } from "./show-monitoring-modal"; import { ShowSwarmOverviewModal } from "./show-swarm-overview-modal"; import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal"; import { WelcomeSuscription } from "./welcome-stripe/welcome-suscription"; +import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal"; export const ShowServers = () => { const { t } = useTranslation("settings"); @@ -328,6 +329,9 @@ export const ShowServers = () => { + )} diff --git a/apps/dokploy/server/api/routers/cluster.ts b/apps/dokploy/server/api/routers/cluster.ts index 0d840757..f165f7fc 100644 --- a/apps/dokploy/server/api/routers/cluster.ts +++ b/apps/dokploy/server/api/routers/cluster.ts @@ -1,22 +1,35 @@ import { getPublicIpWithFallback } from "@/server/wss/terminal"; -import { type DockerNode, IS_CLOUD, docker, execAsync } from "@dokploy/server"; +import { + type DockerNode, + IS_CLOUD, + execAsync, + getRemoteDocker, +} from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { createTRPCRouter, protectedProcedure } from "../trpc"; - export const clusterRouter = createTRPCRouter({ - getNodes: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return []; - } - const workers: DockerNode[] = await docker.listNodes(); + getNodes: protectedProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .query(async ({ input }) => { + if (IS_CLOUD) { + return []; + } - return workers; - }), + const docker = await getRemoteDocker(input.serverId); + const workers: DockerNode[] = await docker.listNodes(); + + return workers; + }), removeWorker: protectedProcedure .input( z.object({ nodeId: z.string(), + serverId: z.string().optional(), }), ) .mutation(async ({ input }) => { @@ -40,37 +53,51 @@ export const clusterRouter = createTRPCRouter({ }); } }), - addWorker: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return { - command: "", - version: "", - }; - } - const result = await docker.swarmInspect(); - const docker_version = await docker.version(); + addWorker: protectedProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .query(async ({ input }) => { + if (IS_CLOUD) { + return { + command: "", + version: "", + }; + } + const docker = await getRemoteDocker(input.serverId); + const result = await docker.swarmInspect(); + const docker_version = await docker.version(); - return { - command: `docker swarm join --token ${ - result.JoinTokens.Worker - } ${await getPublicIpWithFallback()}:2377`, - version: docker_version.Version, - }; - }), - addManager: protectedProcedure.query(async () => { - if (IS_CLOUD) { return { - command: "", - version: "", + command: `docker swarm join --token ${ + result.JoinTokens.Worker + } ${await getPublicIpWithFallback()}:2377`, + version: docker_version.Version, }; - } - const result = await docker.swarmInspect(); - const docker_version = await docker.version(); - return { - command: `docker swarm join --token ${ - result.JoinTokens.Manager - } ${await getPublicIpWithFallback()}:2377`, - version: docker_version.Version, - }; - }), + }), + addManager: protectedProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .query(async ({ input }) => { + if (IS_CLOUD) { + return { + command: "", + version: "", + }; + } + const docker = await getRemoteDocker(input.serverId); + const result = await docker.swarmInspect(); + const docker_version = await docker.version(); + return { + command: `docker swarm join --token ${ + result.JoinTokens.Manager + } ${await getPublicIpWithFallback()}:2377`, + version: docker_version.Version, + }; + }), });