diff --git a/apps/dokploy/components/dashboard/networks/show-network-config.tsx b/apps/dokploy/components/dashboard/networks/show-network-config.tsx new file mode 100644 index 000000000..c7f9a158c --- /dev/null +++ b/apps/dokploy/components/dashboard/networks/show-network-config.tsx @@ -0,0 +1,69 @@ +"use client"; + +import { Eye, Loader2 } from "lucide-react"; +import { useState } from "react"; +import { AlertBlock } from "@/components/shared/alert-block"; +import { CodeEditor } from "@/components/shared/code-editor"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { api } from "@/utils/api"; + +interface Props { + networkId: string; + networkName: string; +} + +export const ShowNetworkConfig = ({ networkId, networkName }: Props) => { + const [open, setOpen] = useState(false); + const { data, isLoading, error } = api.network.inspect.useQuery( + { networkId }, + { enabled: open }, + ); + + return ( + + + + + + + + + Network Config + + docker network inspect output for "{networkName}" + + + {error ? ( + {error.message} + ) : isLoading ? ( + + Loading... + + + ) : ( + + + + + + + + )} + + + ); +}; diff --git a/apps/dokploy/components/dashboard/networks/show-networks.tsx b/apps/dokploy/components/dashboard/networks/show-networks.tsx index e26e60846..25ca3fd02 100644 --- a/apps/dokploy/components/dashboard/networks/show-networks.tsx +++ b/apps/dokploy/components/dashboard/networks/show-networks.tsx @@ -3,6 +3,7 @@ import { Loader2, Network, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { HandleNetwork } from "@/components/dashboard/networks/handle-network"; +import { ShowNetworkConfig } from "@/components/dashboard/networks/show-network-config"; import { DialogAction } from "@/components/shared/dialog-action"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -77,12 +78,11 @@ export const ShowNetworks = () => { Name Driver - Scope Internal Attachable Server Created - + Actions @@ -92,10 +92,12 @@ export const ShowNetworks = () => { {n.name} - {n.driver} - - - {n.driver === "overlay" ? "swarm" : "local"} + + {n.driver} + + {n.driver === "overlay" ? "swarm" : "local"} + + {n.internal ? "Yes" : "No"} @@ -109,35 +111,41 @@ export const ShowNetworks = () => { {new Date(n.createdAt).toLocaleDateString()} - - { - try { - await removeNetwork({ - networkId: n.networkId, - }); - toast.success("Network deleted"); - await utils.network.all.invalidate(); - } catch (error) { - toast.error("Error deleting network", { - description: - error instanceof Error - ? error.message - : "Unknown error", - }); - } - }} - > - + + + { + try { + await removeNetwork({ + networkId: n.networkId, + }); + toast.success("Network deleted"); + await utils.network.all.invalidate(); + } catch (error) { + toast.error("Error deleting network", { + description: + error instanceof Error + ? error.message + : "Unknown error", + }); + } + }} > - - - + + + + + ))} diff --git a/apps/dokploy/components/ui/button.tsx b/apps/dokploy/components/ui/button.tsx index 67f4405b0..f31410ab4 100644 --- a/apps/dokploy/components/ui/button.tsx +++ b/apps/dokploy/components/ui/button.tsx @@ -6,7 +6,7 @@ import type * as React from "react"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", { variants: { variant: { diff --git a/apps/dokploy/server/api/routers/network.ts b/apps/dokploy/server/api/routers/network.ts index a58a68b92..ac8a71310 100644 --- a/apps/dokploy/server/api/routers/network.ts +++ b/apps/dokploy/server/api/routers/network.ts @@ -1,4 +1,9 @@ -import { createNetwork, findNetworkById, removeNetwork } from "@dokploy/server"; +import { + createNetwork, + findNetworkById, + inspectNetwork, + removeNetwork, +} from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; @@ -44,6 +49,19 @@ export const networkRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { return createNetwork(input, ctx.session.activeOrganizationId); }), + inspect: protectedProcedure + .input(apiFindOneNetwork) + .query(async ({ ctx, input }) => { + const network = await findNetworkById(input.networkId); + if (network.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Network not found", + }); + } + return inspectNetwork(input.networkId); + }), + remove: protectedProcedure .input(apiRemoveNetwork) .mutation(async ({ ctx, input }) => { diff --git a/packages/server/src/services/network.ts b/packages/server/src/services/network.ts index fbca5c776..f01a3044f 100644 --- a/packages/server/src/services/network.ts +++ b/packages/server/src/services/network.ts @@ -97,6 +97,24 @@ export const createNetwork = async ( return created; }; +export const inspectNetwork = async (networkId: string) => { + const row = await findNetworkById(networkId); + + const docker = await getRemoteDocker(row.serverId ?? null); + try { + return await docker.getNetwork(row.name).inspect(); + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Failed to inspect Docker network", + cause: error, + }); + } +}; + // Docker networks are immutable: there is no update, only create and remove. export const removeNetwork = async (networkId: string) => { const row = await findNetworkById(networkId);
+ + + +
+ +