diff --git a/public/index.html b/public/index.html index 6922209..c4e19f7 100644 --- a/public/index.html +++ b/public/index.html @@ -1780,6 +1780,11 @@ // Add action buttons html += '
'; + // Abort button (only for running executions) + if (execution.status === 'running') { + html += ``; + } + // Re-run button (only for quick commands with command in logs) const commandLog = execution.logs?.find(l => l.action === 'command_sent'); if (commandLog && commandLog.command) { @@ -1899,6 +1904,18 @@ `; } + if (log.action === 'execution_aborted') { + return ` +
+
[${timestamp}]
+
⛔ Execution Aborted
+
+
Aborted by: ${escapeHtml(log.aborted_by)}
+
+
+ `; + } + // Fallback for unknown log types return `
${JSON.stringify(log, null, 2)}
`; } @@ -1952,6 +1969,28 @@ document.getElementById('quickCommand').scrollIntoView({ behavior: 'smooth' }); } + async function abortExecution(executionId) { + if (!confirm('Are you sure you want to abort this execution? This will mark it as failed.')) return; + + try { + const response = await fetch(`/api/executions/${executionId}/abort`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + if (response.ok) { + showTerminalNotification('Execution aborted', 'success'); + closeModal('viewExecutionModal'); + refreshData(); + } else { + alert('Failed to abort execution'); + } + } catch (error) { + console.error('Error aborting execution:', error); + alert('Error aborting execution'); + } + } + async function downloadExecutionLogs(executionId) { try { const response = await fetch(`/api/executions/${executionId}`); diff --git a/server.js b/server.js index 61e066a..0ac5d29 100644 --- a/server.js +++ b/server.js @@ -872,6 +872,40 @@ app.delete('/api/executions/:id', authenticateSSO, async (req, res) => { } }); +app.post('/api/executions/:id/abort', authenticateSSO, async (req, res) => { + try { + const executionId = req.params.id; + + // Check if execution exists and is running + const [execution] = await pool.query('SELECT status FROM executions WHERE id = ?', [executionId]); + + if (execution.length === 0) { + return res.status(404).json({ error: 'Execution not found' }); + } + + if (execution[0].status !== 'running') { + return res.status(400).json({ error: 'Execution is not running' }); + } + + // Add abort log entry + await addExecutionLog(executionId, { + action: 'execution_aborted', + aborted_by: req.user.username, + timestamp: new Date().toISOString() + }); + + // Update execution status to failed + await updateExecutionStatus(executionId, 'failed'); + + console.log(`[Execution] Execution ${executionId} aborted by ${req.user.username}`); + + res.json({ success: true }); + } catch (error) { + console.error('[Execution] Error aborting execution:', error); + res.status(500).json({ error: error.message }); + } +}); + // Scheduled Commands API app.get('/api/scheduled-commands', authenticateSSO, async (req, res) => { try {