From 8152a827e6a893d8d55ddd8524e8ba1dd2ad48b9 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 7 Jan 2026 22:45:40 -0500 Subject: [PATCH] Phase 3: Quick command enhancements with templates and history Changes: - Added command templates modal with 12 common system commands - Added command history tracking (stored in localStorage) - History saves last 50 commands with timestamp and worker name - Template categories: system info, disk/memory, network, Docker, logs - Click templates to auto-fill command field - Click history items to reuse previous commands - Terminal-themed modals with green/amber styling - History persists across browser sessions Templates included: - System: uname, uptime, CPU info, processes - Resources: df -h, free -h, memory usage - Network: ip addr, active connections - Docker: container list - Logs: syslog tail, who is logged in, last logins Co-Authored-By: Claude Sonnet 4.5 --- public/index.html | 137 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 15 deletions(-) diff --git a/public/index.html b/public/index.html index 27838a5..9b5fe65 100644 --- a/public/index.html +++ b/public/index.html @@ -759,18 +759,23 @@

⚡ Quick Command Execution

-

Execute a command on selected workers instantly

- +

Execute a command on selected workers instantly

+ +
+ + +
+ - + - - - + + +
@@ -803,7 +808,25 @@ + + + + + + + @@ -1167,6 +1190,82 @@ } } + // Command Templates + const commandTemplates = [ + { name: 'System Info', cmd: 'uname -a', desc: 'Show system information' }, + { name: 'Uptime', cmd: 'uptime', desc: 'Show system uptime and load' }, + { name: 'Disk Usage', cmd: 'df -h', desc: 'Show disk space usage' }, + { name: 'Memory Usage', cmd: 'free -h', desc: 'Show memory usage' }, + { name: 'CPU Info', cmd: 'lscpu', desc: 'Show CPU information' }, + { name: 'Running Processes', cmd: 'ps aux --sort=-%mem | head -20', desc: 'Top 20 processes by memory' }, + { name: 'Network Interfaces', cmd: 'ip addr show', desc: 'Show network interfaces' }, + { name: 'Active Connections', cmd: 'ss -tunap', desc: 'Show active network connections' }, + { name: 'Docker Containers', cmd: 'docker ps -a', desc: 'List all Docker containers' }, + { name: 'System Log Tail', cmd: 'tail -n 50 /var/log/syslog', desc: 'Last 50 lines of system log' }, + { name: 'Who is Logged In', cmd: 'w', desc: 'Show logged in users' }, + { name: 'Last Logins', cmd: 'last -n 20', desc: 'Show last 20 logins' } + ]; + + function showCommandTemplates() { + const html = commandTemplates.map((template, index) => ` +
+
${template.name}
+
${escapeHtml(template.cmd)}
+
${template.desc}
+
+ `).join(''); + + document.getElementById('templateList').innerHTML = html; + document.getElementById('commandTemplatesModal').classList.add('show'); + } + + function useTemplate(index) { + document.getElementById('quickCommand').value = commandTemplates[index].cmd; + closeModal('commandTemplatesModal'); + } + + function showCommandHistory() { + const history = JSON.parse(localStorage.getItem('commandHistory') || '[]'); + + if (history.length === 0) { + document.getElementById('historyList').innerHTML = '
No command history yet
'; + } else { + const html = history.map((item, index) => ` +
+
${escapeHtml(item.command)}
+
${new Date(item.timestamp).toLocaleString()} - ${item.worker}
+
+ `).join(''); + document.getElementById('historyList').innerHTML = html; + } + + document.getElementById('commandHistoryModal').classList.add('show'); + } + + function useHistoryCommand(index) { + const history = JSON.parse(localStorage.getItem('commandHistory') || '[]'); + document.getElementById('quickCommand').value = history[index].command; + closeModal('commandHistoryModal'); + } + + function addToCommandHistory(command, workerName) { + const history = JSON.parse(localStorage.getItem('commandHistory') || '[]'); + + // Add to beginning, limit to 50 items + history.unshift({ + command: command, + worker: workerName, + timestamp: new Date().toISOString() + }); + + // Keep only last 50 commands + if (history.length > 50) { + history.splice(50); + } + + localStorage.setItem('commandHistory', JSON.stringify(history)); + } + async function deleteWorker(workerId, name) { if (!confirm(`Delete worker: ${name}?`)) return; @@ -1248,31 +1347,39 @@ async function executeQuickCommand() { const workerId = document.getElementById('quickWorkerSelect').value; const command = document.getElementById('quickCommand').value; - + if (!workerId || !command) { alert('Please select a worker and enter a command'); return; } - + + // Find worker name for history + const worker = workers.find(w => w.id === workerId); + const workerName = worker ? worker.name : 'Unknown'; + const resultDiv = document.getElementById('quickCommandResult'); resultDiv.innerHTML = '
Executing command...
'; - + try { const response = await fetch(`/api/workers/${workerId}/command`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ command }) }); - + if (response.ok) { const data = await response.json(); + + // Add to command history + addToCommandHistory(command, workerName); + resultDiv.innerHTML = ` -
- ✓ Command sent successfully! -
+
+ ✓ Command sent successfully! +
Execution ID: ${data.execution_id}
-
+
Check the Executions tab to see the results