Auto-commit: v2.12 — Fix frontend stuck on Initializing/Waiting for logs

- web/tasks.py: db_log_callback now writes non-OperationalError exceptions to data/app.log for visibility
- web/tasks.py: generate_book_task restructured with try...finally to guarantee final status update — run can never be left in 'running' state if worker crashes
- templates/project.html: added .catch() to fetchLog() with console.error + polling resume on failure; added manual Refresh button to status bar
- templates/run_details.html: improved .catch() in updateLog() with descriptive message + 5s retry; added manual Refresh button to status bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 18:40:28 -05:00
parent 87f24d2bd8
commit 4e39e18dfe
4 changed files with 94 additions and 191 deletions

View File

@@ -107,6 +107,9 @@
<div class="d-flex align-items-center mb-2">
<div class="spinner-border text-primary spinner-border-sm me-2" role="status"></div>
<strong class="text-primary" id="statusPhase">Initializing...</strong>
<button type="button" class="btn btn-sm btn-outline-secondary ms-auto py-0" onclick="fetchLog()" title="Manually refresh status">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
<h5 class="card-title mb-3" id="statusMessage">Preparing environment...</h5>
<div class="progress" style="height: 20px;">
@@ -604,12 +607,17 @@
} else {
if (activeInterval) clearInterval(activeInterval);
activeInterval = null;
// Reload if we were polling (watched it finish) OR if page loaded as running but is now done
if (initialRunStatus === 'running' || initialRunStatus === 'queued') {
window.location.reload();
}
}
})
.catch(err => {
console.error("Polling failed:", err);
// Resume polling so the UI doesn't silently stop updating
if (!activeInterval) activeInterval = setInterval(fetchLog, 2000);
});
}

View File

@@ -100,9 +100,14 @@
<!-- Status Bar -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="fw-bold" id="status-text">Status: {{ run.status|title }}</span>
<span class="text-muted" id="run-duration">{{ run.duration() }}</span>
<div>
<span class="text-muted me-2" id="run-duration">{{ run.duration() }}</span>
<button type="button" class="btn btn-sm btn-outline-secondary py-0" onclick="updateLog()" title="Manually refresh status">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
</div>
<div class="progress" style="height: 20px;">
<div id="status-bar" class="progress-bar {% if run.status == 'running' %}progress-bar-striped progress-bar-animated{% elif run.status == 'failed' %}bg-danger{% else %}bg-success{% endif %}"
@@ -440,7 +445,10 @@
}
}
})
.catch(err => console.error(err));
.catch(err => {
console.error("Polling failed:", err);
setTimeout(updateLog, 5000);
});
}
// Start polling