Skip to main content

max / makenotwork

2.1 KB · 58 lines History Blame Raw
1 //! SyncKit fleet readout for monitoring agents.
2 //!
3 //! `GET /api/internal/synckit/client-versions` reports which SyncKit SDK
4 //! versions are actually syncing, aggregated from `sync_devices.client_version`.
5 //! SyncKit is client-side only, linked into mnw-cli, balanced_breakfast and
6 //! audiofiles, so there is no deployed version for PoM to poll the usual way;
7 //! this is the substitute, and it answers the better question anyway.
8 //!
9 //! Authed by [`AlertsAuth`], the same bearer PoM already carries. That
10 //! credential exists for monitoring agents running on other hosts, which is
11 //! exactly what polls this; `ServiceAuth` is the CLI's on-host token and reusing
12 //! it here would widen a blast radius for no reason. Deliberately not on the
13 //! public health body: which versions are in the field (including old, unpatched
14 //! ones) is an operator signal, not a public one.
15
16 use axum::{
17 Json,
18 extract::{Query, State},
19 response::IntoResponse,
20 };
21 use serde::Deserialize;
22 use sqlx::PgPool;
23
24 use crate::{auth::AlertsAuth, db, error::Result};
25
26 /// Default activity window. A month is long enough that a laptop opened weekly
27 /// still counts, short enough that a machine retired last year stops dragging an
28 /// ancient version through the readout.
29 const DEFAULT_WINDOW_DAYS: i32 = 30;
30
31 /// Widest window we will aggregate over. Bounds the scan, and a year-wide
32 /// reading stops meaning "in the field" anyway.
33 const MAX_WINDOW_DAYS: i32 = 365;
34
35 #[derive(Deserialize)]
36 pub(super) struct WindowQuery {
37 /// Count devices last seen within this many days. Clamped to
38 /// `1..=MAX_WINDOW_DAYS`.
39 days: Option<i32>,
40 }
41
42 pub(super) async fn client_versions(
43 State(db): State<PgPool>,
44 _auth: AlertsAuth,
45 Query(q): Query<WindowQuery>,
46 ) -> Result<impl IntoResponse> {
47 let days = q
48 .days
49 .unwrap_or(DEFAULT_WINDOW_DAYS)
50 .clamp(1, MAX_WINDOW_DAYS);
51 let versions = db::synckit::client_version_distribution(&db, days).await?;
52 Ok(Json(serde_json::json!({
53 "window_days": days,
54 "devices": versions.iter().map(|v| v.devices).sum::<i64>(),
55 "versions": versions,
56 })))
57 }
58