//! Background task that runs the full pipeline on a configurable interval. use std::time::Duration; use actix_web::web; use chrono::Utc; use crate::state::AppState; /// Spawn the pipeline scheduler background loop. /// /// Sleeps for the configured interval, then runs the full pipeline if enabled. /// Reads config each iteration so changes take effect without restart. pub fn spawn(state: web::Data) { tokio::spawn(async move { loop { let (enabled, hours) = { let cfg = state.config.read().await; ( cfg.scheduling.pipeline_enabled, cfg.scheduling.pipeline_interval_hours.max(1), ) }; let sleep_secs = u64::from(hours) * 3600; // Update next-run time { let mut sched = state.scheduler.lock().await; sched.next_pipeline = if enabled { Some((Utc::now() + chrono::Duration::seconds(sleep_secs as i64)).naive_utc()) } else { None }; } tokio::time::sleep(Duration::from_secs(sleep_secs)).await; if !enabled { continue; } // Check if this run was skipped { let mut sched = state.scheduler.lock().await; sched.next_pipeline = None; if sched.skip_pipeline { sched.skip_pipeline = false; tracing::info!("scheduled pipeline skipped (user cancelled)"); continue; } } tracing::info!("scheduled pipeline starting"); let task_ids = crate::pipeline::run_pipeline(&state).await; tracing::info!(?task_ids, "scheduled pipeline complete"); } }); }