Files

84 lines
3.3 KiB
HTML
Raw Permalink Normal View History

{% extends "base.html" %}
{% block title %}Terminal{% endblock %}
{% block content %}
<h1>[$] SSH Terminal</h1>
<div class="card">
<div class="card-title">Remote Shell (root@VPS)</div>
<div class="output" id="output" style="min-height:400px;max-height:700px"></div>
<div style="display:flex;margin-top:5px">
<span style="color:#00ff41;padding:6px">$</span>
<input type="text" id="cmd-input" placeholder="enter command..." style="flex:1"
onkeydown="if(event.key==='Enter')runCmd()"
autofocus>
<button class="btn" onclick="runCmd()">Run</button>
</div>
</div>
<div class="card">
<div class="card-title">Quick Commands</div>
<div class="toolbar">
<button class="btn" onclick="quickCmd('uptime')">uptime</button>
<button class="btn" onclick="quickCmd('free -h')">memory</button>
<button class="btn" onclick="quickCmd('df -h /')">disk</button>
<button class="btn" onclick="quickCmd('docker ps')">docker ps</button>
<button class="btn" onclick="quickCmd('systemctl status nginx')">nginx status</button>
<button class="btn" onclick="quickCmd('ufw status')">firewall</button>
<button class="btn" onclick="quickCmd('tail -20 /var/log/syslog')">syslog</button>
<button class="btn" onclick="quickCmd('last -10')">last logins</button>
<button class="btn" onclick="quickCmd('netstat -tlnp')">open ports</button>
<button class="btn" onclick="quickCmd('gitlab-ctl status')">gitlab status</button>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
const history = [];
let histIdx = -1;
const cmdInput = document.getElementById('cmd-input');
const output = document.getElementById('output');
cmdInput.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
e.preventDefault();
if (histIdx < history.length - 1) { histIdx++; cmdInput.value = history[histIdx]; }
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (histIdx > 0) { histIdx--; cmdInput.value = history[histIdx]; }
else { histIdx = -1; cmdInput.value = ''; }
}
});
async function runCmd() {
const cmd = cmdInput.value.trim();
if (!cmd) return;
history.unshift(cmd);
histIdx = -1;
cmdInput.value = '';
output.innerHTML += '<span style="color:#888">$ ' + escHtml(cmd) + '</span>\n';
output.innerHTML += '<span class="info">running...</span>\n';
output.scrollTop = output.scrollHeight;
const res = await apiPost('/api/terminal/exec', {cmd});
// Remove the "running..." line
output.innerHTML = output.innerHTML.replace(/<span class="info">running\.\.\.<\/span>\n$/, '');
if (res.ok && res.data) {
if (res.data.stdout) output.innerHTML += escHtml(res.data.stdout);
if (res.data.stderr) output.innerHTML += '<span class="err">' + escHtml(res.data.stderr) + '</span>';
if (res.data.exit_code && res.data.exit_code !== 0)
output.innerHTML += '<span class="err">[exit: ' + res.data.exit_code + ']</span>\n';
} else {
output.innerHTML += '<span class="err">Error: ' + (res.error || 'unknown') + '</span>\n';
}
output.innerHTML += '\n';
output.scrollTop = output.scrollHeight;
}
function quickCmd(cmd) {
cmdInput.value = cmd;
runCmd();
}
</script>
{% endblock %}