Add abort execution feature for stuck/running processes
This commit is contained in:
@@ -1780,6 +1780,11 @@
|
||||
// Add action buttons
|
||||
html += '<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid var(--terminal-green); display: flex; gap: 10px;">';
|
||||
|
||||
// Abort button (only for running executions)
|
||||
if (execution.status === 'running') {
|
||||
html += `<button onclick="abortExecution('${executionId}')" style="background-color: var(--terminal-red); border-color: var(--terminal-red);">[ ⛔ Abort Execution ]</button>`;
|
||||
}
|
||||
|
||||
// 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 `
|
||||
<div class="log-entry" style="border-left-color: var(--terminal-red);">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title" style="color: var(--terminal-red);">⛔ Execution Aborted</div>
|
||||
<div class="log-details">
|
||||
<div class="log-field"><span class="log-label">Aborted by:</span> ${escapeHtml(log.aborted_by)}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Fallback for unknown log types
|
||||
return `<div class="log-entry"><pre>${JSON.stringify(log, null, 2)}</pre></div>`;
|
||||
}
|
||||
@@ -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}`);
|
||||
|
||||
34
server.js
34
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 {
|
||||
|
||||
Reference in New Issue
Block a user