From 8397de0dcab99fdf5362d170b2de07af7cc440ec Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:17:46 -0600 Subject: [PATCH] refactor: close connections when the ws is not ready --- .../deployments/show-deployment.tsx | 8 ++- .../deployments/show-deployment-compose.tsx | 30 ++++++++- .../dashboard/docker/logs/docker-logs-id.tsx | 21 +++++- .../server/wss/docker-container-logs.ts | 66 ++++++++++--------- 4 files changed, 88 insertions(+), 37 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index d4b5c5e79..6913aa242 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -21,6 +21,7 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { useEffect(() => { if (!open || !logPath) return; + setData(""); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`; @@ -61,13 +62,14 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { open={open} onOpenChange={(e) => { onClose(); - if (!e) setData(""); + if (!e) { + setData(""); + } - if (!e && wsRef.current) { + if (wsRef.current) { if (wsRef.current.readyState === WebSocket.OPEN) { wsRef.current.close(); } - setData(""); } }} > diff --git a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx index e79104692..8413b14b7 100644 --- a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx @@ -21,20 +21,38 @@ export const ShowDeploymentCompose = ({ }: Props) => { const [data, setData] = useState(""); const endOfLogsRef = useRef(null); + const wsRef = useRef(null); // Ref to hold WebSocket instance useEffect(() => { if (!open || !logPath) return; + setData(""); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`; const ws = new WebSocket(wsUrl); + wsRef.current = ws; // Store WebSocket instance in ref + ws.onmessage = (e) => { setData((currentData) => currentData + e.data); }; - return () => ws.close(); + ws.onerror = (error) => { + console.error("WebSocket error: ", error); + }; + + ws.onclose = () => { + console.log("WebSocket connection closed"); + wsRef.current = null; + }; + + return () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + ws.close(); + wsRef.current = null; + } + }; }, [logPath, open]); const scrollToBottom = () => { @@ -50,7 +68,15 @@ export const ShowDeploymentCompose = ({ open={open} onOpenChange={(e) => { onClose(); - if (!e) setData(""); + if (!e) { + setData(""); + } + + if (wsRef.current) { + if (wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.close(); + } + } }} > diff --git a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx index d9bce7274..c87544da4 100644 --- a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx @@ -1,7 +1,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Terminal } from "@xterm/xterm"; -import React, { useEffect } from "react"; +import React, { useEffect, useRef } from "react"; import { FitAddon } from "xterm-addon-fit"; import "@xterm/xterm/css/xterm.css"; @@ -18,8 +18,12 @@ export const DockerLogsId: React.FC = ({ }) => { const [term, setTerm] = React.useState(); const [lines, setLines] = React.useState(40); + const wsRef = useRef(null); // Ref to hold WebSocket instance const createTerminal = (): Terminal => { + if (containerId === "select-a-containe") { + return new Terminal(); + } const container = document.getElementById(id); if (container) { container.innerHTML = ""; @@ -45,7 +49,7 @@ export const DockerLogsId: React.FC = ({ const wsUrl = `${protocol}//${window.location.host}/docker-container-logs?containerId=${containerId}&tail=${lines}${serverId ? `&serverId=${serverId}` : ""}`; const ws = new WebSocket(wsUrl); - + wsRef.current = ws; const fitAddon = new FitAddon(); termi.loadAddon(fitAddon); // @ts-ignore @@ -54,6 +58,10 @@ export const DockerLogsId: React.FC = ({ termi.focus(); setTerm(termi); + ws.onerror = (error) => { + console.error("WebSocket error: ", error); + }; + ws.onmessage = (e) => { termi.write(e.data); }; @@ -62,12 +70,21 @@ export const DockerLogsId: React.FC = ({ console.log(e.reason); termi.write(`Connection closed!\nReason: ${e.reason}\n`); + wsRef.current = null; }; + return termi; }; useEffect(() => { createTerminal(); + + return () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + wsRef.current.close(); + wsRef.current = null; + } + }; }, [lines, containerId]); useEffect(() => { diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index 833d3bc34..e0b076c9b 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -49,39 +49,45 @@ export const setupDockerContainerLogsWebSocketServer = ( if (!server.sshKeyId) return; const client = new Client(); - new Promise((resolve, reject) => { - client - .once("ready", () => { - const command = ` + client + .once("ready", () => { + const command = ` bash -c "docker container logs --tail ${tail} --follow ${containerId}" `; - client.exec(command, (err, stream) => { - if (err) { - console.error("Execution error:", err); - reject(err); - return; - } - stream - .on("close", () => { - console.log("Connection closed ✅"); - client.end(); - resolve(); - }) - .on("data", (data: string) => { - ws.send(data.toString()); - }) - .stderr.on("data", (data) => { - ws.send(data.toString()); - }); - }); - }) - .connect({ - host: server.ipAddress, - port: server.port, - username: server.username, - privateKey: server.sshKey?.privateKey, - timeout: 99999, + client.exec(command, (err, stream) => { + if (err) { + console.error("Execution error:", err); + ws.close(); + return; + } + stream + .on("close", () => { + console.log("Connection closed ✅"); + client.end(); + ws.close(); + }) + .on("data", (data: string) => { + ws.send(data.toString()); + }) + .stderr.on("data", (data) => { + ws.send(data.toString()); + }); }); + }) + .on("error", (err) => { + console.error("SSH connection error:", err); + ws.send(`SSH error: ${err.message}`); + ws.close(); // Cierra el WebSocket si hay un error con SSH + }) + .connect({ + host: server.ipAddress, + port: server.port, + username: server.username, + privateKey: server.sshKey?.privateKey, + }); + ws.on("close", () => { + console.log("Connection closed ✅, From WS"); + client.end(); }); } else { const shell = getShell();