mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-15 08:25:16 +00:00
Merge pull request #3176 from Dokploy/2974-tail-error-tail-inotify-cannot-be-used-reverting-to-polling-too-many-open-files
fix(wss): close read deployment and container logs connections
This commit is contained in:
@@ -71,7 +71,9 @@ export const setupDockerContainerLogsWebSocketServer = (
|
|||||||
const command = search
|
const command = search
|
||||||
? `${baseCommand} 2>&1 | grep --line-buffered -iF "${escapedSearch}"`
|
? `${baseCommand} 2>&1 | grep --line-buffered -iF "${escapedSearch}"`
|
||||||
: baseCommand;
|
: baseCommand;
|
||||||
client.exec(command, (err, stream) => {
|
// Use pty: true to ensure the remote process receives SIGHUP when SSH connection closes
|
||||||
|
// This is crucial for terminating docker logs processes when the connection is closed
|
||||||
|
client.exec(command, { pty: true }, (err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("Execution error:", err);
|
console.error("Execution error:", err);
|
||||||
ws.close();
|
ws.close();
|
||||||
|
|||||||
@@ -58,7 +58,12 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
`docker exec -it -w / ${containerId} ${activeWay}`,
|
`docker exec -it -w / ${containerId} ${activeWay}`,
|
||||||
{ pty: true },
|
{ pty: true },
|
||||||
(err, stream) => {
|
(err, stream) => {
|
||||||
if (err) throw err;
|
if (err) {
|
||||||
|
console.error("SSH exec error:", err);
|
||||||
|
ws.close();
|
||||||
|
conn.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
stream
|
stream
|
||||||
.on("close", (code: number, _signal: string) => {
|
.on("close", (code: number, _signal: string) => {
|
||||||
@@ -93,10 +98,20 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
|||||||
|
|
||||||
ws.on("close", () => {
|
ws.on("close", () => {
|
||||||
stream.end();
|
stream.end();
|
||||||
|
// Ensure SSH connection is closed when WebSocket closes
|
||||||
|
conn.end();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
.on("error", (err) => {
|
||||||
|
console.error("SSH connection error:", err);
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(`SSH error: ${err.message}`);
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
conn.end();
|
||||||
|
})
|
||||||
.connect({
|
.connect({
|
||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
port: server.port,
|
port: server.port,
|
||||||
|
|||||||
@@ -31,8 +31,11 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
const serverId = url.searchParams.get("serverId");
|
const serverId = url.searchParams.get("serverId");
|
||||||
const { user, session } = await validateRequest(req);
|
const { user, session } = await validateRequest(req);
|
||||||
|
|
||||||
|
// Generate unique connection ID for tracking
|
||||||
|
const connectionId = `deployment-logs-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
||||||
|
|
||||||
if (!logPath) {
|
if (!logPath) {
|
||||||
console.log("logPath no provided");
|
console.log(`[${connectionId}] logPath no provided`);
|
||||||
ws.close(4000, "logPath no provided");
|
ws.close(4000, "logPath no provided");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -42,40 +45,55 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tailProcess: ReturnType<typeof spawn> | null = null;
|
||||||
|
let sshClient: Client | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
const server = await findServerById(serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
if (!server.sshKeyId) return;
|
if (!server.sshKeyId) {
|
||||||
const client = new Client();
|
ws.close();
|
||||||
client
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sshClient = new Client();
|
||||||
|
sshClient
|
||||||
.on("ready", () => {
|
.on("ready", () => {
|
||||||
const command = `
|
const command = `
|
||||||
tail -n +1 -f ${logPath};
|
tail -n +1 -f ${logPath};
|
||||||
`;
|
`;
|
||||||
client.exec(command, (err, stream) => {
|
sshClient!.exec(command, (err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("Execution error:", err);
|
sshClient!.end();
|
||||||
ws.close();
|
ws.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stream
|
stream
|
||||||
.on("close", () => {
|
.on("close", () => {
|
||||||
client.end();
|
sshClient!.end();
|
||||||
ws.close();
|
ws.close();
|
||||||
})
|
})
|
||||||
.on("data", (data: string) => {
|
.on("data", (data: string) => {
|
||||||
ws.send(data.toString());
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(data.toString());
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.stderr.on("data", (data) => {
|
.stderr.on("data", (data) => {
|
||||||
ws.send(data.toString());
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(data.toString());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.on("error", (err) => {
|
.on("error", (err) => {
|
||||||
console.error("SSH connection error:", err);
|
if (ws.readyState === ws.OPEN) {
|
||||||
ws.send(`SSH error: ${err.message}`);
|
ws.send(`SSH error: ${err.message}`);
|
||||||
ws.close(); // Cierra el WebSocket si hay un error con SSH
|
ws.close();
|
||||||
|
}
|
||||||
|
if (sshClient) {
|
||||||
|
sshClient.end();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.connect({
|
.connect({
|
||||||
host: server.ipAddress,
|
host: server.ipAddress,
|
||||||
@@ -85,26 +103,75 @@ export const setupDeploymentLogsWebSocketServer = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", () => {
|
ws.on("close", () => {
|
||||||
client.end();
|
if (sshClient) {
|
||||||
|
sshClient.end();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const tail = spawn("tail", ["-n", "+1", "-f", logPath]);
|
tailProcess = spawn("tail", ["-n", "+1", "-f", logPath]);
|
||||||
|
|
||||||
tail.stdout.on("data", (data) => {
|
const stdout = tailProcess.stdout;
|
||||||
ws.send(data.toString());
|
const stderr = tailProcess.stderr;
|
||||||
});
|
|
||||||
|
|
||||||
tail.stderr.on("data", (data) => {
|
if (stdout) {
|
||||||
ws.send(new Error(`tail error: ${data.toString()}`).message);
|
stdout.on("data", (data) => {
|
||||||
});
|
if (ws.readyState === ws.OPEN) {
|
||||||
tail.on("close", () => {
|
ws.send(data.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
stderr.on("data", (data) => {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.send(new Error(`tail error: ${data.toString()}`).message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tailProcess.on("close", () => {
|
||||||
ws.close();
|
ws.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tailProcess.on("error", () => {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on("close", () => {
|
||||||
|
if (tailProcess && !tailProcess.killed) {
|
||||||
|
tailProcess.kill("SIGTERM");
|
||||||
|
// Force kill after a timeout if it doesn't terminate
|
||||||
|
setTimeout(() => {
|
||||||
|
if (tailProcess && !tailProcess.killed) {
|
||||||
|
tailProcess.kill("SIGKILL");
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Clean up resources on error
|
||||||
|
if (tailProcess && !tailProcess.killed) {
|
||||||
|
tailProcess.kill("SIGTERM");
|
||||||
|
setTimeout(() => {
|
||||||
|
if (tailProcess && !tailProcess.killed) {
|
||||||
|
tailProcess.kill("SIGKILL");
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
if (sshClient) {
|
||||||
|
sshClient.end();
|
||||||
|
}
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
// @ts-ignore
|
||||||
|
const errorMessage = error?.message as unknown as string;
|
||||||
|
ws.send(errorMessage || "An error occurred");
|
||||||
|
ws.close();
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// @ts-ignore
|
|
||||||
// const errorMessage = error?.message as unknown as string;
|
|
||||||
// ws.send(errorMessage);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user