From d20f86ffe13a4a285381d31c9be03f7c3e3a52b3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 14 Dec 2024 22:46:01 -0600 Subject: [PATCH] refactor: remove unsued files --- .../docker/terminal/docker-terminal.tsx | 12 +- .../settings/web-server/terminal-modal.tsx | 2 +- .../settings/web-server/terminal.tsx | 11 +- apps/dokploy/package.json | 8 +- .../server/wss/docker-container-terminal.ts | 12 +- apps/dokploy/server/wss/terminal.ts | 77 ++++----- packages/server/src/index.ts | 5 - packages/server/src/services/auth.ts | 2 +- .../server/src/wss/docker-container-logs.ts | 133 --------------- .../src/wss/docker-container-terminal.ts | 152 ------------------ packages/server/src/wss/docker-stats.ts | 96 ----------- packages/server/src/wss/listen-deployment.ts | 101 ------------ packages/server/src/wss/terminal.ts | 107 ------------ packages/server/src/wss/utils.ts | 29 ++-- pnpm-lock.yaml | 39 +++-- 15 files changed, 106 insertions(+), 680 deletions(-) delete mode 100644 packages/server/src/wss/docker-container-logs.ts delete mode 100644 packages/server/src/wss/docker-container-terminal.ts delete mode 100644 packages/server/src/wss/docker-stats.ts delete mode 100644 packages/server/src/wss/listen-deployment.ts delete mode 100644 packages/server/src/wss/terminal.ts diff --git a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx index 4008d6fd..098860cf 100644 --- a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx +++ b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx @@ -4,6 +4,7 @@ import { FitAddon } from "xterm-addon-fit"; import "@xterm/xterm/css/xterm.css"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { AttachAddon } from "@xterm/addon-attach"; +import { WebLinksAddon } from "@xterm/addon-web-links"; interface Props { id: string; @@ -25,13 +26,11 @@ export const DockerTerminal: React.FC = ({ } const term = new Terminal({ cursorBlink: true, - cols: 80, - rows: 30, lineHeight: 1.4, convertEol: true, theme: { cursor: "transparent", - background: "rgba(0, 0, 0, 0)", + background: "transparent", }, }); const addonFit = new FitAddon(); @@ -47,6 +46,7 @@ export const DockerTerminal: React.FC = ({ term.open(termRef.current); term.loadAddon(addonFit); term.loadAddon(addonAttach); + term.loadAddon(new WebLinksAddon()); addonFit.fit(); return () => { ws.readyState === WebSocket.OPEN && ws.close(); @@ -54,8 +54,8 @@ export const DockerTerminal: React.FC = ({ }, [containerId, activeWay, id]); return ( -
-
+
+
Select way to connect to {containerId} @@ -66,7 +66,7 @@ export const DockerTerminal: React.FC = ({
-
+
diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx index 5bdba8b8..19053879 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx @@ -11,7 +11,7 @@ import { api } from "@/utils/api"; import dynamic from "next/dynamic"; import type React from "react"; -const Terminal = dynamic(() => import("./terminal").then((e) => e.Terminal), { +const Terminal = dynamic(async () => (await import("./terminal")).Terminal, { ssr: false, }); diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx index 2fe7f83c..f5febcda 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx @@ -4,6 +4,7 @@ import { useEffect, useRef } from "react"; import { FitAddon } from "xterm-addon-fit"; import "@xterm/xterm/css/xterm.css"; import { AttachAddon } from "@xterm/addon-attach"; +import { WebLinksAddon } from "@xterm/addon-web-links"; interface Props { id: string; @@ -20,13 +21,11 @@ export const Terminal: React.FC = ({ id, serverId }) => { } const term = new XTerm({ cursorBlink: true, - cols: 80, - rows: 30, lineHeight: 1.4, convertEol: true, theme: { cursor: "transparent", - background: "#19191A", + background: "transparent", }, }); const addonFit = new FitAddon(); @@ -42,15 +41,17 @@ export const Terminal: React.FC = ({ id, serverId }) => { term.open(termRef.current); term.loadAddon(addonFit); term.loadAddon(addonAttach); + term.loadAddon(new WebLinksAddon()); addonFit.fit(); + return () => { ws.readyState === WebSocket.OPEN && ws.close(); }; }, [id, serverId]); return ( -
-
+
+
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 292d3efb..14286dc1 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,6 +35,11 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { + "xterm-addon-fit": "0.8.0", + "@xterm/xterm": "^5.3.0", + "xterm": "5.2.1", + "@xterm/addon-attach": "^0.11.0", + "@xterm/addon-web-links": "^0.10.0", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-yaml": "^6.1.1", "@codemirror/language": "^6.10.1", @@ -71,8 +76,6 @@ "@trpc/server": "^10.43.6", "@uiw/codemirror-theme-github": "^4.22.1", "@uiw/react-codemirror": "^4.22.1", - "@xterm/addon-attach": "0.10.0", - "@xterm/xterm": "^5.4.0", "adm-zip": "^0.5.14", "bcrypt": "5.1.1", "bullmq": "5.4.2", @@ -117,7 +120,6 @@ "undici": "^6.19.2", "use-resize-observer": "9.1.0", "ws": "8.16.0", - "xterm-addon-fit": "^0.8.0", "zod": "^3.23.4", "zod-form-data": "^2.0.2" }, diff --git a/apps/dokploy/server/wss/docker-container-terminal.ts b/apps/dokploy/server/wss/docker-container-terminal.ts index eeba72d5..4bf49bf3 100644 --- a/apps/dokploy/server/wss/docker-container-terminal.ts +++ b/apps/dokploy/server/wss/docker-container-terminal.ts @@ -110,12 +110,12 @@ export const setupDockerContainerTerminalWebSocketServer = ( shell, ["-c", `docker exec -it ${containerId} ${activeWay}`], { - name: "xterm-256color", - cwd: process.env.HOME, - env: process.env, - encoding: "utf8", - cols: 80, - rows: 30, + // name: "xterm-256color", + // cwd: process.env.HOME, + // env: process.env, + // encoding: "utf8", + // cols: 80, + // rows: 30, }, ); diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index eb0bf2e2..a6338afe 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -70,53 +70,44 @@ export const setupTerminalWebSocketServer = ( let stderr = ""; conn .once("ready", () => { - conn.shell( - { - term: "terminal", - cols: 80, - rows: 30, - height: 30, - width: 80, - }, - (err, stream) => { - if (err) throw err; + conn.shell({}, (err, stream) => { + if (err) throw err; - stream - .on("close", (code: number, signal: string) => { - ws.send(`\nContainer closed with code: ${code}\n`); - conn.end(); - }) - .on("data", (data: string) => { - stdout += data.toString(); - ws.send(data.toString()); - }) - .stderr.on("data", (data) => { - stderr += data.toString(); - ws.send(data.toString()); - console.error("Error: ", data.toString()); - }); + stream + .on("close", (code: number, signal: string) => { + ws.send(`\nContainer closed with code: ${code}\n`); + conn.end(); + }) + .on("data", (data: string) => { + stdout += data.toString(); + ws.send(data.toString()); + }) + .stderr.on("data", (data) => { + stderr += data.toString(); + ws.send(data.toString()); + console.error("Error: ", data.toString()); + }); - ws.on("message", (message) => { - try { - let command: string | Buffer[] | Buffer | ArrayBuffer; - if (Buffer.isBuffer(message)) { - command = message.toString("utf8"); - } else { - command = message; - } - stream.write(command.toString()); - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - ws.send(errorMessage); + ws.on("message", (message) => { + try { + let command: string | Buffer[] | Buffer | ArrayBuffer; + if (Buffer.isBuffer(message)) { + command = message.toString("utf8"); + } else { + command = message; } - }); + stream.write(command.toString()); + } catch (error) { + // @ts-ignore + const errorMessage = error?.message as unknown as string; + ws.send(errorMessage); + } + }); - ws.on("close", () => { - stream.end(); - }); - }, - ); + ws.on("close", () => { + stream.end(); + }); + }); }) .on("error", (err) => { if (err.level === "client-authentication") { diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index f3f1e96f..41f2b0fd 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -102,11 +102,6 @@ export * from "./utils/traefik/security"; export * from "./utils/traefik/types"; export * from "./utils/traefik/web-server"; -export * from "./wss/docker-container-logs"; -export * from "./wss/docker-container-terminal"; -export * from "./wss/docker-stats"; -export * from "./wss/listen-deployment"; -export * from "./wss/terminal"; export * from "./wss/utils"; export * from "./utils/access-log/handler"; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 11e2d24c..598e39e3 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -7,7 +7,7 @@ import { auth, users, } from "@dokploy/server/db/schema"; -import { getPublicIpWithFallback } from "@dokploy/server/wss/terminal"; +import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; import { eq } from "drizzle-orm"; diff --git a/packages/server/src/wss/docker-container-logs.ts b/packages/server/src/wss/docker-container-logs.ts deleted file mode 100644 index 75292018..00000000 --- a/packages/server/src/wss/docker-container-logs.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type http from "node:http"; -import { findServerById } from "@dokploy/server/services/server"; -import { spawn } from "node-pty"; -import { Client } from "ssh2"; -import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; -import { getShell } from "./utils"; - -export const setupDockerContainerLogsWebSocketServer = ( - server: http.Server, -) => { - const wssTerm = new WebSocketServer({ - noServer: true, - path: "/docker-container-logs", - }); - - server.on("upgrade", (req, socket, head) => { - const { pathname } = new URL(req.url || "", `http://${req.headers.host}`); - - if (pathname === "/_next/webpack-hmr") { - return; - } - if (pathname === "/docker-container-logs") { - wssTerm.handleUpgrade(req, socket, head, function done(ws) { - wssTerm.emit("connection", ws, req); - }); - } - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); - const containerId = url.searchParams.get("containerId"); - const tail = url.searchParams.get("tail"); - const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); - - if (!containerId) { - ws.close(4000, "containerId no provided"); - return; - } - - if (!user || !session) { - ws.close(); - return; - } - try { - if (serverId) { - const server = await findServerById(serverId); - - if (!server.sshKeyId) return; - const client = new Client(); - new Promise((resolve, reject) => { - 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", () => { - 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, - }); - }); - } else { - const shell = getShell(); - const ptyProcess = spawn( - shell, - [ - "-c", - `docker container logs --tail ${tail} --follow ${containerId}`, - ], - { - name: "xterm-256color", - cwd: process.env.HOME, - env: process.env, - encoding: "utf8", - cols: 80, - rows: 30, - }, - ); - - ptyProcess.onData((data) => { - ws.send(data); - }); - ws.on("close", () => { - ptyProcess.kill(); - }); - ws.on("message", (message) => { - try { - let command: string | Buffer[] | Buffer | ArrayBuffer; - if (Buffer.isBuffer(message)) { - command = message.toString("utf8"); - } else { - command = message; - } - ptyProcess.write(command.toString()); - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - ws.send(errorMessage); - } - }); - } - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - - ws.send(errorMessage); - } - }); -}; diff --git a/packages/server/src/wss/docker-container-terminal.ts b/packages/server/src/wss/docker-container-terminal.ts deleted file mode 100644 index 0cb174b6..00000000 --- a/packages/server/src/wss/docker-container-terminal.ts +++ /dev/null @@ -1,152 +0,0 @@ -import type http from "node:http"; -import { findServerById } from "@dokploy/server/services/server"; -import { spawn } from "node-pty"; -import { Client } from "ssh2"; -import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; -import { getShell } from "./utils"; - -export const setupDockerContainerTerminalWebSocketServer = ( - server: http.Server, -) => { - const wssTerm = new WebSocketServer({ - noServer: true, - path: "/docker-container-terminal", - }); - - server.on("upgrade", (req, socket, head) => { - const { pathname } = new URL(req.url || "", `http://${req.headers.host}`); - - if (pathname === "/_next/webpack-hmr") { - return; - } - if (pathname === "/docker-container-terminal") { - wssTerm.handleUpgrade(req, socket, head, function done(ws) { - wssTerm.emit("connection", ws, req); - }); - } - }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); - const containerId = url.searchParams.get("containerId"); - const activeWay = url.searchParams.get("activeWay"); - const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); - - if (!containerId) { - ws.close(4000, "containerId no provided"); - return; - } - - if (!user || !session) { - ws.close(); - return; - } - try { - if (serverId) { - const server = await findServerById(serverId); - if (!server.sshKeyId) - throw new Error("No SSH key available for this server"); - - const conn = new Client(); - let stdout = ""; - let stderr = ""; - conn - .once("ready", () => { - conn.exec( - `docker exec -it ${containerId} ${activeWay}`, - { pty: true }, - (err, stream) => { - if (err) throw err; - - stream - .on("close", (code: number, signal: string) => { - ws.send(`\nContainer closed with code: ${code}\n`); - conn.end(); - }) - .on("data", (data: string) => { - stdout += data.toString(); - ws.send(data.toString()); - }) - .stderr.on("data", (data) => { - stderr += data.toString(); - ws.send(data.toString()); - console.error("Error: ", data.toString()); - }); - - ws.on("message", (message) => { - try { - let command: string | Buffer[] | Buffer | ArrayBuffer; - if (Buffer.isBuffer(message)) { - command = message.toString("utf8"); - } else { - command = message; - } - stream.write(command.toString()); - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - ws.send(errorMessage); - } - }); - - ws.on("close", () => { - stream.end(); - }); - }, - ); - }) - .connect({ - host: server.ipAddress, - port: server.port, - username: server.username, - privateKey: server.sshKey?.privateKey, - timeout: 99999, - }); - } else { - const shell = getShell(); - const ptyProcess = spawn( - shell, - ["-c", `docker exec -it ${containerId} ${activeWay}`], - { - name: "xterm-256color", - cwd: process.env.HOME, - env: process.env, - encoding: "utf8", - cols: 80, - rows: 30, - }, - ); - - ptyProcess.onData((data) => { - ws.send(data); - }); - ws.on("close", () => { - ptyProcess.kill(); - }); - ws.on("message", (message) => { - try { - let command: string | Buffer[] | Buffer | ArrayBuffer; - if (Buffer.isBuffer(message)) { - command = message.toString("utf8"); - } else { - command = message; - } - ptyProcess.write(command.toString()); - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - ws.send(errorMessage); - } - }); - } - } catch (error) { - // @ts-ignore - const errorMessage = error?.message as unknown as string; - - ws.send(errorMessage); - } - }); -}; diff --git a/packages/server/src/wss/docker-stats.ts b/packages/server/src/wss/docker-stats.ts deleted file mode 100644 index ed1dc46f..00000000 --- a/packages/server/src/wss/docker-stats.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type http from "node:http"; -import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; -import { docker } from "../constants"; -import { - getLastAdvancedStatsFile, - recordAdvancedStats, -} from "../monitoring/utilts"; - -export const setupDockerStatsMonitoringSocketServer = ( - server: http.Server, -) => { - const wssTerm = new WebSocketServer({ - noServer: true, - path: "/listen-docker-stats-monitoring", - }); - - server.on("upgrade", (req, socket, head) => { - const { pathname } = new URL(req.url || "", `http://${req.headers.host}`); - - if (pathname === "/_next/webpack-hmr") { - return; - } - if (pathname === "/listen-docker-stats-monitoring") { - wssTerm.handleUpgrade(req, socket, head, function done(ws) { - wssTerm.emit("connection", ws, req); - }); - } - }); - - wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); - const appName = url.searchParams.get("appName"); - const appType = (url.searchParams.get("appType") || "application") as - | "application" - | "stack" - | "docker-compose"; - const { user, session } = await validateWebSocketRequest(req); - - if (!appName) { - ws.close(4000, "appName no provided"); - return; - } - - if (!user || !session) { - ws.close(); - return; - } - const intervalId = setInterval(async () => { - try { - const filter = { - status: ["running"], - ...(appType === "application" && { - label: [`com.docker.swarm.service.name=${appName}`], - }), - ...(appType === "stack" && { - label: [`com.docker.swarm.task.name=${appName}`], - }), - ...(appType === "docker-compose" && { - name: [appName], - }), - }; - - const containers = await docker.listContainers({ - filters: JSON.stringify(filter), - }); - - const container = containers[0]; - if (!container || container?.State !== "running") { - ws.close(4000, "Container not running"); - return; - } - - const stats = await docker.getContainer(container.Id).stats({ - stream: false, - }); - - await recordAdvancedStats(stats, appName); - const data = await getLastAdvancedStatsFile(appName); - - ws.send( - JSON.stringify({ - data, - }), - ); - } catch (error) { - // @ts-ignore - ws.close(4000, `Error: ${error.message}`); - } - }, 1300); - - ws.on("close", () => { - clearInterval(intervalId); - }); - }); -}; diff --git a/packages/server/src/wss/listen-deployment.ts b/packages/server/src/wss/listen-deployment.ts deleted file mode 100644 index 363a3cc8..00000000 --- a/packages/server/src/wss/listen-deployment.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { spawn } from "node:child_process"; -import type http from "node:http"; -import { findServerById } from "@dokploy/server/services/server"; -import { Client } from "ssh2"; -import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; - -export const setupDeploymentLogsWebSocketServer = ( - server: http.Server, -) => { - const wssTerm = new WebSocketServer({ - noServer: true, - path: "/listen-deployment", - }); - - server.on("upgrade", (req, socket, head) => { - const { pathname } = new URL(req.url || "", `http://${req.headers.host}`); - - if (pathname === "/_next/webpack-hmr") { - return; - } - if (pathname === "/listen-deployment") { - wssTerm.handleUpgrade(req, socket, head, function done(ws) { - wssTerm.emit("connection", ws, req); - }); - } - }); - - wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); - const logPath = url.searchParams.get("logPath"); - const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); - - if (!logPath) { - ws.close(4000, "logPath no provided"); - return; - } - - if (!user || !session) { - ws.close(); - return; - } - - try { - if (serverId) { - const server = await findServerById(serverId); - - if (!server.sshKeyId) return; - const client = new Client(); - new Promise((resolve, reject) => { - client - .on("ready", () => { - const command = ` - tail -n +1 -f ${logPath}; - `; - client.exec(command, (err, stream) => { - if (err) { - console.error("Execution error:", err); - reject(err); - return; - } - stream - .on("close", () => { - 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, - }); - }); - } else { - const tail = spawn("tail", ["-n", "+1", "-f", logPath]); - - tail.stdout.on("data", (data) => { - ws.send(data.toString()); - }); - - tail.stderr.on("data", (data) => { - ws.send(new Error(`tail error: ${data.toString()}`).message); - }); - } - } catch (error) { - // @ts-ignore - // const errorMessage = error?.message as unknown as string; - ws.send(errorMessage); - } - }); -}; diff --git a/packages/server/src/wss/terminal.ts b/packages/server/src/wss/terminal.ts deleted file mode 100644 index 562040d7..00000000 --- a/packages/server/src/wss/terminal.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type http from "node:http"; -import path from "node:path"; -import { findServerById } from "@dokploy/server/services/server"; -import { spawn } from "node-pty"; -import { publicIpv4, publicIpv6 } from "public-ip"; -import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; -import { paths } from "../constants"; - -export const getPublicIpWithFallback = async () => { - // @ts-ignore - let ip = null; - try { - ip = await publicIpv4(); - } catch (error) { - console.log( - "Error to obtain public IPv4 address, falling back to IPv6", - // @ts-ignore - error.message, - ); - try { - ip = await publicIpv6(); - } catch (error) { - // @ts-ignore - console.error("Error to obtain public IPv6 address", error.message); - ip = null; - } - } - return ip; -}; - -export const setupTerminalWebSocketServer = ( - server: http.Server, -) => { - const wssTerm = new WebSocketServer({ - noServer: true, - path: "/terminal", - }); - - server.on("upgrade", (req, socket, head) => { - const { pathname } = new URL(req.url || "", `http://${req.headers.host}`); - if (pathname === "/_next/webpack-hmr") { - return; - } - if (pathname === "/terminal") { - wssTerm.handleUpgrade(req, socket, head, function done(ws) { - wssTerm.emit("connection", ws, req); - }); - } - }); - - wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); - const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); - if (!user || !session || !serverId) { - ws.close(); - return; - } - - const server = await findServerById(serverId); - - if (!server) { - ws.close(); - return; - } - const { SSH_PATH } = paths(); - const privateKey = path.join(SSH_PATH, `${server.sshKeyId}_rsa`); - const sshCommand = [ - "ssh", - "-o", - "StrictHostKeyChecking=no", - "-i", - privateKey, - `${server.username}@${server.ipAddress}`, - ]; - const ptyProcess = spawn("ssh", sshCommand.slice(1), { - name: "xterm-256color", - cwd: process.env.HOME, - env: process.env, - encoding: "utf8", - cols: 80, - rows: 30, - }); - - ptyProcess.onData((data) => { - ws.send(data); - }); - ws.on("message", (message) => { - try { - let command: string | Buffer[] | Buffer | ArrayBuffer; - if (Buffer.isBuffer(message)) { - command = message.toString("utf8"); - } else { - command = message; - } - ptyProcess.write(command.toString()); - } catch (error) { - console.log(error); - } - }); - - ws.on("close", () => { - ptyProcess.kill(); - }); - }); -}; diff --git a/packages/server/src/wss/utils.ts b/packages/server/src/wss/utils.ts index b5567127..d9190f3c 100644 --- a/packages/server/src/wss/utils.ts +++ b/packages/server/src/wss/utils.ts @@ -1,12 +1,23 @@ -import os from "node:os"; +import { publicIpv4, publicIpv6 } from "public-ip"; -export const getShell = () => { - switch (os.platform()) { - case "win32": - return "powershell.exe"; - case "darwin": - return "zsh"; - default: - return "bash"; +export const getPublicIpWithFallback = async () => { + // @ts-ignore + let ip = null; + try { + ip = await publicIpv4(); + } catch (error) { + console.log( + "Error to obtain public IPv4 address, falling back to IPv6", + // @ts-ignore + error.message, + ); + try { + ip = await publicIpv6(); + } catch (error) { + // @ts-ignore + console.error("Error to obtain public IPv6 address", error.message); + ip = null; + } } + return ip; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09d71bbf..6a3069f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -206,10 +206,13 @@ importers: specifier: ^4.22.1 version: 4.23.0(@babel/runtime@7.25.0)(@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@xterm/addon-attach': - specifier: 0.10.0 + specifier: ^0.11.0 + version: 0.11.0(@xterm/xterm@5.5.0) + '@xterm/addon-web-links': + specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) '@xterm/xterm': - specifier: ^5.4.0 + specifier: ^5.3.0 version: 5.5.0 adm-zip: specifier: ^0.5.14 @@ -343,9 +346,12 @@ importers: ws: specifier: 8.16.0 version: 8.16.0 + xterm: + specifier: 5.2.1 + version: 5.2.1 xterm-addon-fit: - specifier: ^0.8.0 - version: 0.8.0(xterm@5.3.0) + specifier: 0.8.0 + version: 0.8.0(xterm@5.2.1) zod: specifier: ^3.23.4 version: 3.23.8 @@ -3400,8 +3406,13 @@ packages: '@webassemblyjs/wast-printer@1.12.1': resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} - '@xterm/addon-attach@0.10.0': - resolution: {integrity: sha512-ES/XO8pC1tPHSkh4j7qzM8ajFt++u8KMvfRc9vKIbjHTDOxjl9IUVo+vcQgLn3FTCM3w2czTvBss8nMWlD83Cg==} + '@xterm/addon-attach@0.11.0': + resolution: {integrity: sha512-JboCN0QAY6ZLY/SSB/Zl2cQ5zW1Eh4X3fH7BnuR1NB7xGRhzbqU2Npmpiw/3zFlxDaU88vtKzok44JKi2L2V2Q==} + peerDependencies: + '@xterm/xterm': ^5.0.0 + + '@xterm/addon-web-links@0.10.0': + resolution: {integrity: sha512-QhrHCUr8w6ATGviyXwcAIM1qN3nD1hdxwMC8fsW7z/6aaQlb2nt7zmByJt4eOn7ZzrHOzczljqV5S2pkdQp2xw==} peerDependencies: '@xterm/xterm': ^5.0.0 @@ -6785,8 +6796,8 @@ packages: peerDependencies: xterm: ^5.0.0 - xterm@5.3.0: - resolution: {integrity: sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==} + xterm@5.2.1: + resolution: {integrity: sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA==} deprecated: This package is now deprecated. Move to @xterm/xterm instead. y18n@4.0.3: @@ -9635,7 +9646,11 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 - '@xterm/addon-attach@0.10.0(@xterm/xterm@5.5.0)': + '@xterm/addon-attach@0.11.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + + '@xterm/addon-web-links@0.10.0(@xterm/xterm@5.5.0)': dependencies: '@xterm/xterm': 5.5.0 @@ -13098,11 +13113,11 @@ snapshots: xtend@4.0.2: {} - xterm-addon-fit@0.8.0(xterm@5.3.0): + xterm-addon-fit@0.8.0(xterm@5.2.1): dependencies: - xterm: 5.3.0 + xterm: 5.2.1 - xterm@5.3.0: {} + xterm@5.2.1: {} y18n@4.0.3: {}