mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -26,11 +25,13 @@ export const DockerTerminal: React.FC<Props> = ({
|
||||
}
|
||||
const term = new Terminal({
|
||||
cursorBlink: true,
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
lineHeight: 1.4,
|
||||
convertEol: true,
|
||||
theme: {
|
||||
cursor: "transparent",
|
||||
background: "transparent",
|
||||
background: "rgba(0, 0, 0, 0)",
|
||||
},
|
||||
});
|
||||
const addonFit = new FitAddon();
|
||||
@@ -46,7 +47,6 @@ export const DockerTerminal: React.FC<Props> = ({
|
||||
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<Props> = ({
|
||||
}, [containerId, activeWay, id]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span>
|
||||
Select way to connect to <b>{containerId}</b>
|
||||
</span>
|
||||
@@ -66,7 +66,7 @@ export const DockerTerminal: React.FC<Props> = ({
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="w-full h-full rounded-lg p-2 border min-h-[50vh]">
|
||||
<div className="w-full h-full rounded-lg p-2 bg-[#19191A]">
|
||||
<div id={id} ref={termRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { api } from "@/utils/api";
|
||||
import dynamic from "next/dynamic";
|
||||
import type React from "react";
|
||||
|
||||
const Terminal = dynamic(async () => (await import("./terminal")).Terminal, {
|
||||
const Terminal = dynamic(() => import("./terminal").then((e) => e.Terminal), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -21,11 +20,13 @@ export const Terminal: React.FC<Props> = ({ id, serverId }) => {
|
||||
}
|
||||
const term = new XTerm({
|
||||
cursorBlink: true,
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
lineHeight: 1.4,
|
||||
convertEol: true,
|
||||
theme: {
|
||||
cursor: "transparent",
|
||||
background: "transparent",
|
||||
background: "#19191A",
|
||||
},
|
||||
});
|
||||
const addonFit = new FitAddon();
|
||||
@@ -41,17 +42,15 @@ export const Terminal: React.FC<Props> = ({ 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 (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<div className="w-full h-full border rounded-lg p-2 min-h-[50vh]">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="w-full h-full bg-input rounded-lg p-2 ">
|
||||
<div id={id} ref={termRef} className="rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,11 +35,6 @@
|
||||
"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",
|
||||
@@ -76,6 +71,8 @@
|
||||
"@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",
|
||||
@@ -120,6 +117,7 @@
|
||||
"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"
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -70,44 +70,53 @@ export const setupTerminalWebSocketServer = (
|
||||
let stderr = "";
|
||||
conn
|
||||
.once("ready", () => {
|
||||
conn.shell({}, (err, stream) => {
|
||||
if (err) throw err;
|
||||
conn.shell(
|
||||
{
|
||||
term: "terminal",
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
height: 30,
|
||||
width: 80,
|
||||
},
|
||||
(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") {
|
||||
|
||||
Reference in New Issue
Block a user