From 8ddb3ae589f3c487cc2f6e796d6c601602521071 Mon Sep 17 00:00:00 2001 From: Midas Wollinger Date: Fri, 21 Aug 2026 23:14:19 +0200 Subject: [PATCH] connectiopn fix round 2 4 --- static/js/admin.js | 10 +++++++++ static/js/rdp.js | 2 ++ static/js/terminal.js | 49 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/static/js/admin.js b/static/js/admin.js index 4cc162d..655355a 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -1414,6 +1414,11 @@ } document.getElementById("watch-close-btn").addEventListener("click", closeWatchOverlay); + // Klick auf den abgedunkelten Hintergrund (nicht auf das Panel selbst) + // schliesst die Live-Ansicht ebenfalls. + document.getElementById("watch-overlay").addEventListener("click", (ev) => { + if (ev.target === ev.currentTarget) closeWatchOverlay(); + }); function watchSession(session) { closeWatchOverlay(); @@ -1517,6 +1522,11 @@ } document.getElementById("rec-close-btn").addEventListener("click", closeRecordingOverlay); + // Klick auf den abgedunkelten Hintergrund (nicht auf das Panel selbst) + // schliesst die Wiedergabe ebenfalls. + document.getElementById("rec-overlay").addEventListener("click", (ev) => { + if (ev.target === ev.currentTarget) closeRecordingOverlay(); + }); function createSshPlayer(entries) { const outputEntries = entries.filter((e) => e.dir === "output"); diff --git a/static/js/rdp.js b/static/js/rdp.js index cc692fa..47897e4 100644 --- a/static/js/rdp.js +++ b/static/js/rdp.js @@ -109,6 +109,8 @@ }); document.getElementById("exit-btn").addEventListener("click", () => { + statusEl.textContent = "Sitzung wird beendet ..."; + statusEl.classList.remove("error"); try { client.disconnect(); } catch (_) { /* bereits getrennt */ } window.close(); // Siehe terminal.js fuer die ausfuehrliche Begruendung: window.close() diff --git a/static/js/terminal.js b/static/js/terminal.js index fe0f666..30191d0 100644 --- a/static/js/terminal.js +++ b/static/js/terminal.js @@ -33,8 +33,24 @@ function b64encode(str) { return btoa(unescape(encodeURIComponent(str))); } - function b64decode(b64) { - return decodeURIComponent(escape(atob(b64))); + // Bugfix: NICHT ueber decodeURIComponent(escape(atob(...))) als JS-String + // dekodieren. Jedes "output"-Frame entspricht genau einem Chunk aus + // process.stdout.read() auf dem Server (app/ssh_proxy/terminal_ws.py) -- + // ein Mehrbyte-UTF-8-Zeichen (Umlaute, Box-Drawing-Zeichen, Emoji in + // Banner/Prompt/htop etc.) kann dabei GENAU AN DER CHUNK-GRENZE + // auseinandergerissen werden. decodeURIComponent() wirft dann einen + // "URI malformed"-Fehler fuer diesen einen Chunk (siehe + // static/js/admin.js::createSshPlayer fuer die noch schwerwiegendere + // Auswirkung bei der Wiedergabe -- dort blieb die gesamte Wiedergabe an + // genau dieser Stelle haengen). xterm.js' term.write() nimmt daher direkt + // die rohen Bytes als Uint8Array entgegen -- xterm.js ist ein echter + // Terminal-Emulator mit eigenem, chunk-uebergreifend korrektem + // UTF-8-Parser, genau fuer diesen Fall gebaut. + function b64decodeBytes(b64) { + const bin = atob(b64); + const bytes = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); + return bytes; } // Troubleshooting: der Server liefert bei einem Fehlschlag entweder eine @@ -76,7 +92,7 @@ ws.addEventListener("message", (ev) => { const msg = JSON.parse(ev.data); if (msg.type === "output") { - term.write(b64decode(msg.data)); + term.write(b64decodeBytes(msg.data)); } else if (msg.type === "error") { lastErrorMessage = msg.message; statusEl.textContent = "Fehler: " + msg.message; @@ -107,6 +123,28 @@ }); document.addEventListener("fullscreenchange", sendResize); + document.getElementById("exit-btn").addEventListener("click", () => { + // Sofortiges sichtbares Feedback, damit erkennbar ist, dass der Klick + // ueberhaupt ankam -- unabhaengig davon, ob window.close() gleich + // klappt oder der Fallback (siehe unten) erst nach der Wartezeit greift. + statusEl.textContent = "Sitzung wird beendet ..."; + statusEl.classList.remove("error"); + try { + if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) { + ws.close(1000, "Sitzung durch Benutzer beendet"); + } + } catch (_) { /* bereits getrennt */ } + window.close(); + // window.close() schliesst den Tab nur, wenn er per Skript/Link mit + // target="_blank" geoeffnet wurde (siehe dashboard.js) UND noch keine + // weitere Navigation darin stattfand -- sonst verweigern Browser das + // lautlos, ohne Fehler oder Rueckgabewert. Deshalb IMMER zusaetzlich + // nach kurzer Wartezeit zum Dashboard weiterleiten: war das Schliessen + // erfolgreich, existiert die Seite dann laengst nicht mehr und der + // Timeout laeuft ins Leere; sonst bleibt so ein Weg zurueck. + window.setTimeout(() => { window.location.href = "/dashboard"; }, 300); + }); + // --- Dateitransfer-Panel ------------------------------------------------ const ftOverlay = document.getElementById("ft-overlay"); @@ -123,6 +161,11 @@ } document.getElementById("filetransfer-btn").addEventListener("click", openFt); document.getElementById("ft-close-btn").addEventListener("click", closeFt); + // Klick auf den abgedunkelten Hintergrund (nicht auf das Panel selbst) + // schliesst das Overlay ebenfalls -- Standardverhalten fuer solche Panels. + ftOverlay.addEventListener("click", (ev) => { + if (ev.target === ftOverlay) closeFt(); + }); function formatSize(bytes) { if (bytes < 1024) return `${bytes} B`;