//! SyncKit field-version readout storage. //! //! Stores the latest result per target the same way `scan_pipeline_checks` does, //! so `/status.json` renders the distribution from the ledger rather than //! re-probing MNW's internal endpoint on every viewer poll. Keeping the history //! rather than overwriting one row is deliberate: the interesting question about a //! fleet is how fast an old version drains, and that needs yesterday's reading. use super::{Result, SqlitePool, SyncKitFleetCheckResult}; use crate::types::SyncKitVersionSnapshot; use tracing::instrument; #[instrument(skip_all)] pub async fn insert_synckit_fleet_check( pool: &SqlitePool, result: &SyncKitFleetCheckResult, ) -> Result { let versions = serde_json::to_string(&result.versions).unwrap_or_default(); let row = sqlx::query( "INSERT INTO synckit_fleet_checks (target, window_days, devices, versions, checked_at, error) VALUES (?, ?, ?, ?, ?, ?)", ) .bind(&result.target) .bind(result.window_days) .bind(result.devices) .bind(&versions) .bind(&result.checked_at) .bind(&result.error) .execute(pool) .await?; Ok(row.last_insert_rowid()) } /// The latest SyncKit fleet readout for a target, if one has been recorded. #[instrument(skip_all)] pub async fn get_latest_synckit_fleet_check( pool: &SqlitePool, target: &str, ) -> Result> { Ok(sqlx::query_as::<_, SyncKitFleetCheckRow>( "SELECT id, target, window_days, devices, versions, checked_at, error FROM synckit_fleet_checks WHERE target = ? ORDER BY id DESC LIMIT 1", ) .bind(target) .fetch_optional(pool) .await?) } #[derive(Debug, Clone, sqlx::FromRow, serde::Serialize)] pub struct SyncKitFleetCheckRow { pub id: i64, pub target: String, pub window_days: i64, pub devices: i64, /// JSON array of version snapshots. Use [`Self::version_list`]. pub versions: String, pub checked_at: String, pub error: Option, } impl SyncKitFleetCheckRow { /// Decode the stored version snapshots, tolerating a malformed blob as empty. pub fn version_list(&self) -> Vec { serde_json::from_str(&self.versions).unwrap_or_default() } }