91 lines
3.6 KiB
HTML
91 lines
3.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>cmd</title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html, body { height: 100%; background: #0f1117; color: #e0e0e0; font-family: monospace; }
|
|
body { padding: 12px; display: flex; flex-direction: column; gap: 8px; }
|
|
.cmd-row { display: flex; gap: 8px; align-items: flex-start; }
|
|
.cmd-text { flex: 1; background: #1a1d27; color: #7ec8e3; border: 1px solid #2a2d37;
|
|
border-radius: 6px; padding: 10px 12px; font-size: 0.78rem; font-family: monospace;
|
|
white-space: pre-wrap; word-break: break-all; cursor: default; user-select: text; }
|
|
.cmd-copy { padding: 6px 10px; background: #2a2d37; color: #aaa; border: 1px solid #3a3d47;
|
|
border-radius: 4px; font-family: monospace; font-size: 0.75rem; cursor: pointer;
|
|
white-space: nowrap; flex-shrink: 0; }
|
|
.cmd-copy:hover { background: #3a3d47; color: #e0e0e0; }
|
|
.cmd-copy.ok { color: #22c55e; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="status-bar" style="font-size:0.7rem;color:#444;margin-bottom:2px">refreshing in 5s</div>
|
|
<div id="loaded-ts" style="font-size:0.68rem;color:#555;margin-bottom:6px">loaded: —</div>
|
|
<div id="commands"></div>
|
|
<script>
|
|
/* COMMANDS_START */
|
|
var _ts = "";
|
|
var _cmds = [
|
|
"sudo curl -s -u \"admin:Git.jhestyr.net123\" \"https://git.jhestyr.net/api/v1/repos/admin/istrain/raw/sdr/software/analyzer/server.js?ref=e844af2\" -o /mnt/tank/apps/istrain-analyzer/server.js && sudo docker restart istrain-analyzer-1 && echo \"analyzer ok\"",
|
|
"echo \"[]\" | sudo tee /mnt/tank/apps/istrain/data/train_events.json && echo \"cleared\""
|
|
];
|
|
/* COMMANDS_END */
|
|
function render(cmds) {
|
|
const el = document.getElementById('commands');
|
|
el.innerHTML = '';
|
|
cmds.forEach(cmd => {
|
|
const row = document.createElement('div');
|
|
row.className = 'cmd-row';
|
|
const text = document.createElement('div');
|
|
text.className = 'cmd-text';
|
|
text.textContent = cmd;
|
|
const btn = document.createElement('button');
|
|
btn.className = 'cmd-copy';
|
|
btn.textContent = 'copy';
|
|
btn.onclick = () => {
|
|
navigator.clipboard.writeText(cmd).then(() => {
|
|
btn.textContent = 'ok'; btn.classList.add('ok');
|
|
setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('ok'); }, 1500);
|
|
});
|
|
};
|
|
row.appendChild(text);
|
|
row.appendChild(btn);
|
|
el.appendChild(row);
|
|
});
|
|
}
|
|
function timeAgo(tsStr) {
|
|
if (!tsStr) return '';
|
|
const d = new Date(tsStr.replace(' ', 'T'));
|
|
if (isNaN(d)) return '';
|
|
const secs = Math.floor((Date.now() - d) / 1000);
|
|
if (secs < 60) return secs + 's ago';
|
|
if (secs < 3600) return Math.floor(secs / 60) + 'm ago';
|
|
return Math.floor(secs / 3600) + 'h ' + Math.floor((secs % 3600) / 60) + 'm ago';
|
|
}
|
|
function updateTs(ts) {
|
|
const ago = timeAgo(ts);
|
|
document.getElementById('loaded-ts').textContent = 'loaded: ' + (ts || '—') + (ago ? ' · ' + ago : '');
|
|
}
|
|
render(_cmds);
|
|
updateTs(_ts);
|
|
let countdown = 5;
|
|
let _currentTs = _ts;
|
|
const bar = document.getElementById('status-bar');
|
|
setInterval(() => {
|
|
countdown--;
|
|
updateTs(_currentTs);
|
|
bar.textContent = countdown > 0 ? 'refreshing in ' + countdown + 's' : 'refreshing...';
|
|
if (countdown <= 0) {
|
|
countdown = 5;
|
|
fetch(location.href + '?t=' + Date.now()).then(r => r.text()).then(html => {
|
|
const m = html.match(/\/\* COMMANDS_START \*\/[\s\S]*?var _cmds = (\[[\s\S]*?\]);\s*\/\* COMMANDS_END \*\//);
|
|
const tm = html.match(/var _ts = "([^"]*)";/);
|
|
if (m) render(JSON.parse(m[1]));
|
|
if (tm) { _currentTs = tm[1]; updateTs(_currentTs); }
|
|
}).catch(() => {});
|
|
}
|
|
}, 1000);
|
|
</script>
|
|
</body>
|
|
</html>
|