158 lines
No EOL
4.4 KiB
HTML
158 lines
No EOL
4.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>${title}</title>
|
|
<link rel="stylesheet" type="text/css" href="/static/bootstrap/bootstrap.min.css">
|
|
<link rel="stylesheet" type="text/css" href="/static/bootstrap/bootstrap-icons.min.css">
|
|
<link rel="stylesheet" type="text/css" href="./static/css/style.css">
|
|
<link rel="shortcut icon" type="image/png" href="./static/favicon.png">
|
|
<link rel="shortcut icon" sizes="192x192" href="./static/favicon.png">
|
|
<link rel="apple-touch-icon" href="./static/favicon.png">
|
|
<style type="text/css">
|
|
#output {
|
|
height: 20em;
|
|
resize: vertical;
|
|
font-family: monospace;
|
|
}
|
|
#command {
|
|
font-family: monospace;
|
|
}
|
|
.status-ok {
|
|
color: var(--bs-success-text-emphasis);
|
|
}
|
|
.status-error {
|
|
color: var(--bs-danger-text-emphasis);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="wrapper" class="d-flex flex-column vh-100">
|
|
${menu}
|
|
|
|
<div class="container flex-grow-1 min-height-0 z-0">
|
|
<div class="row h-100">
|
|
<div class="p-3 mx-auto">
|
|
<select class="form-select mb-3" id="commandlist">
|
|
<option value="" disabled selected>Commands</option>
|
|
<option value="status">Status</option>
|
|
<option value="shutdown">Shut down</option>
|
|
</select>
|
|
<div id="output" class="w-100 border overflow-y-auto p-2 mb-3"></div>
|
|
<form method="post" action="/reporter/admin/api" id="commandform" class="mb-3" autocomplete="off">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="command" name="command">
|
|
<button type="submit" class="btn btn-primary"><i class="bi bi-send"></i></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/static/bootstrap/bootstrap.bundle.min.js"></script>
|
|
<script type="text/javascript">
|
|
const el = (id) => document.getElementById(id);
|
|
let theme = "light";
|
|
|
|
const form = el('commandform');
|
|
const output = el('output');
|
|
const commandEl = el('command');
|
|
const commandList = el('commandlist');
|
|
const html = document.documentElement;
|
|
let history = [];
|
|
let cursor = null;
|
|
let buffer = "";
|
|
|
|
const sendCommand = async () => {
|
|
const command = commandEl.value;
|
|
if (command == '') { return; }
|
|
if (command != history[0]) {
|
|
history.unshift(command);
|
|
}
|
|
output.innerHTML += `<pre class='mb-0'>> ${command}</pre>`;
|
|
const formData = new FormData(form);
|
|
commandEl.value = '';
|
|
cursor = null;
|
|
buffer = "";
|
|
|
|
const words = command.split(' ');
|
|
|
|
if (words[0] == "history") {
|
|
output.innerHTML += `<pre class="status-ok">${history.toReversed().join('\n')}</pre>`;
|
|
}
|
|
else if (words[0] == "theme") {
|
|
const themes = ["dark", "light"];
|
|
if (words.length == 1) {
|
|
output.innerHTML += `<pre class="status-ok">Using ${theme} theme.</pre>`;
|
|
}
|
|
else if (themes.includes(words[1])) {
|
|
html.setAttribute("data-bs-theme", words[1]);
|
|
theme = words[1];
|
|
output.innerHTML += `<pre class="status-ok">Theme set to ${words[1]}</pre>`;
|
|
}
|
|
else {
|
|
output.innerHTML += `<pre class="status-error">Theme ${words[1]} unsupported. Available: ${themes.join(", ")}</pre>`;
|
|
}
|
|
}
|
|
else {
|
|
try {
|
|
const response = await fetch(form.action, {
|
|
method: "POST",
|
|
body: formData
|
|
});
|
|
result = await response.json();
|
|
output.innerHTML += `<pre class="status-${result.status}">${result.data}\n[${result.timestamp}]</pre>`;
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
output.scrollTop = output.scrollHeight;
|
|
}
|
|
|
|
commandList.addEventListener("change", (event) => {
|
|
commandEl.value = commandList.value;
|
|
sendCommand();
|
|
commandList.options[0].selected = true;
|
|
});
|
|
|
|
form.addEventListener("submit", (event) => {
|
|
event.preventDefault();
|
|
sendCommand();
|
|
});
|
|
|
|
commandEl.addEventListener("keydown", (event) => {
|
|
if (event.keyCode == 38) { //up
|
|
event.preventDefault();
|
|
if (cursor == null && history.length > 0) {
|
|
cursor = 0;
|
|
buffer = commandEl.value;
|
|
commandEl.value = history[0];
|
|
}
|
|
else if (history.length > cursor+1) {
|
|
cursor++;
|
|
commandEl.value = history[cursor];
|
|
}
|
|
}
|
|
else if (event.keyCode == 40) { //down
|
|
event.preventDefault();
|
|
if (cursor == 0) {
|
|
cursor = null;
|
|
commandEl.value = buffer;
|
|
buffer = "";
|
|
}
|
|
else if (cursor > 0) {
|
|
cursor--;
|
|
commandEl.value = history[cursor];
|
|
}
|
|
}
|
|
});
|
|
|
|
commandEl.focus();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |