added live windows
This commit is contained in:
parent
feba1ca13f
commit
58a862c98d
|
|
@ -153,7 +153,7 @@
|
|||
<h2>Chat Test</h2>
|
||||
<div class="chat-box" id="chat-box"></div>
|
||||
<div class="input-row">
|
||||
<input type="text" id="chat-input" value="aria lebst du noch?" placeholder="Nachricht...">
|
||||
<input type="text" id="chat-input" placeholder="Nachricht an ARIA...">
|
||||
<button class="btn" id="btn-gw" onclick="testGateway()">Gateway senden</button>
|
||||
<button class="btn" id="btn-rvs" onclick="testRVS()">Via RVS senden</button>
|
||||
</div>
|
||||
|
|
@ -193,6 +193,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ARIA Live-Ansicht -->
|
||||
<div class="card" style="margin-top:12px; padding: 8px 0 0 0;">
|
||||
<div style="padding: 0 12px;">
|
||||
<div class="tab-bar">
|
||||
<button class="tab-btn active" id="live-tab-ssh" onclick="switchLiveTab('ssh')">SSH Terminal</button>
|
||||
<button class="tab-btn" id="live-tab-desktop" onclick="switchLiveTab('desktop')">Desktop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:#080810; border:1px solid #1E1E2E; border-radius:0 0 6px 6px; position:relative;">
|
||||
<!-- SSH Terminal -->
|
||||
<div id="live-ssh" style="height:350px; padding:4px;">
|
||||
<div id="live-ssh-bar" style="display:flex;gap:6px;align-items:center;padding:4px 4px 6px;">
|
||||
<button class="btn" onclick="startLiveSSH()" id="btn-live-ssh" style="padding:4px 12px;font-size:11px;">Verbinden</button>
|
||||
<span id="live-ssh-status" style="font-size:11px;color:#8888AA;">Nicht verbunden</span>
|
||||
</div>
|
||||
<div id="live-ssh-term" style="height:calc(100% - 32px);"></div>
|
||||
</div>
|
||||
<!-- Desktop Viewer -->
|
||||
<div id="live-desktop" style="height:350px; display:none; position:relative;">
|
||||
<div id="desktop-placeholder" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#555570;">
|
||||
<div style="font-size:36px;margin-bottom:12px;">🖥</div>
|
||||
<div style="font-size:13px;">Kein Desktop verfuegbar</div>
|
||||
<div style="font-size:11px;margin-top:4px;">ARIA muss zuerst eine Desktop-Umgebung installieren</div>
|
||||
<button class="btn secondary" onclick="checkDesktop()" style="margin-top:12px;padding:4px 12px;font-size:11px;">Desktop pruefen</button>
|
||||
</div>
|
||||
<iframe id="desktop-vnc" style="width:100%;height:100%;border:none;display:none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terminal Modal -->
|
||||
<div class="modal-overlay" id="term-modal">
|
||||
<div class="modal-box">
|
||||
|
|
@ -347,6 +377,10 @@
|
|||
el.textContent = msg.error ? `Fehler: ${msg.error}` : msg.info;
|
||||
return;
|
||||
}
|
||||
// Live SSH + Desktop
|
||||
if (msg.type?.startsWith('live_ssh_')) { handleLiveSSH(msg); return; }
|
||||
if (msg.type === 'desktop_status') { handleDesktop(msg); return; }
|
||||
|
||||
if (msg.type === 'term_ready') {
|
||||
document.getElementById('term-status').textContent = 'Verbunden — interaktives Terminal';
|
||||
if (term) term.writeln('\x1b[32mVerbunden!\x1b[0m\r\n');
|
||||
|
|
@ -646,6 +680,120 @@
|
|||
if (e.key === 'Enter') testGateway();
|
||||
});
|
||||
|
||||
// ── ARIA Live-Ansicht (SSH + Desktop) ──────────────────
|
||||
|
||||
let liveSshTerm = null;
|
||||
let liveSshFit = null;
|
||||
|
||||
function switchLiveTab(tab) {
|
||||
document.getElementById('live-ssh').style.display = tab === 'ssh' ? 'block' : 'none';
|
||||
document.getElementById('live-desktop').style.display = tab === 'desktop' ? 'block' : 'none';
|
||||
document.getElementById('live-tab-ssh').className = 'tab-btn' + (tab === 'ssh' ? ' active' : '');
|
||||
document.getElementById('live-tab-desktop').className = 'tab-btn' + (tab === 'desktop' ? ' active' : '');
|
||||
if (tab === 'ssh' && liveSshTerm && liveSshFit) {
|
||||
setTimeout(() => liveSshFit.fit(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
function startLiveSSH() {
|
||||
const statusEl = document.getElementById('live-ssh-status');
|
||||
const btn = document.getElementById('btn-live-ssh');
|
||||
|
||||
// Wenn schon verbunden, trennen
|
||||
if (liveSshTerm && liveSshTerm._sshConnected) {
|
||||
send({ action: 'live_ssh_close' });
|
||||
statusEl.textContent = 'Getrennt';
|
||||
statusEl.style.color = '#FF6B6B';
|
||||
btn.textContent = 'Verbinden';
|
||||
liveSshTerm._sshConnected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
statusEl.textContent = 'Verbinde...';
|
||||
statusEl.style.color = '#FFD60A';
|
||||
|
||||
function initSSHTerm() {
|
||||
const container = document.getElementById('live-ssh-term');
|
||||
if (!liveSshTerm) {
|
||||
liveSshTerm = new Terminal({
|
||||
theme: { background: '#080810', foreground: '#E0E0F0', cursor: '#0096FF' },
|
||||
fontFamily: 'Courier New, monospace',
|
||||
fontSize: 12,
|
||||
cursorBlink: true,
|
||||
});
|
||||
liveSshFit = new FitAddon.FitAddon();
|
||||
liveSshTerm.loadAddon(liveSshFit);
|
||||
liveSshTerm.open(container);
|
||||
liveSshFit.fit();
|
||||
liveSshTerm.onData((data) => {
|
||||
send({ action: 'live_ssh_input', data });
|
||||
});
|
||||
}
|
||||
liveSshTerm.clear();
|
||||
send({ action: 'live_ssh_start' });
|
||||
}
|
||||
|
||||
if (typeof Terminal === 'undefined') {
|
||||
const s = document.createElement('script');
|
||||
s.src = 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js';
|
||||
s.onload = () => {
|
||||
const s2 = document.createElement('script');
|
||||
s2.src = 'https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.min.js';
|
||||
s2.onload = () => initSSHTerm();
|
||||
document.head.appendChild(s2);
|
||||
};
|
||||
document.head.appendChild(s);
|
||||
} else {
|
||||
initSSHTerm();
|
||||
}
|
||||
}
|
||||
|
||||
function handleLiveSSH(msg) {
|
||||
const statusEl = document.getElementById('live-ssh-status');
|
||||
const btn = document.getElementById('btn-live-ssh');
|
||||
if (msg.type === 'live_ssh_data' && liveSshTerm) {
|
||||
const raw = atob(msg.data);
|
||||
const bytes = new Uint8Array(raw.length);
|
||||
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
|
||||
liveSshTerm.write(bytes);
|
||||
} else if (msg.type === 'live_ssh_connected') {
|
||||
statusEl.textContent = 'Verbunden mit aria-wohnung';
|
||||
statusEl.style.color = '#34C759';
|
||||
btn.textContent = 'Trennen';
|
||||
if (liveSshTerm) liveSshTerm._sshConnected = true;
|
||||
} else if (msg.type === 'live_ssh_error') {
|
||||
statusEl.textContent = msg.error || 'Fehler';
|
||||
statusEl.style.color = '#FF6B6B';
|
||||
btn.textContent = 'Verbinden';
|
||||
if (liveSshTerm) liveSshTerm._sshConnected = false;
|
||||
} else if (msg.type === 'live_ssh_closed') {
|
||||
statusEl.textContent = 'Getrennt';
|
||||
statusEl.style.color = '#8888AA';
|
||||
btn.textContent = 'Verbinden';
|
||||
if (liveSshTerm) liveSshTerm._sshConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
function checkDesktop() {
|
||||
send({ action: 'check_desktop' });
|
||||
}
|
||||
|
||||
function handleDesktop(msg) {
|
||||
if (msg.type === 'desktop_status') {
|
||||
const placeholder = document.getElementById('desktop-placeholder');
|
||||
const vnc = document.getElementById('desktop-vnc');
|
||||
if (msg.available && msg.url) {
|
||||
placeholder.style.display = 'none';
|
||||
vnc.style.display = 'block';
|
||||
vnc.src = msg.url;
|
||||
} else {
|
||||
placeholder.style.display = 'flex';
|
||||
vnc.style.display = 'none';
|
||||
placeholder.querySelector('div:nth-child(2)').textContent = msg.message || 'Kein Desktop verfuegbar';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connectWS();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -962,6 +962,14 @@ wss.on("connection", (ws) => {
|
|||
writeProxyCredentials(msg.credentials);
|
||||
} else if (msg.action === "docker_logs") {
|
||||
handleDockerLogs(ws, msg.tab, msg.tail);
|
||||
} else if (msg.action === "live_ssh_start") {
|
||||
startLiveSSH(ws);
|
||||
} else if (msg.action === "live_ssh_input") {
|
||||
if (ws._sshSock) ws._sshSock.write(msg.data);
|
||||
} else if (msg.action === "live_ssh_close") {
|
||||
if (ws._sshSock) { ws._sshSock.end(); ws._sshSock = null; }
|
||||
} else if (msg.action === "check_desktop") {
|
||||
checkDesktopAvailable(ws);
|
||||
}
|
||||
} catch {}
|
||||
});
|
||||
|
|
@ -971,6 +979,121 @@ wss.on("connection", (ws) => {
|
|||
});
|
||||
});
|
||||
|
||||
// ── Live SSH ─────────────────────────────────────────────
|
||||
|
||||
function startLiveSSH(clientWs) {
|
||||
// Bestehende Session schliessen
|
||||
if (clientWs._sshSock) {
|
||||
try { clientWs._sshSock.end(); } catch (_) {}
|
||||
clientWs._sshSock = null;
|
||||
}
|
||||
|
||||
const createBody = JSON.stringify({
|
||||
AttachStdin: true, AttachStdout: true, AttachStderr: true, Tty: true,
|
||||
Cmd: ["ssh", "-tt", "aria-wohnung"],
|
||||
});
|
||||
|
||||
const createReq = http.request({
|
||||
socketPath: "/var/run/docker.sock",
|
||||
path: "/containers/aria-core/exec",
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(createBody) },
|
||||
}, (res) => {
|
||||
let data = "";
|
||||
res.on("data", (c) => data += c);
|
||||
res.on("end", () => {
|
||||
if (res.statusCode !== 201) {
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_error", error: `Exec create failed: HTTP ${res.statusCode}` }));
|
||||
return;
|
||||
}
|
||||
const execId = JSON.parse(data).Id;
|
||||
const startBody = JSON.stringify({ Detach: false, Tty: true });
|
||||
|
||||
const sock = net.connect({ path: "/var/run/docker.sock" }, () => {
|
||||
const req = `POST /exec/${execId}/start HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nConnection: Upgrade\r\nUpgrade: tcp\r\nContent-Length: ${Buffer.byteLength(startBody)}\r\n\r\n${startBody}`;
|
||||
sock.write(req);
|
||||
});
|
||||
|
||||
let headersParsed = false;
|
||||
let headerBuf = "";
|
||||
|
||||
sock.on("data", (chunk) => {
|
||||
if (!headersParsed) {
|
||||
headerBuf += chunk.toString("utf-8");
|
||||
const headerEnd = headerBuf.indexOf("\r\n\r\n");
|
||||
if (headerEnd === -1) return;
|
||||
const headers = headerBuf.slice(0, headerEnd);
|
||||
const remaining = chunk.slice(chunk.length - (headerBuf.length - headerEnd - 4));
|
||||
headersParsed = true;
|
||||
|
||||
if (!headers.includes("200") && !headers.includes("101")) {
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_error", error: `SSH start failed` }));
|
||||
sock.end();
|
||||
return;
|
||||
}
|
||||
|
||||
log("info", "server", "Live SSH-Session gestartet");
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_connected" }));
|
||||
clientWs._sshSock = sock;
|
||||
|
||||
if (remaining.length > 0) {
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_data", data: remaining.toString("base64") }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Stream-Daten ans Frontend
|
||||
try {
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_data", data: chunk.toString("base64") }));
|
||||
} catch (_) { sock.end(); }
|
||||
});
|
||||
|
||||
sock.on("close", () => {
|
||||
clientWs._sshSock = null;
|
||||
try { clientWs.send(JSON.stringify({ type: "live_ssh_closed" })); } catch (_) {}
|
||||
log("info", "server", "Live SSH-Session beendet");
|
||||
});
|
||||
|
||||
sock.on("error", (err) => {
|
||||
clientWs._sshSock = null;
|
||||
try { clientWs.send(JSON.stringify({ type: "live_ssh_error", error: err.message })); } catch (_) {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
createReq.on("error", (err) => {
|
||||
clientWs.send(JSON.stringify({ type: "live_ssh_error", error: `Docker: ${err.message}` }));
|
||||
});
|
||||
createReq.end(createBody);
|
||||
}
|
||||
|
||||
function checkDesktopAvailable(clientWs) {
|
||||
// Pruefen ob VNC auf der VM laeuft (Port 5900/5901)
|
||||
const checkSock = net.connect({ host: "host.docker.internal", port: 5901 }, () => {
|
||||
checkSock.end();
|
||||
clientWs.send(JSON.stringify({
|
||||
type: "desktop_status",
|
||||
available: true,
|
||||
url: `http://${clientWs._socket?.remoteAddress || "localhost"}:6080/vnc.html?autoconnect=true`,
|
||||
message: "VNC Desktop verfuegbar",
|
||||
}));
|
||||
});
|
||||
checkSock.on("error", () => {
|
||||
clientWs.send(JSON.stringify({
|
||||
type: "desktop_status",
|
||||
available: false,
|
||||
message: "Kein VNC-Server auf aria-wohnung gefunden (Port 5901)",
|
||||
}));
|
||||
});
|
||||
checkSock.setTimeout(3000, () => {
|
||||
checkSock.destroy();
|
||||
clientWs.send(JSON.stringify({
|
||||
type: "desktop_status",
|
||||
available: false,
|
||||
message: "VNC-Server nicht erreichbar (Timeout)",
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// ── Start ───────────────────────────────────────────────
|
||||
|
||||
server.listen(HTTP_PORT, "0.0.0.0", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue