mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-18 13:45:23 +02:00
fix: improve error handling for Traefik port updates and enhance port availability checks
This commit is contained in:
@@ -105,7 +105,9 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
||||
});
|
||||
toast.success(t("settings.server.webServer.traefik.portsUpdated"));
|
||||
setOpen(false);
|
||||
} catch {}
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || "Error updating Traefik ports");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -822,6 +822,19 @@ export const settingsRouter = createTRPCRouter({
|
||||
"dokploy-traefik",
|
||||
input?.serverId,
|
||||
);
|
||||
|
||||
for (const port of input.additionalPorts) {
|
||||
const portCheck = await checkPortInUse(
|
||||
port.publishedPort,
|
||||
input.serverId,
|
||||
);
|
||||
if (portCheck.isInUse) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: `Port ${port.targetPort} is already in use by ${portCheck.conflictingContainer}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
const preparedEnv = prepareEnvironmentVariables(env);
|
||||
|
||||
await writeTraefikSetup({
|
||||
|
||||
@@ -290,10 +290,10 @@ export const getContainersByAppLabel = async (
|
||||
|
||||
const command =
|
||||
type === "swarm"
|
||||
? `docker ps -a --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
? `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: type === "standalone"
|
||||
? `docker ps -a --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: `docker ps -a --filter "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
|
||||
? `docker ps --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: `docker ps --filter "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, command);
|
||||
stdout = result.stdout;
|
||||
|
||||
@@ -392,73 +392,23 @@ export const readPorts = async (
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a port is already in use by another container
|
||||
* @param port - The port number to check
|
||||
* @param serverId - Optional server ID for remote Docker
|
||||
* @returns Object with isInUse boolean and conflictingContainer name if found
|
||||
*/
|
||||
export const checkPortInUse = async (
|
||||
port: number,
|
||||
serverId?: string,
|
||||
): Promise<{ isInUse: boolean; conflictingContainer?: string }> => {
|
||||
try {
|
||||
// Method 1: Check all containers and inspect their port bindings
|
||||
const listContainersCommand = `docker ps -a --format '{{.Names}}'`;
|
||||
let containersList = "";
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, listContainersCommand);
|
||||
containersList = result.stdout.trim();
|
||||
} else {
|
||||
const result = await execAsync(listContainersCommand);
|
||||
containersList = result.stdout.trim();
|
||||
}
|
||||
const command = `docker ps -a --format '{{.Names}}' | grep -v '^dokploy-traefik$' | while read name; do docker port "$name" 2>/dev/null | grep -q ':${port}' && echo "$name" && break; done || true`;
|
||||
const { stdout } = serverId
|
||||
? await execAsyncRemote(serverId, command)
|
||||
: await execAsync(command);
|
||||
|
||||
const containerNames = containersList
|
||||
.split("\n")
|
||||
.filter((name) => name && name !== "dokploy-traefik");
|
||||
const container = stdout.trim();
|
||||
|
||||
// Check each container's port bindings
|
||||
for (const containerName of containerNames) {
|
||||
const portCheckCommand = `docker port ${containerName} 2>/dev/null | grep ':${port}' || true`;
|
||||
let portOutput = "";
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, portCheckCommand);
|
||||
portOutput = result.stdout.trim();
|
||||
} else {
|
||||
const result = await execAsync(portCheckCommand);
|
||||
portOutput = result.stdout.trim();
|
||||
}
|
||||
|
||||
if (portOutput) {
|
||||
return {
|
||||
isInUse: true,
|
||||
conflictingContainer: containerName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Check using ss/netstat for any process using the port
|
||||
const portCheckCommand = `ss -tuln 2>/dev/null | grep ':${port} ' || netstat -tuln 2>/dev/null | grep ':${port} ' || true`;
|
||||
let portCheckOutput = "";
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, portCheckCommand);
|
||||
portCheckOutput = result.stdout.trim();
|
||||
} else {
|
||||
const result = await execAsync(portCheckCommand);
|
||||
portCheckOutput = result.stdout.trim();
|
||||
}
|
||||
|
||||
if (portCheckOutput) {
|
||||
// Port is in use but we couldn't identify the container
|
||||
// This could be a non-Docker process or a container we couldn't detect
|
||||
return { isInUse: true };
|
||||
}
|
||||
|
||||
return { isInUse: false };
|
||||
return {
|
||||
isInUse: !!container,
|
||||
conflictingContainer: container || undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
// If check fails, log error but don't block the operation
|
||||
// The actual Docker bind will fail if port is truly in use
|
||||
console.error("Error checking port availability:", error);
|
||||
return { isInUse: false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user