Live sync SSH metadata in background

This commit is contained in:
Eric Wendland 2026-05-18 18:29:45 +02:00
commit 68153be5d5
9 changed files with 318 additions and 13 deletions

View file

@ -709,6 +709,31 @@ impl Store {
.map_err(StoreError::from)
}
pub fn put_module_state(&self, state: &StoredModuleState) -> Result<(), StoreError> {
self.conn.execute(
r#"INSERT OR REPLACE INTO module_state(module, state_json, updated_at_ms)
VALUES (?1, ?2, ?3)"#,
params![state.module, state.state_json, state.updated_at_ms],
)?;
Ok(())
}
pub fn get_module_state(&self, module: &str) -> Result<Option<StoredModuleState>, StoreError> {
let mut stmt = self.conn.prepare(
"SELECT module, state_json, updated_at_ms FROM module_state WHERE module = ?1",
)?;
let mut rows = stmt.query(params![module])?;
if let Some(row) = rows.next()? {
Ok(Some(StoredModuleState {
module: row.get(0)?,
state_json: row.get(1)?,
updated_at_ms: row.get(2)?,
}))
} else {
Ok(None)
}
}
pub fn insert_auth_op(&self, op: &StoredAuthOp) -> Result<(), StoreError> {
self.conn.execute(
r#"INSERT OR REPLACE INTO auth_ops(op_id, resource_id, op_json, created_at_ms)
@ -846,6 +871,20 @@ impl Store {
.map_err(StoreError::from)
}
pub fn list_ssh_cert_requests_since(
&self,
since_ms: i64,
) -> Result<Vec<StoredSshCertRequest>, StoreError> {
let mut stmt = self.conn.prepare(
r#"SELECT request_id, requester_node, public_key, public_key_fingerprint, cert_kind,
principals_json, requested_validity, renewal_of, reason, status, created_at_ms
FROM ssh_cert_requests WHERE created_at_ms >= ?1 ORDER BY created_at_ms, request_id"#,
)?;
let rows = stmt.query_map(params![since_ms], stored_ssh_cert_request_from_row)?;
rows.collect::<Result<Vec<_>, _>>()
.map_err(StoreError::from)
}
pub fn insert_ssh_certificate(
&self,
certificate: &StoredSshCertificate,
@ -883,6 +922,27 @@ impl Store {
.map_err(StoreError::from)
}
pub fn list_ssh_certificates_since(
&self,
since_ms: i64,
) -> Result<Vec<StoredSshCertificate>, StoreError> {
let mut stmt = self.conn.prepare(
r#"SELECT cert_id, request_id, certificate, certificate_fingerprint, imported_at_ms
FROM ssh_certificates WHERE imported_at_ms >= ?1 ORDER BY imported_at_ms, cert_id"#,
)?;
let rows = stmt.query_map(params![since_ms], |row| {
Ok(StoredSshCertificate {
cert_id: row.get(0)?,
request_id: row.get(1)?,
certificate: row.get(2)?,
certificate_fingerprint: row.get(3)?,
imported_at_ms: row.get(4)?,
})
})?;
rows.collect::<Result<Vec<_>, _>>()
.map_err(StoreError::from)
}
pub fn insert_ssh_revocation(
&self,
revocation: &StoredSshRevocation,
@ -921,6 +981,28 @@ impl Store {
rows.collect::<Result<Vec<_>, _>>()
.map_err(StoreError::from)
}
pub fn list_ssh_revocations_since(
&self,
since_ms: i64,
) -> Result<Vec<StoredSshRevocation>, StoreError> {
let mut stmt = self.conn.prepare(
r#"SELECT revocation_id, kind, target, reason, created_at_ms, published
FROM ssh_revocations WHERE created_at_ms >= ?1 ORDER BY created_at_ms, revocation_id"#,
)?;
let rows = stmt.query_map(params![since_ms], |row| {
Ok(StoredSshRevocation {
revocation_id: row.get(0)?,
kind: row.get(1)?,
target: row.get(2)?,
reason: row.get(3)?,
created_at_ms: row.get(4)?,
published: row.get::<_, i64>(5)? != 0,
})
})?;
rows.collect::<Result<Vec<_>, _>>()
.map_err(StoreError::from)
}
}
fn stored_ssh_cert_request_from_row(
@ -1064,6 +1146,13 @@ pub struct StoredPeerCard {
pub updated_at_ms: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StoredModuleState {
pub module: String,
pub state_json: String,
pub updated_at_ms: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StoredAuthOp {
pub op_id: String,
@ -1173,6 +1262,18 @@ mod tests {
.expect("insert revocation");
assert_eq!(
store.list_ssh_revocations().expect("list revocations"),
vec![revocation.clone()]
);
assert_eq!(
store
.list_ssh_cert_requests_since(0)
.expect("list requests since"),
vec![request]
);
assert_eq!(
store
.list_ssh_revocations_since(1)
.expect("list revocations since"),
vec![revocation]
);
}
@ -1193,6 +1294,23 @@ mod tests {
assert_eq!(store.list_peer_cards().expect("list"), vec![peer_card]);
}
#[test]
fn module_state_roundtrip() {
let store = Store::open_memory().expect("open");
let state = StoredModuleState {
module: "live-sync:node:laptop:ssh-certs".to_owned(),
state_json: r#"{"cursor_ms":42}"#.to_owned(),
updated_at_ms: 43,
};
store.put_module_state(&state).expect("put state");
assert_eq!(
store
.get_module_state("live-sync:node:laptop:ssh-certs")
.expect("get state"),
Some(state)
);
}
#[test]
fn auth_ops_roundtrip_by_resource() {
let store = Store::open_memory().expect("open");