second commit
This commit is contained in:
100
static/js/terminal.js
Normal file
100
static/js/terminal.js
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* SSH-Terminal-Client: xterm.js <-> /ws/ssh/{host_id}.
|
||||
*
|
||||
* Vollbild: native Browser-Fullscreen-API auf dem Session-Container (Konzept 4.3).
|
||||
* Copy & Paste: xterm.js liefert dies bei SSH bereits nativ ueber die
|
||||
* System-Zwischenablage (Markieren-zum-Kopieren / Strg+Umschalt+V) -- keine
|
||||
* serverseitige Sonderbehandlung noetig, im Gegensatz zu RDP (siehe rdp.js).
|
||||
*/
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const hostId = window.JUMPHOST_HOST_ID;
|
||||
const statusEl = document.getElementById("status");
|
||||
const shell = document.getElementById("session-shell");
|
||||
|
||||
const term = new Terminal({
|
||||
cursorBlink: true,
|
||||
fontFamily: "Menlo, Consolas, monospace",
|
||||
fontSize: 14,
|
||||
theme: { background: "#000000" },
|
||||
});
|
||||
const fitAddon = new FitAddon.FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
term.open(document.getElementById("terminal"));
|
||||
fitAddon.fit();
|
||||
|
||||
function b64encode(str) {
|
||||
return btoa(unescape(encodeURIComponent(str)));
|
||||
}
|
||||
function b64decode(b64) {
|
||||
return decodeURIComponent(escape(atob(b64)));
|
||||
}
|
||||
|
||||
const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const ws = new WebSocket(`${proto}//${window.location.host}/ws/ssh/${hostId}`);
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
statusEl.textContent = "Verbunden";
|
||||
sendResize();
|
||||
});
|
||||
ws.addEventListener("close", () => { statusEl.textContent = "Verbindung beendet"; });
|
||||
ws.addEventListener("error", () => { statusEl.textContent = "Verbindungsfehler"; });
|
||||
|
||||
ws.addEventListener("message", (ev) => {
|
||||
const msg = JSON.parse(ev.data);
|
||||
if (msg.type === "output") {
|
||||
term.write(b64decode(msg.data));
|
||||
} else if (msg.type === "error") {
|
||||
statusEl.textContent = "Fehler: " + msg.message;
|
||||
}
|
||||
});
|
||||
|
||||
term.onData((data) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: "input", data: b64encode(data) }));
|
||||
}
|
||||
});
|
||||
|
||||
function sendResize() {
|
||||
fitAddon.fit();
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: "resize", cols: term.cols, rows: term.rows }));
|
||||
}
|
||||
}
|
||||
window.addEventListener("resize", sendResize);
|
||||
|
||||
document.getElementById("fullscreen-btn").addEventListener("click", () => {
|
||||
if (!document.fullscreenElement) {
|
||||
shell.requestFullscreen().catch(() => {});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
document.addEventListener("fullscreenchange", sendResize);
|
||||
|
||||
const fileInput = document.getElementById("file-input");
|
||||
document.getElementById("upload-btn").addEventListener("click", () => fileInput.click());
|
||||
fileInput.addEventListener("change", async () => {
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
const remotePath = prompt("Zielpfad auf dem Server:", `/tmp/${file.name}`);
|
||||
if (!remotePath) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
statusEl.textContent = `Lade ${file.name} hoch ...`;
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/ssh/${hostId}/files/upload?remote_path=${encodeURIComponent(remotePath)}`,
|
||||
{ method: "POST", credentials: "same-origin", body: formData }
|
||||
);
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.detail || "Upload fehlgeschlagen");
|
||||
statusEl.textContent = `Upload ok (AV: ${data.av_scan_result})`;
|
||||
} catch (err) {
|
||||
statusEl.textContent = "Upload-Fehler: " + err.message;
|
||||
}
|
||||
fileInput.value = "";
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user