Let's get this thing working

This commit is contained in:
2024-12-20 23:21:31 -05:00
parent c1b79c4cf6
commit 2234f3df40
7 changed files with 1418 additions and 0 deletions

42
.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

8
Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM node:slim
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
CMD ["node", "server.js"]

11
config.json Normal file
View File

@@ -0,0 +1,11 @@
{
"web": {
"ip": "0.0.0.0",
"port": 80
},
"ssh": {
"host": "host.docker.internal",
"port": 22,
"username": "gibby"
}
}

69
index.html Normal file
View File

@@ -0,0 +1,69 @@
<html>
<head>
<title>SSH Terminal</title>
<link rel="stylesheet" href="/xterm.css" />
<script src="/xterm.js"></script>
<script src="/xterm-addon-fit.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
window.addEventListener(
"load",
function () {
var terminalContainer = document.getElementById("terminal-container");
const term = new Terminal({ cursorBlink: true });
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
term.open(terminalContainer);
fitAddon.fit();
var socket = io(); //.connect();
socket.on("connect", function () {
term.write("\r\n*** Connected to backend ***\r\n");
});
// Browser -> Backend
term.onKey(function (ev) {
socket.emit("data", ev.key);
});
// Backend -> Browser
socket.on("data", function (data) {
term.write(data);
});
socket.on("disconnect", function () {
term.write("\r\n*** Disconnected from backend ***\r\n");
});
},
false
);
</script>
<style>
body {
font-family: helvetica, sans-serif, arial;
font-size: 1em;
color: #111;
}
h1 {
text-align: center;
}
#terminal-container {
width: 960px;
height: 600px;
margin: 0 auto;
padding: 2px;
}
#terminal-container .terminal {
background-color: #111;
color: #fafafa;
padding: 2px;
}
#terminal-container .terminal:focus .terminal-cursor {
background-color: #fafafa;
}
</style>
</head>
<body>
<div id="terminal-container"></div>
</body>
</html>

1170
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "webterm",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"express": "^4.21.2",
"socket.io": "^4.8.1",
"ssh2": "^1.16.0"
}
}

100
server.js Normal file
View File

@@ -0,0 +1,100 @@
var fs = require("fs");
var path = require("path");
var server = require("http").createServer(onRequest);
var io = require("socket.io")(server);
var SSHClient = require("ssh2").Client;
var config = {};
try {
// try and load the config file
const data = fs.readFileSync("/data/config.json", "utf-8");
config = JSON.parse(data);
console.log("Loaded config.json");
} catch (ex) {
console.log("Unable to load config.json, using defaults");
console.error(ex);
}
var sshConfig = {
host: config.ssh?.host ?? "host.docker.internal",
port: config.ssh?.port ?? 22,
username: config.ssh?.username,
};
if (config.ssh?.password) {
sshConfig.password = config.ssh.password;
} else if (config.ssh?.privateKey) {
sshConfig.privateKey = fs.readFileSync(
path.join("/data", config.ssh.privateKey)
);
}
// Load static files into memory
var staticFiles = {};
var basePath = path.join(require.resolve("@xterm/xterm"), "..");
staticFiles["/xterm.css"] = fs.readFileSync(
path.join(basePath, "../css/xterm.css")
);
staticFiles["/xterm.js"] = fs.readFileSync(path.join(basePath, "xterm.js"));
basePath = path.join(require.resolve("@xterm/addon-fit"), "..");
staticFiles["/xterm-addon-fit.js"] = fs.readFileSync(
path.join(basePath, "addon-fit.js")
);
staticFiles["/"] = fs.readFileSync("index.html");
// Handle static file serving
function onRequest(req, res) {
var file;
if (req.method === "GET" && (file = staticFiles[req.url])) {
res.writeHead(200, {
"Content-Type":
"text/" +
(/css$/.test(req.url)
? "css"
: /js$/.test(req.url)
? "javascript"
: "html"),
});
return res.end(file);
}
res.writeHead(404);
res.end();
}
io.on("connection", function (socket) {
var conn = new SSHClient();
conn
.on("ready", function () {
socket.emit("data", "\r\nConnecting...\r\n");
conn.shell(function (err, stream) {
if (err)
return socket.emit(
"data",
`\r\nConnection Error: ${err.message}\r\n`
);
socket.on("data", function (data) {
stream.write(data);
});
stream
.on("data", function (d) {
socket.emit("data", d.toString("binary"));
})
.on("close", function () {
conn.end();
});
});
})
.on("close", function () {
socket.emit("data", "\r\nConnection Closed\r\n");
})
.on("error", function (err) {
socket.emit("data", `\r\nConnection Error: ${err.message}\r\n`);
})
.connect(sshConfig);
});
let port = config.web?.port ?? 80;
let ip = config.web?.ip ?? "0.0.0.0";
console.log(`Listening on ${ip}:${port}`);
server.listen(port, ip);