mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 22:25:22 +02:00
fix(traefik): validate port 8080 before enabling dashboard
This commit is contained in:
@@ -97,7 +97,12 @@ export const ShowTraefikActions = ({ serverId }: Props) => {
|
||||
);
|
||||
refetchDashboard();
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((error) => {
|
||||
const errorMessage =
|
||||
error?.message ||
|
||||
"Failed to toggle dashboard. Please check if port 8080 is available.";
|
||||
toast.error(errorMessage);
|
||||
});
|
||||
}}
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
canAccessToTraefikFiles,
|
||||
checkGPUStatus,
|
||||
checkPortInUse,
|
||||
cleanStoppedContainers,
|
||||
cleanUpDockerBuilder,
|
||||
cleanUpSystemPrune,
|
||||
@@ -130,6 +131,17 @@ export const settingsRouter = createTRPCRouter({
|
||||
let newPorts = ports;
|
||||
// If receive true, add 8080 to ports
|
||||
if (input.enableDashboard) {
|
||||
// Check if port 8080 is already in use before enabling dashboard
|
||||
const portCheck = await checkPortInUse(8080, input.serverId);
|
||||
if (portCheck.isInUse) {
|
||||
const conflictingContainer = portCheck.conflictingContainer
|
||||
? ` by container "${portCheck.conflictingContainer}"`
|
||||
: "";
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: `Port 8080 is already in use${conflictingContainer}. Please stop the conflicting service or use a different port for the Traefik dashboard.`,
|
||||
});
|
||||
}
|
||||
newPorts.push({
|
||||
targetPort: 8080,
|
||||
publishedPort: 8080,
|
||||
|
||||
@@ -290,10 +290,10 @@ export const getContainersByAppLabel = async (
|
||||
|
||||
const command =
|
||||
type === "swarm"
|
||||
? `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
? `docker ps -a --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: type === "standalone"
|
||||
? `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}}'`;
|
||||
? `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}}'`;
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, command);
|
||||
stdout = result.stdout;
|
||||
|
||||
@@ -394,6 +394,78 @@ 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 containerNames = containersList
|
||||
.split("\n")
|
||||
.filter((name) => name && name !== "dokploy-traefik");
|
||||
|
||||
// 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 };
|
||||
} 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 };
|
||||
}
|
||||
};
|
||||
|
||||
export const writeTraefikSetup = async (input: TraefikOptions) => {
|
||||
const resourceType = await getDockerResourceType(
|
||||
"dokploy-traefik",
|
||||
|
||||
Reference in New Issue
Block a user