mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-06 20:22:14 +00: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"));
|
toast.success(t("settings.server.webServer.traefik.portsUpdated"));
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch {}
|
} catch (error) {
|
||||||
|
toast.error((error as Error).message || "Error updating Traefik ports");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -822,6 +822,19 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
"dokploy-traefik",
|
"dokploy-traefik",
|
||||||
input?.serverId,
|
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);
|
const preparedEnv = prepareEnvironmentVariables(env);
|
||||||
|
|
||||||
await writeTraefikSetup({
|
await writeTraefikSetup({
|
||||||
|
|||||||
@@ -290,10 +290,10 @@ export const getContainersByAppLabel = async (
|
|||||||
|
|
||||||
const command =
|
const command =
|
||||||
type === "swarm"
|
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"
|
: type === "standalone"
|
||||||
? `docker ps -a --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
? `docker ps --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 "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
const result = await execAsyncRemote(serverId, command);
|
const result = await execAsyncRemote(serverId, command);
|
||||||
stdout = result.stdout;
|
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 (
|
export const checkPortInUse = async (
|
||||||
port: number,
|
port: number,
|
||||||
serverId?: string,
|
serverId?: string,
|
||||||
): Promise<{ isInUse: boolean; conflictingContainer?: string }> => {
|
): Promise<{ isInUse: boolean; conflictingContainer?: string }> => {
|
||||||
try {
|
try {
|
||||||
// Method 1: Check all containers and inspect their port bindings
|
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 listContainersCommand = `docker ps -a --format '{{.Names}}'`;
|
const { stdout } = serverId
|
||||||
let containersList = "";
|
? await execAsyncRemote(serverId, command)
|
||||||
if (serverId) {
|
: await execAsync(command);
|
||||||
const result = await execAsyncRemote(serverId, listContainersCommand);
|
|
||||||
containersList = result.stdout.trim();
|
|
||||||
} else {
|
|
||||||
const result = await execAsync(listContainersCommand);
|
|
||||||
containersList = result.stdout.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerNames = containersList
|
const container = stdout.trim();
|
||||||
.split("\n")
|
|
||||||
.filter((name) => name && name !== "dokploy-traefik");
|
|
||||||
|
|
||||||
// Check each container's port bindings
|
return {
|
||||||
for (const containerName of containerNames) {
|
isInUse: !!container,
|
||||||
const portCheckCommand = `docker port ${containerName} 2>/dev/null | grep ':${port}' || true`;
|
conflictingContainer: container || undefined,
|
||||||
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 };
|
|
||||||
} catch (error) {
|
} 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);
|
console.error("Error checking port availability:", error);
|
||||||
return { isInUse: false };
|
return { isInUse: false };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user