mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
The xterm console never connected: socketiod expected a different token format, its worker had no compute credentials, one global console was shared by every client and its output broadcast to all of them, and the Docker image did not run socketiod at all. - console/tokens.py: one authorize() for noVNC and socketiod; the signed token is the only credential, checked at connect time. - socketiod: one pty worker per client (consolecallback with the instance id), one session per VM, a session cap, input and resize checked; the parent never opens libvirt. The engine.io same-origin check is on. - consolecallback: opens the console with the compute's credentials and exits with a message when it cannot. - Docker runs socketiod as a runit service; nginx proxies /socket.io/ with Host and the scheme for the origin check. Supervisor and systemd start it as well; WEBVIRTCLOUD_SERIAL_CONSOLE=1 enables it. - Tests: token and session unit tests, socketiod as a process, and a live test that reads SeaBIOS from a wvc-test VM on the TCP and SSH hosts.
100 lines
3.5 KiB
HTML
100 lines
3.5 KiB
HTML
{% extends "console-base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block head %}
|
|
<title>WebVirtCloud - XTerm</title>
|
|
<link rel="stylesheet" href="{% static 'css/xterm.css' %}"/>
|
|
<script src="{% static 'js/xterm/xterm.js' %}"></script>
|
|
<script src="{% static 'js/xterm/addon-fit.js' %}"></script>
|
|
|
|
<script src="{% static 'js/socket.io.js' %}"></script>
|
|
|
|
{% endblock %}
|
|
{% block content %}
|
|
<div style="background: white; padding-bottom: 5px;">
|
|
<span style="font-size: small;">Status: <span style="font-size: small;" id="status">connecting...</span></span>
|
|
<button id="button" type="button" onclick="myFunction();">Connect</button>
|
|
</div>
|
|
|
|
<div style="width: 100%; height:100%;" id="terminal"></div>
|
|
<script>
|
|
// socketiod is behind this page's own /socket.io/ location; the page's
|
|
// signed token is the credential (the cookie holds the last console's)
|
|
var socket = io({transports: ["websocket", "polling"], auth: {token: "{{ token|escapejs }}"}});
|
|
|
|
const status = document.getElementById("status");
|
|
const button = document.getElementById("button");
|
|
|
|
var term = new Terminal({
|
|
cursorBlink: true,
|
|
});
|
|
var fitAddon = new FitAddon.FitAddon();
|
|
term.loadAddon(fitAddon);
|
|
|
|
term.open(document.getElementById('terminal'));
|
|
|
|
// typed and pasted input
|
|
term.onData((data) => {
|
|
socket.emit("pty_input", {"input": data})
|
|
});
|
|
|
|
socket.on("pty_output", function(output){
|
|
term.write(output["output"])
|
|
})
|
|
|
|
function showStatus(text, color) {
|
|
status.textContent = text;
|
|
status.style.backgroundColor = color;
|
|
button.textContent = socket.connected ? "Disconnect" : "Connect";
|
|
}
|
|
socket.on("connect", () => showStatus("connected", "lightgreen"));
|
|
socket.on("disconnect", () => showStatus("disconnected", "#ff8383"));
|
|
// refused: serial console off, an expired token, or the VM's console already open
|
|
socket.on("connect_error", () => showStatus("refused", "#ff8383"));
|
|
|
|
function myFunction(){
|
|
if (socket.connected) {
|
|
socket.disconnect();
|
|
} else {
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
function resize(){
|
|
fitAddon.fit()
|
|
socket.emit("resize", {"cols": term.cols, "rows": term.rows})
|
|
}
|
|
|
|
function bindTerminalKey(id, str) {
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
el.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
socket.emit("pty_input", {"input": str});
|
|
});
|
|
}
|
|
}
|
|
bindTerminalKey('ctrlc', '\x03');
|
|
bindTerminalKey('ctrld', '\x04');
|
|
bindTerminalKey('ctrlz', '\x1a');
|
|
bindTerminalKey('ctrlbackslash', '\x1c');
|
|
bindTerminalKey('sendtab', '\t');
|
|
bindTerminalKey('sendesc', '\x1b');
|
|
|
|
const fsBtn = document.getElementById('fullscreen_button');
|
|
if (fsBtn) {
|
|
fsBtn.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
if (document.fullscreenElement) {
|
|
document.exitFullscreen().catch(() => {});
|
|
} else {
|
|
document.documentElement.requestFullscreen().catch(() => {});
|
|
}
|
|
});
|
|
}
|
|
|
|
window.onresize = resize;
|
|
window.onload = resize;
|
|
</script>
|
|
{% endblock %}
|