//! SyncKit fleet readout for monitoring agents. //! //! `GET /api/internal/synckit/client-versions` reports which SyncKit SDK //! versions are actually syncing, aggregated from `sync_devices.client_version`. //! SyncKit is client-side only, linked into mnw-cli, balanced_breakfast and //! audiofiles, so there is no deployed version for PoM to poll the usual way; //! this is the substitute, and it answers the better question anyway. //! //! Authed by [`AlertsAuth`], the same bearer PoM already carries. That //! credential exists for monitoring agents running on other hosts, which is //! exactly what polls this; `ServiceAuth` is the CLI's on-host token and reusing //! it here would widen a blast radius for no reason. Deliberately not on the //! public health body: which versions are in the field (including old, unpatched //! ones) is an operator signal, not a public one. use axum::{ Json, extract::{Query, State}, response::IntoResponse, }; use serde::Deserialize; use sqlx::PgPool; use crate::{auth::AlertsAuth, db, error::Result}; /// Default activity window. A month is long enough that a laptop opened weekly /// still counts, short enough that a machine retired last year stops dragging an /// ancient version through the readout. const DEFAULT_WINDOW_DAYS: i32 = 30; /// Widest window we will aggregate over. Bounds the scan, and a year-wide /// reading stops meaning "in the field" anyway. const MAX_WINDOW_DAYS: i32 = 365; #[derive(Deserialize)] pub(super) struct WindowQuery { /// Count devices last seen within this many days. Clamped to /// `1..=MAX_WINDOW_DAYS`. days: Option, } pub(super) async fn client_versions( State(db): State, _auth: AlertsAuth, Query(q): Query, ) -> Result { let days = q .days .unwrap_or(DEFAULT_WINDOW_DAYS) .clamp(1, MAX_WINDOW_DAYS); let versions = db::synckit::client_version_distribution(&db, days).await?; Ok(Json(serde_json::json!({ "window_days": days, "devices": versions.iter().map(|v| v.devices).sum::(), "versions": versions, }))) }