From c65026353a3e0496842e5592809f9dba5f7c0006 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Thu, 5 Feb 2026 23:18:41 -0600 Subject: [PATCH] feat(network-form): add DriverOptsEntries to network form schema and UI - Introduced DriverOptsEntries to the network form schema, allowing users to specify driver options for networks. - Updated the form UI to support adding, editing, and removing driver options dynamically. - Adjusted the backend schema to accept driver options as a record of key-value pairs. --- .../cluster/swarm-forms/network-form.tsx | 121 +++++++++++++++++- packages/server/src/db/schema/shared.ts | 2 +- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx index 508bb7140..43a816dfc 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx @@ -16,12 +16,18 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +const driverOptEntrySchema = z.object({ + key: z.string(), + value: z.string(), +}); + export const networkFormSchema = z.object({ networks: z .array( z.object({ Target: z.string().optional(), Aliases: z.string().optional(), + DriverOptsEntries: z.array(driverOptEntrySchema).optional(), }), ) .optional(), @@ -80,6 +86,12 @@ export const NetworkForm = ({ id, type }: NetworkFormProps) => { const networkEntries = data.networkSwarm.map((network) => ({ Target: network.Target || "", Aliases: network.Aliases?.join(", ") || "", + DriverOptsEntries: network.DriverOpts + ? Object.entries(network.DriverOpts).map(([key, value]) => ({ + key, + value: value ?? "", + })) + : [], })); form.reset({ networks: networkEntries }); } @@ -91,12 +103,25 @@ export const NetworkForm = ({ id, type }: NetworkFormProps) => { const networksArray = formData.networks ?.filter((network) => network.Target) - .map((network) => ({ - Target: network.Target, - Aliases: network.Aliases - ? network.Aliases.split(",").map((alias) => alias.trim()) - : undefined, - })) || []; + .map((network) => { + const entries = + (network.DriverOptsEntries ?? []).filter( + (e) => e.key.trim() !== "", + ); + const driverOpts = + entries.length > 0 + ? Object.fromEntries( + entries.map((e) => [e.key.trim(), e.value]), + ) + : undefined; + return { + Target: network.Target, + Aliases: network.Aliases + ? network.Aliases.split(",").map((alias) => alias.trim()) + : undefined, + DriverOpts: driverOpts, + }; + }) || []; // If no networks, send null to clear the database const networksToSend = networksArray.length > 0 ? networksArray : null; @@ -166,6 +191,82 @@ export const NetworkForm = ({ id, type }: NetworkFormProps) => { )} /> +
+ Driver options (optional) + + e.g. com.docker.network.driver.mtu, com.docker.network.driver.host_binding + + {(form.watch(`networks.${index}.DriverOptsEntries`) ?? []).map( + (_, optIndex) => ( +
+ ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + +
+ ), + )} + +
diff --git a/packages/server/src/db/schema/shared.ts b/packages/server/src/db/schema/shared.ts index 600593d7a..4cdebe1f8 100644 --- a/packages/server/src/db/schema/shared.ts +++ b/packages/server/src/db/schema/shared.ts @@ -167,7 +167,7 @@ export const NetworkSwarmSchema = z.array( .object({ Target: z.string().optional(), Aliases: z.array(z.string()).optional(), - DriverOpts: z.object({}).optional(), + DriverOpts: z.record(z.string()).optional(), }) .strict(), );