Fix stuck processing jobs and increase timeouts
All checks were successful
Build and Push Frontend Docker Image / build (push) Successful in 57s
Build and Push Docker Image / build (push) Successful in 30m18s

Background Job Processor:
- Add src/services/jobProcessor.ts that polls RunPod every 30s for stuck jobs
- Automatically completes or fails jobs that were abandoned (user navigated away)
- Times out jobs after 25 minutes

Client-Side Resume:
- Add GET /api/generate/pending endpoint to fetch user's processing jobs
- Add checkPendingJobs() that runs on login/page load
- Show notification banner when user has jobs generating in background
- Add "View Progress" button to resume polling for a job

Timeout Increases (10min → 25min):
- src/utils/validators.ts: request validation max/default
- src/config.ts: RUNPOD_MAX_TIMEOUT_MS default
- public/js/app.js: client-side polling maxTime
- src/services/jobProcessor.ts: background processor timeout

CI/CD Optimization:
- Add paths-ignore to backend build.yaml to skip rebuilds on frontend-only changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-01-07 05:36:53 +00:00
parent 0758b866bd
commit 8f050b41a0
9 changed files with 429 additions and 5 deletions

View File

@@ -75,6 +75,62 @@ function showMainApp() {
}
showSection('generate');
checkPendingJobs();
}
// Pending Jobs
async function checkPendingJobs() {
try {
const data = await api('/generate/pending');
if (data.jobs && data.jobs.length > 0) {
showPendingJobsNotification(data.jobs);
}
} catch (error) {
console.error('Failed to check pending jobs:', error);
}
}
function showPendingJobsNotification(jobs) {
// Remove existing banner if any
const existingBanner = document.querySelector('.pending-jobs-banner');
if (existingBanner) existingBanner.remove();
const banner = document.createElement('div');
banner.className = 'pending-jobs-banner';
banner.innerHTML = `
<span>You have ${jobs.length} video${jobs.length > 1 ? 's' : ''} generating in the background</span>
<div class="pending-jobs-actions">
<button class="btn btn-sm" onclick="resumeLatestJob(${jobs[0].id}, '${jobs[0].runpod_job_id}')">View Progress</button>
<button class="btn btn-sm" onclick="this.closest('.pending-jobs-banner').remove()">Dismiss</button>
</div>
`;
document.querySelector('.main-content').prepend(banner);
}
async function resumeLatestJob(contentId, jobId) {
// Switch to generate tab and show progress
showSection('generate');
const statusEl = document.getElementById('generation-status');
const videoEl = document.getElementById('output-video');
const btn = document.getElementById('generate-btn');
btn.disabled = true;
btn.textContent = 'Generating...';
statusEl.className = 'status-message info';
statusEl.textContent = 'Resuming job...';
statusEl.classList.remove('hidden');
videoEl.classList.add('hidden');
// Remove the banner
const banner = document.querySelector('.pending-jobs-banner');
if (banner) banner.remove();
// Poll for completion
await pollJob(jobId, contentId, statusEl, videoEl);
btn.disabled = false;
btn.textContent = 'Generate Video';
}
// Login
@@ -268,7 +324,7 @@ document.getElementById('generate-form').addEventListener('submit', async (e) =>
async function pollJob(jobId, contentId, statusEl, videoEl) {
const startTime = Date.now();
const maxTime = 10 * 60 * 1000; // 10 minutes
const maxTime = 25 * 60 * 1000; // 25 minutes
while (Date.now() - startTime < maxTime) {
const elapsed = Math.floor((Date.now() - startTime) / 1000);