mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor(manage-traefik-ports): remove publishMode from port management and update related logic
This commit is contained in:
parent
4c5bc541d6
commit
160742c2cf
@ -19,13 +19,6 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { ArrowRightLeft, Plus, Trash2 } from "lucide-react";
|
import { ArrowRightLeft, Plus, Trash2 } from "lucide-react";
|
||||||
@ -44,7 +37,6 @@ interface Props {
|
|||||||
const PortSchema = z.object({
|
const PortSchema = z.object({
|
||||||
targetPort: z.number().min(1, "Target port is required"),
|
targetPort: z.number().min(1, "Target port is required"),
|
||||||
publishedPort: z.number().min(1, "Published port is required"),
|
publishedPort: z.number().min(1, "Published port is required"),
|
||||||
publishMode: z.enum(["ingress", "host"]),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const TraefikPortsSchema = z.object({
|
const TraefikPortsSchema = z.object({
|
||||||
@ -88,7 +80,7 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
|||||||
}, [currentPorts, form]);
|
}, [currentPorts, form]);
|
||||||
|
|
||||||
const handleAddPort = () => {
|
const handleAddPort = () => {
|
||||||
append({ targetPort: 0, publishedPort: 0, publishMode: "host" });
|
append({ targetPort: 0, publishedPort: 0 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: TraefikPortsForm) => {
|
const onSubmit = async (data: TraefikPortsForm) => {
|
||||||
@ -154,7 +146,7 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
|||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Card key={field.id}>
|
<Card key={field.id}>
|
||||||
<CardContent className="grid grid-cols-[1fr_1fr_1.5fr_auto] gap-4 p-4 transparent">
|
<CardContent className="grid grid-cols-[1fr_1fr_auto] gap-4 p-4 transparent">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`ports.${index}.targetPort`}
|
name={`ports.${index}.targetPort`}
|
||||||
@ -207,39 +199,6 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`ports.${index}.publishMode`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel className="text-sm font-medium text-muted-foreground">
|
|
||||||
{t(
|
|
||||||
"settings.server.webServer.traefik.publishMode",
|
|
||||||
)}
|
|
||||||
</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger className="dark:bg-black">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="host">
|
|
||||||
Host Mode
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="ingress">
|
|
||||||
Ingress Mode
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex items-end">
|
<div className="flex items-end">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => remove(index)}
|
onClick={() => remove(index)}
|
||||||
@ -263,30 +222,23 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
|||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
<strong>
|
<strong>
|
||||||
Each port mapping defines how external traffic reaches
|
Each port mapping defines how external traffic reaches
|
||||||
your containers.
|
your containers through Traefik.
|
||||||
</strong>
|
</strong>
|
||||||
<ul className="pt-2">
|
<ul className="pt-2">
|
||||||
<li>
|
<li>
|
||||||
<strong>Host Mode:</strong> Directly binds the port
|
<strong>Target Port:</strong> The port inside your
|
||||||
to the host machine.
|
container that the service is listening on.
|
||||||
<ul className="p-2 list-inside list-disc">
|
|
||||||
<li>
|
|
||||||
Best for single-node deployments or when you
|
|
||||||
need guaranteed port availability.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Ingress Mode:</strong> Routes through Docker
|
<strong>Published Port:</strong> The port on your
|
||||||
Swarm's load balancer.
|
host machine that will be mapped to the target port.
|
||||||
<ul className="p-2 list-inside list-disc">
|
|
||||||
<li>
|
|
||||||
Recommended for multi-node deployments and
|
|
||||||
better scalability.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p className="mt-2">
|
||||||
|
All ports are bound directly to the host machine,
|
||||||
|
allowing Traefik to handle incoming traffic and route
|
||||||
|
it appropriately to your services.
|
||||||
|
</p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</AlertBlock>
|
</AlertBlock>
|
||||||
|
@ -97,14 +97,20 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
toggleDashboard: adminProcedure
|
toggleDashboard: adminProcedure
|
||||||
.input(apiEnableDashboard)
|
.input(apiEnableDashboard)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
|
const ports = (await getTraefikPorts(input.serverId)).filter(
|
||||||
|
(port) =>
|
||||||
|
port.targetPort !== 80 &&
|
||||||
|
port.targetPort !== 443 &&
|
||||||
|
port.targetPort !== 8080,
|
||||||
|
);
|
||||||
await initializeTraefik({
|
await initializeTraefik({
|
||||||
|
additionalPorts: ports,
|
||||||
enableDashboard: input.enableDashboard,
|
enableDashboard: input.enableDashboard,
|
||||||
serverId: input.serverId,
|
serverId: input.serverId,
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
cleanUnusedImages: adminProcedure
|
cleanUnusedImages: adminProcedure
|
||||||
.input(apiServerSchema)
|
.input(apiServerSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
@ -749,7 +755,6 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
z.object({
|
z.object({
|
||||||
targetPort: z.number(),
|
targetPort: z.number(),
|
||||||
publishedPort: z.number(),
|
publishedPort: z.number(),
|
||||||
publishMode: z.enum(["ingress", "host"]).default("host"),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@ -782,59 +787,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
getTraefikPorts: adminProcedure
|
getTraefikPorts: adminProcedure
|
||||||
.input(apiServerSchema)
|
.input(apiServerSchema)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const command = `docker container inspect --format='{{json .NetworkSettings.Ports}}' dokploy-traefik`;
|
return await getTraefikPorts(input?.serverId);
|
||||||
|
|
||||||
try {
|
|
||||||
let stdout = "";
|
|
||||||
if (input?.serverId) {
|
|
||||||
const result = await execAsyncRemote(input.serverId, command);
|
|
||||||
stdout = result.stdout;
|
|
||||||
} else if (!IS_CLOUD) {
|
|
||||||
const result = await execAsync(command);
|
|
||||||
stdout = result.stdout;
|
|
||||||
}
|
|
||||||
|
|
||||||
const portsMap = JSON.parse(stdout.trim());
|
|
||||||
const additionalPorts: Array<{
|
|
||||||
targetPort: number;
|
|
||||||
publishedPort: number;
|
|
||||||
publishMode: "host" | "ingress";
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
// Convert the Docker container port format to our expected format
|
|
||||||
for (const [containerPort, bindings] of Object.entries(portsMap)) {
|
|
||||||
if (!bindings) continue;
|
|
||||||
|
|
||||||
const [port = ""] = containerPort.split("/");
|
|
||||||
if (!port) continue;
|
|
||||||
|
|
||||||
const targetPortNum = Number.parseInt(port, 10);
|
|
||||||
if (Number.isNaN(targetPortNum)) continue;
|
|
||||||
|
|
||||||
// Skip default ports
|
|
||||||
if ([80, 443, 8080].includes(targetPortNum)) continue;
|
|
||||||
|
|
||||||
for (const binding of bindings as Array<{ HostPort: string }>) {
|
|
||||||
if (!binding.HostPort) continue;
|
|
||||||
const publishedPort = Number.parseInt(binding.HostPort, 10);
|
|
||||||
if (Number.isNaN(publishedPort)) continue;
|
|
||||||
|
|
||||||
additionalPorts.push({
|
|
||||||
targetPort: targetPortNum,
|
|
||||||
publishedPort,
|
|
||||||
publishMode: "host", // Docker standalone uses host mode by default
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return additionalPorts;
|
|
||||||
} catch (error) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to get Traefik ports",
|
|
||||||
cause: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
updateLogCleanup: adminProcedure
|
updateLogCleanup: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
@ -853,3 +806,56 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
return getLogCleanupStatus();
|
return getLogCleanupStatus();
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getTraefikPorts = async (serverId?: string) => {
|
||||||
|
const command = `docker container inspect --format='{{json .NetworkSettings.Ports}}' dokploy-traefik`;
|
||||||
|
try {
|
||||||
|
let stdout = "";
|
||||||
|
if (serverId) {
|
||||||
|
const result = await execAsyncRemote(serverId, command);
|
||||||
|
stdout = result.stdout;
|
||||||
|
} else if (!IS_CLOUD) {
|
||||||
|
const result = await execAsync(command);
|
||||||
|
stdout = result.stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const portsMap = JSON.parse(stdout.trim());
|
||||||
|
const additionalPorts: Array<{
|
||||||
|
targetPort: number;
|
||||||
|
publishedPort: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
// Convert the Docker container port format to our expected format
|
||||||
|
for (const [containerPort, bindings] of Object.entries(portsMap)) {
|
||||||
|
if (!bindings) continue;
|
||||||
|
|
||||||
|
const [port = ""] = containerPort.split("/");
|
||||||
|
if (!port) continue;
|
||||||
|
|
||||||
|
const targetPortNum = Number.parseInt(port, 10);
|
||||||
|
if (Number.isNaN(targetPortNum)) continue;
|
||||||
|
|
||||||
|
// Skip default ports
|
||||||
|
if ([80, 443].includes(targetPortNum)) continue;
|
||||||
|
|
||||||
|
for (const binding of bindings as Array<{ HostPort: string }>) {
|
||||||
|
if (!binding.HostPort) continue;
|
||||||
|
const publishedPort = Number.parseInt(binding.HostPort, 10);
|
||||||
|
if (Number.isNaN(publishedPort)) continue;
|
||||||
|
|
||||||
|
additionalPorts.push({
|
||||||
|
targetPort: targetPortNum,
|
||||||
|
publishedPort,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return additionalPorts;
|
||||||
|
} catch (error) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Failed to get Traefik ports",
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -22,7 +22,6 @@ interface TraefikOptions {
|
|||||||
additionalPorts?: {
|
additionalPorts?: {
|
||||||
targetPort: number;
|
targetPort: number;
|
||||||
publishedPort: number;
|
publishedPort: number;
|
||||||
publishMode?: "ingress" | "host";
|
|
||||||
}[];
|
}[];
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user