Fix sentinel countdown: sync to actual scan schedule, not page load
- Sentinel thread sets next_scan_ts = time.time() + interval BEFORE sleeping - API returns next_scan_in derived from real next_scan_ts, not estimated - Frontend calculates server clock offset and counts down to the actual target timestamp — refresh shows the same remaining time, not a reset - Shows ✓ in green when scan fires, resumes countdown on next poll Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
357918013d
commit
7948089f04
@ -775,13 +775,21 @@ async function loadThreats() {
|
||||
|
||||
sentinelCard.appendChild(sHeader);
|
||||
|
||||
// Start countdown
|
||||
// Countdown synced to server's next_scan_ts
|
||||
// Store the absolute target time so refresh doesn't reset
|
||||
if (window._sentinelTimer) clearInterval(window._sentinelTimer);
|
||||
window._sentinelCountdown = nextIn;
|
||||
var serverNow = sentinel.server_time || (Date.now()/1000);
|
||||
var nextScanTs = serverNow + nextIn;
|
||||
window._sentinelTargetTs = nextScanTs;
|
||||
window._sentinelServerOffset = serverNow - (Date.now()/1000); // clock difference
|
||||
window._sentinelTimer = setInterval(function(){
|
||||
window._sentinelCountdown = Math.max(0, window._sentinelCountdown - 1);
|
||||
var localNow = (Date.now()/1000) + (window._sentinelServerOffset||0);
|
||||
var remaining = Math.max(0, (window._sentinelTargetTs||0) - localNow);
|
||||
var el = document.getElementById('sentinel-countdown');
|
||||
if (el) { el.textContent = Math.ceil(window._sentinelCountdown) || '...'; if (window._sentinelCountdown <= 0) { el.textContent = '✓'; el.style.color = '#4ade80'; clearInterval(window._sentinelTimer); } }
|
||||
if (el) {
|
||||
if (remaining > 0) { el.textContent = Math.ceil(remaining); el.style.color = '#d946ef'; }
|
||||
else { el.textContent = '✓'; el.style.color = '#4ade80'; }
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
if (ss.last_error) {
|
||||
@ -6390,7 +6398,7 @@ SENTINEL_MODEL = "qwen2.5:latest"
|
||||
SENTINEL_INTERVAL = 300 # 5 minutes
|
||||
_sentinel_last_pos = 0
|
||||
_sentinel_results = [] # last 50 analyses
|
||||
_sentinel_stats = {"scans": 0, "bans": 0, "last_run": None, "last_error": None}
|
||||
_sentinel_stats = {"scans": 0, "bans": 0, "last_run": None, "last_error": None, "next_scan_ts": 0}
|
||||
|
||||
def _sentinel_log_entry(msg):
|
||||
"""Write to sentinel log file."""
|
||||
@ -6573,6 +6581,7 @@ def _sentinel_loop():
|
||||
|
||||
_sentinel_log_entry("SENTINEL_START model=" + SENTINEL_MODEL + " interval=" + str(SENTINEL_INTERVAL) + "s")
|
||||
while True:
|
||||
_sentinel_stats["next_scan_ts"] = time.time() + SENTINEL_INTERVAL
|
||||
time.sleep(SENTINEL_INTERVAL)
|
||||
try:
|
||||
_sentinel_scan()
|
||||
@ -6585,18 +6594,16 @@ def _sentinel_loop():
|
||||
@app.route("/api/admin/sentinel")
|
||||
@admin_required
|
||||
def admin_sentinel_status():
|
||||
last_ts = _sentinel_stats.get("last_run_ts", 0)
|
||||
now = time.time()
|
||||
elapsed = now - last_ts if last_ts else 0
|
||||
next_in = max(0, SENTINEL_INTERVAL - elapsed)
|
||||
next_ts = _sentinel_stats.get("next_scan_ts", 0)
|
||||
next_in = max(0, next_ts - now)
|
||||
return jsonify({
|
||||
"stats": _sentinel_stats,
|
||||
"recent_verdicts": list(reversed(_sentinel_results[-20:])),
|
||||
"model": SENTINEL_MODEL,
|
||||
"interval": SENTINEL_INTERVAL,
|
||||
"elapsed_since_scan": round(elapsed, 1),
|
||||
"next_scan_in": round(next_in, 1),
|
||||
"server_time": now
|
||||
"server_time": round(now, 1)
|
||||
})
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user