Add abort execution feature for stuck/running processes
This commit is contained in:
@@ -1780,6 +1780,11 @@
|
|||||||
// Add action buttons
|
// Add action buttons
|
||||||
html += '<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid var(--terminal-green); display: flex; gap: 10px;">';
|
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)
|
// Re-run button (only for quick commands with command in logs)
|
||||||
const commandLog = execution.logs?.find(l => l.action === 'command_sent');
|
const commandLog = execution.logs?.find(l => l.action === 'command_sent');
|
||||||
if (commandLog && commandLog.command) {
|
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
|
// Fallback for unknown log types
|
||||||
return `<div class="log-entry"><pre>${JSON.stringify(log, null, 2)}</pre></div>`;
|
return `<div class="log-entry"><pre>${JSON.stringify(log, null, 2)}</pre></div>`;
|
||||||
}
|
}
|
||||||
@@ -1952,6 +1969,28 @@
|
|||||||
document.getElementById('quickCommand').scrollIntoView({ behavior: 'smooth' });
|
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) {
|
async function downloadExecutionLogs(executionId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/executions/${executionId}`);
|
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
|
// Scheduled Commands API
|
||||||
app.get('/api/scheduled-commands', authenticateSSO, async (req, res) => {
|
app.get('/api/scheduled-commands', authenticateSSO, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user