fix(scheduler): prevent auto-enable with wrong interval on page load
Root cause: GET /api/sync/schedule returned interval_minutes=null when scheduler was stopped, causing dropdown to stay on first HTML option (1 min). Setting .value programmatically could trigger onchange, sending a second PUT with interval=1 right after the user's intended interval. - GET schedule falls back to DB/default (10 min) when scheduler is off - Add _schedulerLoading flag to block onchange during loadSchedulerStatus - Default interval 10 min everywhere (was 5 in backend) - Cache bust dashboard.js v=33 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@ async def lifespan(app: FastAPI):
|
|||||||
try:
|
try:
|
||||||
config = await sqlite_service.get_scheduler_config()
|
config = await sqlite_service.get_scheduler_config()
|
||||||
if config.get("enabled") == "True":
|
if config.get("enabled") == "True":
|
||||||
interval = int(config.get("interval_minutes", "5"))
|
interval = int(config.get("interval_minutes", "10"))
|
||||||
scheduler_service.start_scheduler(interval)
|
scheduler_service.start_scheduler(interval)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -804,8 +804,12 @@ async def update_schedule(config: ScheduleConfig):
|
|||||||
|
|
||||||
@router.get("/api/sync/schedule")
|
@router.get("/api/sync/schedule")
|
||||||
async def get_schedule():
|
async def get_schedule():
|
||||||
"""Get current scheduler status."""
|
"""Get current scheduler status (falls back to DB for interval)."""
|
||||||
return scheduler_service.get_scheduler_status()
|
status = scheduler_service.get_scheduler_status()
|
||||||
|
if status["interval_minutes"] is None:
|
||||||
|
config = await sqlite_service.get_scheduler_config()
|
||||||
|
status["interval_minutes"] = int(config.get("interval_minutes", "10"))
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/settings")
|
@router.get("/api/settings")
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def init_scheduler():
|
|||||||
logger.info("Scheduler initialized")
|
logger.info("Scheduler initialized")
|
||||||
|
|
||||||
|
|
||||||
def start_scheduler(interval_minutes: int = 5):
|
def start_scheduler(interval_minutes: int = 10):
|
||||||
"""Start the scheduler with the given interval."""
|
"""Start the scheduler with the given interval."""
|
||||||
global _is_running
|
global _is_running
|
||||||
if _scheduler is None:
|
if _scheduler is None:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ let _lastRunId = null;
|
|||||||
let _currentRunId = null;
|
let _currentRunId = null;
|
||||||
let _pollIntervalMs = 5000; // default, overridden from settings
|
let _pollIntervalMs = 5000; // default, overridden from settings
|
||||||
let _knownLastRunId = null; // track last_run.run_id to detect missed syncs
|
let _knownLastRunId = null; // track last_run.run_id to detect missed syncs
|
||||||
|
let _schedulerLoading = false; // prevent onchange during programmatic load
|
||||||
|
|
||||||
// ── Init ──────────────────────────────────────────
|
// ── Init ──────────────────────────────────────────
|
||||||
|
|
||||||
@@ -202,6 +203,7 @@ async function toggleScheduler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateSchedulerInterval() {
|
async function updateSchedulerInterval() {
|
||||||
|
if (_schedulerLoading) return; // ignore programmatic changes during load
|
||||||
const enabled = document.getElementById('schedulerToggle').checked;
|
const enabled = document.getElementById('schedulerToggle').checked;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
await toggleScheduler();
|
await toggleScheduler();
|
||||||
@@ -209,15 +211,16 @@ async function updateSchedulerInterval() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadSchedulerStatus() {
|
async function loadSchedulerStatus() {
|
||||||
|
_schedulerLoading = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/sync/schedule');
|
const res = await fetch('/api/sync/schedule');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
document.getElementById('schedulerToggle').checked = data.enabled || false;
|
document.getElementById('schedulerToggle').checked = data.enabled || false;
|
||||||
if (data.interval_minutes) {
|
document.getElementById('schedulerInterval').value = data.interval_minutes || 10;
|
||||||
document.getElementById('schedulerInterval').value = data.interval_minutes;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('loadSchedulerStatus error:', err);
|
console.error('loadSchedulerStatus error:', err);
|
||||||
|
} finally {
|
||||||
|
_schedulerLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,5 +114,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=32"></script>
|
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=33"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user