diff --git a/js/ui.js b/js/ui.js index b8e42a0..b691475 100644 --- a/js/ui.js +++ b/js/ui.js @@ -289,6 +289,10 @@ async function saveInstance() { hardware_acceleration: +document.getElementById('f-hardware-accel').checked, }; + // Snapshot job state before creation — jobs fire immediately after the 201 + // so the baseline must be captured before the POST, not after. + const jobBaseline = !editingVmid ? await _snapshotJobBaseline() : null; + const result = editingVmid ? await updateInstance(editingVmid, data) : await createInstance(data); @@ -298,7 +302,7 @@ async function saveInstance() { showToast(`${name} ${editingVmid ? 'updated' : 'created'}`, 'success'); closeModal(); - if (!editingVmid) await _waitForOnCreateJobs(); + if (jobBaseline) await _waitForOnCreateJobs(jobBaseline); if (currentVmid && document.getElementById('page-detail').classList.contains('active')) { await renderDetailPage(vmid); @@ -307,16 +311,18 @@ async function saveInstance() { } } -async function _waitForOnCreateJobs() { +async function _snapshotJobBaseline() { + const jobs = await fetch('/api/jobs').then(r => r.json()); + return new Map(jobs.map(j => [j.id, j.last_run_id ?? null])); +} + +async function _waitForOnCreateJobs(baseline) { const jobs = await fetch('/api/jobs').then(r => r.json()); const relevant = jobs.filter(j => { try { return JSON.parse(j.config || '{}').run_on_create; } catch { return false; } }); if (!relevant.length) return; - // Snapshot run IDs before jobs fire so we can detect new completions - const baseline = new Map(relevant.map(j => [j.id, j.last_run_id ?? null])); - const deadline = Date.now() + 30_000; while (Date.now() < deadline) { await new Promise(r => setTimeout(r, 500));