Phase 5: Auto-cleanup and pagination for executions
Changes: Server-side: - Added automatic cleanup of old executions (runs daily) - Configurable retention period via EXECUTION_RETENTION_DAYS env var (default: 30 days) - Cleanup runs on server startup and every 24 hours - Only cleans completed/failed executions, keeps running ones - Added pagination support to /api/executions endpoint - Returns total count, limit, offset, and hasMore flag Client-side: - Implemented "Load More" button for execution pagination - Loads 50 executions at a time - Appends additional executions when "Load More" clicked - Shows total execution count info - Backward compatible with old API format Benefits: - Automatic database maintenance - Prevents execution table from growing indefinitely - Better performance with large execution histories - User can browse all executions via pagination - Configurable retention policy per deployment Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -969,22 +969,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadExecutions() {
|
||||
try {
|
||||
const response = await fetch('/api/executions');
|
||||
const executions = await response.json();
|
||||
|
||||
const dashHtml = executions.length === 0 ?
|
||||
'<div class="empty">No executions yet</div>' :
|
||||
executions.slice(0, 5).map(e => `
|
||||
<div class="execution-item" onclick="viewExecution('${e.id}')">
|
||||
<span class="status ${e.status}">${e.status}</span>
|
||||
<strong>${e.workflow_name || '[Quick Command]'}</strong>
|
||||
<div class="timestamp">by ${e.started_by} at ${new Date(e.started_at).toLocaleString()}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
document.getElementById('dashExecutions').innerHTML = dashHtml;
|
||||
let executionOffset = 0;
|
||||
const executionLimit = 50;
|
||||
|
||||
async function loadExecutions(append = false) {
|
||||
try {
|
||||
if (!append) executionOffset = 0;
|
||||
|
||||
const response = await fetch(`/api/executions?limit=${executionLimit}&offset=${executionOffset}`);
|
||||
const data = await response.json();
|
||||
const executions = data.executions || data; // Handle old and new API format
|
||||
|
||||
// Dashboard view (always first 5)
|
||||
if (!append) {
|
||||
const dashHtml = executions.length === 0 ?
|
||||
'<div class="empty">No executions yet</div>' :
|
||||
executions.slice(0, 5).map(e => `
|
||||
<div class="execution-item" onclick="viewExecution('${e.id}')">
|
||||
<span class="status ${e.status}">${e.status}</span>
|
||||
<strong>${e.workflow_name || '[Quick Command]'}</strong>
|
||||
<div class="timestamp">by ${e.started_by} at ${new Date(e.started_at).toLocaleString()}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
document.getElementById('dashExecutions').innerHTML = dashHtml;
|
||||
}
|
||||
|
||||
// Full execution list
|
||||
const fullHtml = executions.length === 0 ?
|
||||
'<div class="empty">No executions yet</div>' :
|
||||
executions.map(e => `
|
||||
@@ -997,12 +1007,29 @@
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
document.getElementById('executionList').innerHTML = fullHtml;
|
||||
|
||||
if (append) {
|
||||
document.getElementById('executionList').innerHTML += fullHtml;
|
||||
} else {
|
||||
document.getElementById('executionList').innerHTML = fullHtml;
|
||||
}
|
||||
|
||||
// Add "Load More" button if there are more executions
|
||||
if (data.hasMore) {
|
||||
const loadMoreBtn = `<button onclick="loadMoreExecutions()" style="width: 100%; margin-top: 15px;">[ Load More Executions ]</button>`;
|
||||
document.getElementById('executionList').innerHTML += loadMoreBtn;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading executions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMoreExecutions() {
|
||||
executionOffset += executionLimit;
|
||||
await loadExecutions(true);
|
||||
}
|
||||
|
||||
async function clearCompletedExecutions() {
|
||||
if (!confirm('Delete all completed and failed executions?')) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user