Skip to main content

max / makenotwork

2.3 KB · 72 lines History Blame Raw
1 //! Local CA-bundle freshness check storage.
2 //!
3 //! Same shape as `systemd_checks`: the latest result per target is read from the
4 //! ledger rather than shelling out to `apt-cache` on every viewer poll, and the
5 //! issue lines ride along as a JSON blob.
6
7 use super::{CaBundleCheckResult, Result, SqlitePool};
8 use tracing::instrument;
9
10 #[instrument(skip_all)]
11 pub async fn insert_ca_bundle_check(
12 pool: &SqlitePool,
13 result: &CaBundleCheckResult,
14 ) -> Result<i64> {
15 let issues = serde_json::to_string(&result.issues).unwrap_or_default();
16 let row = sqlx::query(
17 "INSERT INTO ca_bundle_checks (target, status, package, installed, candidate, cert_count, lists_age_hours, issues, checked_at, error)
18 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
19 )
20 .bind(&result.target)
21 .bind(&result.status)
22 .bind(&result.package)
23 .bind(&result.installed)
24 .bind(&result.candidate)
25 .bind(result.cert_count)
26 .bind(result.lists_age_hours)
27 .bind(&issues)
28 .bind(&result.checked_at)
29 .bind(&result.error)
30 .execute(pool)
31 .await?;
32 Ok(row.last_insert_rowid())
33 }
34
35 /// The latest CA-bundle check for a target, if one has been recorded.
36 #[instrument(skip_all)]
37 pub async fn get_latest_ca_bundle_check(
38 pool: &SqlitePool,
39 target: &str,
40 ) -> Result<Option<CaBundleCheckRow>> {
41 Ok(sqlx::query_as::<_, CaBundleCheckRow>(
42 "SELECT id, target, status, package, installed, candidate, cert_count, lists_age_hours, issues, checked_at, error
43 FROM ca_bundle_checks WHERE target = ? ORDER BY id DESC LIMIT 1",
44 )
45 .bind(target)
46 .fetch_optional(pool)
47 .await?)
48 }
49
50 #[derive(Debug, Clone, sqlx::FromRow, serde::Serialize)]
51 pub struct CaBundleCheckRow {
52 pub id: i64,
53 pub target: String,
54 pub status: String,
55 pub package: String,
56 pub installed: Option<String>,
57 pub candidate: Option<String>,
58 pub cert_count: Option<i64>,
59 pub lists_age_hours: Option<i64>,
60 /// JSON array of the fired issue lines. Use [`Self::issue_list`].
61 pub issues: String,
62 pub checked_at: String,
63 pub error: Option<String>,
64 }
65
66 impl CaBundleCheckRow {
67 /// Decode the stored issue lines, tolerating a malformed blob as empty.
68 pub fn issue_list(&self) -> Vec<String> {
69 serde_json::from_str(&self.issues).unwrap_or_default()
70 }
71 }
72