added pty terminal support to the client side
This commit is contained in:
285
xterm-pts.html
Normal file
285
xterm-pts.html
Normal file
@ -0,0 +1,285 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Terminal</title>
|
||||
<link rel="stylesheet" href="xterm.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#terminal-info-container {
|
||||
vertical-align: middle;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#terminal-target {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#terminal-status {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#terminal-errmsg {
|
||||
display: inline-block;
|
||||
color: red;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#terminal-control {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#terminal-control button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#terminal-view-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
/*height: 100%;*/
|
||||
}
|
||||
|
||||
#login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 99999;
|
||||
}
|
||||
#login-form-container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
text-align: center;
|
||||
}
|
||||
#login-form-title {
|
||||
line-height: 2em;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<script src="xterm.js"></script>
|
||||
<script src="xterm-addon-fit.js"></script>
|
||||
|
||||
<script>
|
||||
window.onload = function(event) {
|
||||
const terminal_container = document.getElementById('terminal-container');
|
||||
const terminal_target = document.getElementById('terminal-target');
|
||||
const terminal_status = document.getElementById('terminal-status');
|
||||
const terminal_errmsg = document.getElementById('terminal-errmsg');
|
||||
const terminal_view_container = document.getElementById('terminal-view-container');
|
||||
const terminal_connect = document.getElementById('terminal-connect');
|
||||
const terminal_disconnect = document.getElementById('terminal-disconnect');
|
||||
const login_container = document.getElementById('login-container');
|
||||
const login_form_title = document.getElementById('login-form-title');
|
||||
const login_form = document.getElementById('login-form');
|
||||
//const username_field = document.getElementById('username');
|
||||
//const password_field= document.getElementById('password');
|
||||
|
||||
const term = new window.Terminal({
|
||||
lineHeight: 1.2,
|
||||
//fontSize: 14,
|
||||
fontFamily: 'Ubuntu Mono, Consolas, SF Mono, courier-new, courier, monospace'
|
||||
});
|
||||
const fit_addon = new window.FitAddon.FitAddon();
|
||||
const text_decoder = new TextDecoder();
|
||||
|
||||
term.loadAddon(fit_addon)
|
||||
term.open(terminal_view_container);
|
||||
|
||||
let set_terminal_target = function(name) {
|
||||
terminal_target.innerText = name;
|
||||
login_form_title.innerText = name
|
||||
}
|
||||
|
||||
let set_terminal_status = function(msg, errmsg) {
|
||||
if (msg != null) terminal_status.innerText = msg;
|
||||
if (errmsg != null) terminal_errmsg.innerText = errmsg
|
||||
}
|
||||
|
||||
let adjust_terminal_size_unconnected = function() {
|
||||
fit_addon.fit();
|
||||
}
|
||||
|
||||
let fetch_session_info = async function() {
|
||||
let url = window.location.protocol + '//' + window.location.host;
|
||||
let pathname = window.location.pathname;
|
||||
pathname = pathname.replace(/\/$/, '');
|
||||
pathname = pathname.substring(0, pathname.lastIndexOf('/'));
|
||||
url += pathname + `/session-info`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url);
|
||||
if (!resp.ok) throw new Error(`HTTP error in getting session info - status ${resp.status}`);
|
||||
const route = await resp.json()
|
||||
if ('client-peer-name' in route) {
|
||||
set_terminal_target(route['client-peer-name']) // change to the name
|
||||
document.title = route['client-peer-name']
|
||||
}
|
||||
} catch (e) {
|
||||
set_terminal_target('');
|
||||
document.title = '';
|
||||
set_terminal_status (null, e);
|
||||
}
|
||||
}
|
||||
|
||||
let toggle_login_form = function(visible) {
|
||||
//if (visible) fetch_session_info();
|
||||
login_container.style.visibility = (visible? 'visible': 'hidden');
|
||||
terminal_disconnect.style.visibility = (visible? 'hidden': 'visible');
|
||||
//if (visible) username_field.focus();
|
||||
//else term.focus();
|
||||
if (visible) terminal_connect.focus();
|
||||
else term.focus();
|
||||
}
|
||||
|
||||
toggle_login_form(true);
|
||||
window.onresize = adjust_terminal_size_unconnected;
|
||||
adjust_terminal_size_unconnected()
|
||||
|
||||
login_form.onsubmit = async function(event) {
|
||||
event.preventDefault();
|
||||
toggle_login_form(false)
|
||||
|
||||
const username = "" //username_field.value.trim();
|
||||
const password = "" //password_field.value.trim();
|
||||
|
||||
const prefix = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
let pathname = window.location.pathname;
|
||||
pathname = pathname.replace(/\/$/, '');
|
||||
pathname = pathname.substring(0, pathname.lastIndexOf('/'));
|
||||
let url = prefix + window.location.host + pathname + `/ws`;
|
||||
|
||||
const socket = new WebSocket(url);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
set_terminal_status('Connecting...', '');
|
||||
|
||||
const adjust_terminal_size_connected = function() {
|
||||
fit_addon.fit();
|
||||
if (socket.readyState == 1) // if open
|
||||
socket.send(JSON.stringify({ type: 'size', data: [term.rows.toString(), term.cols.toString()] }));
|
||||
};
|
||||
|
||||
socket.onopen = function () {
|
||||
socket.send(JSON.stringify({ type: 'open', data: [username, password]}));
|
||||
};
|
||||
|
||||
socket.onmessage = function(event) {
|
||||
try {
|
||||
let event_text;
|
||||
event_text = (typeof event.data === 'string')? event.data: text_decoder.decode(new Uint8Array(event.data));
|
||||
const msg = JSON.parse(event_text);
|
||||
if (msg.type == "iov") {
|
||||
for (const data of msg.data) term.write(data);
|
||||
} else if (msg.type == "status") {
|
||||
if (msg.data.length >= 1) {
|
||||
if (msg.data[0] == 'opened') {
|
||||
set_terminal_status('Connected', '');
|
||||
adjust_terminal_size_connected()
|
||||
term.clear()
|
||||
} else if (msg.data[0] == 'closed') {
|
||||
// doesn't really matter
|
||||
// socket.onclose() will be executed anyway
|
||||
}
|
||||
}
|
||||
} else if (msg.type == "error") {
|
||||
toggle_login_form(true);
|
||||
window.onresize = adjust_terminal_size_unconnected;
|
||||
set_terminal_status(null, msg.data.join(' '))
|
||||
}
|
||||
} catch (e) {
|
||||
set_terminal_status('Disconnected', e);
|
||||
toggle_login_form(true)
|
||||
window.onresize = adjust_terminal_size_unconnected;
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = function(event) {
|
||||
set_terminal_status('Disconnected', event);
|
||||
toggle_login_form(true)
|
||||
window.onresize = adjust_terminal_size_unconnected;
|
||||
};
|
||||
|
||||
socket.onclose = function() {
|
||||
set_terminal_status('Disconnected', null);
|
||||
toggle_login_form(true)
|
||||
window.onresize = adjust_terminal_size_unconnected;
|
||||
};
|
||||
|
||||
term.onData(function(data) {
|
||||
if (socket.readyState == 1) // if open
|
||||
socket.send(JSON.stringify({ type: 'iov', data: [data] }));
|
||||
});
|
||||
|
||||
window.onresize = adjust_terminal_size_connected;
|
||||
terminal_disconnect.onclick = function(event) {
|
||||
socket.send(JSON.stringify({ type: 'close', data: [""] }));
|
||||
//socket.close()
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="login-container">
|
||||
<div id="login-form-container">
|
||||
<div id="login-form-title"></div>
|
||||
<form id="login-form">
|
||||
<!-- <label>
|
||||
Username: <input type="text" id="username" required />
|
||||
</label>
|
||||
<br /><br />
|
||||
<label>
|
||||
Password: <input type="password" id="password" required />
|
||||
</label> -->
|
||||
Click Connect below to start your secure terminal session
|
||||
<br /><br />
|
||||
<button type="submit" id="terminal-connect">Connect</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="terminal-container">
|
||||
<div id="terminal-info-container">
|
||||
<div id="terminal-target"></div>
|
||||
<div id="terminal-status"></div>
|
||||
<div id="terminal-errmsg"></div>
|
||||
<div id="terminal-control">
|
||||
<button id="terminal-disconnect" type="button">Disconnect</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="terminal-view-container"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user