feat(scheduler): live running indicators for cron-triggered jobs
Add api/status.php — reads all scheduled jobs' stat files and returns a
{id: status} map. The scheduler page polls it every 3s and lights up the
dot on any job whose stat shows status=running, clearing it when done.
Replace single vvRunningId with vvRunningSet (Set) so multiple jobs can
show as running simultaneously. vvSetDot/vvClearDot handle per-job dots
and the header dot for the active job. Remove stale-count detection from
vvFetchRight — status poll is now the single source of truth.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// Returns current run status for all scheduled jobs.
|
||||
// Used by the scheduler page to light up running indicators without user interaction.
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/scheduler.php';
|
||||
|
||||
$schedule = vv_schedule_load();
|
||||
$result = [];
|
||||
foreach ($schedule as $id => $entry) {
|
||||
$statFile = vv_job_stat_path($id);
|
||||
if (!file_exists($statFile)) continue;
|
||||
$stat = json_decode(@file_get_contents($statFile) ?: '{}', true) ?: [];
|
||||
$status = $stat['status'] ?? 'unknown';
|
||||
if ($status === 'running' && !empty($stat['pid']) && !file_exists("/proc/{$stat['pid']}")) {
|
||||
$status = 'error';
|
||||
}
|
||||
$result[$id] = $status;
|
||||
}
|
||||
echo json_encode($result);
|
||||
@@ -302,13 +302,12 @@ foreach ($tree as $orch) {
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let vvActiveId = null;
|
||||
let vvRunningId = null;
|
||||
let vvLastTs = 0;
|
||||
let vvStaleCount = 0;
|
||||
let vvPollTimer = null;
|
||||
let vvEditorId = null;
|
||||
let vvConfId = null;
|
||||
let vvActiveId = null;
|
||||
let vvRunningSet = new Set();
|
||||
let vvPollTimer = null;
|
||||
let vvStatusTimer = null;
|
||||
let vvEditorId = null;
|
||||
let vvConfId = null;
|
||||
|
||||
function vvPost(url, data) {
|
||||
const params = new URLSearchParams({csrf_token, ...data});
|
||||
@@ -391,7 +390,6 @@ function vvBackToSuggestions() {
|
||||
}
|
||||
vvActiveId = null;
|
||||
if (vvPollTimer) { clearInterval(vvPollTimer); vvPollTimer = null; }
|
||||
vvSetRunning(null);
|
||||
|
||||
document.getElementById('vv-log-pre').style.display = 'none';
|
||||
document.getElementById('vv-editor').style.display = 'none';
|
||||
@@ -463,37 +461,44 @@ function vvFetchRight() {
|
||||
requestAnimationFrame(() => { pre.scrollTop = savedScroll; });
|
||||
}
|
||||
ts.textContent = d.ts ? 'Last run: ' + new Date(d.ts * 1000).toLocaleString() : '';
|
||||
|
||||
// Running indicator: clear when log stops changing
|
||||
if (vvRunningId === vvActiveId) {
|
||||
if (d.ts && d.ts === vvLastTs) {
|
||||
if (++vvStaleCount >= 2) vvSetRunning(null);
|
||||
} else {
|
||||
vvLastTs = d.ts || 0;
|
||||
vvStaleCount = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function vvSetRunning(id) {
|
||||
// Clear old dot
|
||||
if (vvRunningId) {
|
||||
const old = document.querySelector('[data-id="' + CSS.escape(vvRunningId) + '"]');
|
||||
if (old) { const dot = old.querySelector('.vv-job-dot'); dot.className = 'vv-job-dot'; }
|
||||
}
|
||||
vvRunningId = id;
|
||||
vvLastTs = 0;
|
||||
vvStaleCount = 0;
|
||||
const headerDot = document.getElementById('vv-log-dot');
|
||||
if (id) {
|
||||
headerDot.style.display = 'inline-block';
|
||||
const job = document.querySelector('[data-id="' + CSS.escape(id) + '"]');
|
||||
if (job) job.querySelector('.vv-job-dot').classList.add('vv-dot-running');
|
||||
} else {
|
||||
headerDot.style.display = 'none';
|
||||
}
|
||||
function vvSetDot(id) {
|
||||
const job = document.querySelector('[data-id="' + CSS.escape(id) + '"]');
|
||||
if (job) job.querySelector('.vv-job-dot').classList.add('vv-dot-running');
|
||||
if (id === vvActiveId) document.getElementById('vv-log-dot').style.display = 'inline-block';
|
||||
}
|
||||
|
||||
function vvClearDot(id) {
|
||||
const job = document.querySelector('[data-id="' + CSS.escape(id) + '"]');
|
||||
if (job) job.querySelector('.vv-job-dot').classList.remove('vv-dot-running');
|
||||
if (id === vvActiveId) document.getElementById('vv-log-dot').style.display = 'none';
|
||||
}
|
||||
|
||||
function vvPollStatus() {
|
||||
fetch('/plugins/varaverk/api/status.php')
|
||||
.then(r => r.json())
|
||||
.then(statuses => {
|
||||
const nowRunning = new Set(
|
||||
Object.entries(statuses).filter(([, s]) => s === 'running').map(([id]) => id)
|
||||
);
|
||||
for (const id of vvRunningSet) {
|
||||
if (!nowRunning.has(id)) { vvClearDot(id); if (id === vvActiveId) vvFetchRight(); }
|
||||
}
|
||||
for (const id of nowRunning) {
|
||||
if (!vvRunningSet.has(id)) { vvSetDot(id); if (id === vvActiveId) vvFetchRight(); }
|
||||
}
|
||||
vvRunningSet = nowRunning;
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function vvStartStatusPoll() {
|
||||
if (vvStatusTimer) return;
|
||||
vvPollStatus();
|
||||
vvStatusTimer = setInterval(vvPollStatus, 3000);
|
||||
}
|
||||
|
||||
function vvSelectLog(btn) {
|
||||
@@ -505,12 +510,14 @@ function vvRunJob(btn) {
|
||||
const job = btn.closest('[data-id]');
|
||||
const id = job.dataset.id;
|
||||
vvOpenRight(id);
|
||||
vvSetRunning(id);
|
||||
vvSetDot(id);
|
||||
vvRunningSet.add(id);
|
||||
vvPost('/plugins/varaverk/api/run.php', {id})
|
||||
.then(d => {
|
||||
if (!d.ok) {
|
||||
document.getElementById('vv-log-pre').textContent = '✗ ' + (d.error ?? 'Failed to start');
|
||||
vvSetRunning(null);
|
||||
vvClearDot(id);
|
||||
vvRunningSet.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -519,12 +526,14 @@ function vvDryRun(btn) {
|
||||
const job = btn.closest('[data-id]');
|
||||
const id = job.dataset.id;
|
||||
vvOpenRight(id);
|
||||
vvSetRunning(id);
|
||||
vvSetDot(id);
|
||||
vvRunningSet.add(id);
|
||||
vvPost('/plugins/varaverk/api/dryrun.php', {id})
|
||||
.then(d => {
|
||||
if (!d.ok) {
|
||||
document.getElementById('vv-log-pre').textContent = '✗ ' + (d.error ?? 'Failed to start');
|
||||
vvSetRunning(null);
|
||||
vvClearDot(id);
|
||||
vvRunningSet.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -809,5 +818,6 @@ requestAnimationFrame(function() {
|
||||
}
|
||||
vvFitRight();
|
||||
}
|
||||
vvStartStatusPoll();
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user