//! Peer identity and heartbeat storage for the monitoring mesh. use super::{Result, SqlitePool}; use tracing::instrument; /// Store a peer's identity (first-seen UUID). INSERT OR IGNORE, first wins. #[instrument(skip_all)] pub async fn store_peer_identity( pool: &SqlitePool, peer_name: &str, instance_id: &str, ) -> Result<()> { let now = chrono::Utc::now().to_rfc3339(); sqlx::query( "INSERT OR IGNORE INTO peer_identities (peer_name, instance_id, first_seen) VALUES (?, ?, ?)", ) .bind(peer_name) .bind(instance_id) .bind(&now) .execute(pool) .await?; Ok(()) } /// Update a peer's identity (UUID changed, e.g. after reinstall). #[instrument(skip_all)] pub async fn update_peer_identity( pool: &SqlitePool, peer_name: &str, instance_id: &str, ) -> Result<()> { let now = chrono::Utc::now().to_rfc3339(); sqlx::query( "UPDATE peer_identities SET instance_id = ?, first_seen = ? WHERE peer_name = ?", ) .bind(instance_id) .bind(&now) .bind(peer_name) .execute(pool) .await?; Ok(()) } /// Get a peer's first-seen instance ID. #[instrument(skip_all)] pub async fn get_peer_identity(pool: &SqlitePool, peer_name: &str) -> Result> { let row = sqlx::query_as::<_, (String,)>( "SELECT instance_id FROM peer_identities WHERE peer_name = ?", ) .bind(peer_name) .fetch_optional(pool) .await?; Ok(row.map(|r| r.0)) } #[instrument(skip_all)] pub async fn insert_peer_heartbeat( pool: &SqlitePool, peer_name: &str, status: &str, latency_ms: i64, ) -> Result { let now = chrono::Utc::now().to_rfc3339(); let result = sqlx::query( "INSERT INTO peer_heartbeats (peer_name, status, latency_ms, checked_at) VALUES (?, ?, ?, ?)", ) .bind(peer_name) .bind(status) .bind(latency_ms) .bind(&now) .execute(pool) .await?; Ok(result.last_insert_rowid()) } #[instrument(skip_all)] pub async fn get_peer_heartbeat_history( pool: &SqlitePool, peer_name: &str, limit: i64, ) -> Result> { Ok(sqlx::query_as::<_, PeerHeartbeatRow>( "SELECT id, peer_name, status, latency_ms, checked_at FROM peer_heartbeats WHERE peer_name = ? ORDER BY id DESC LIMIT ?", ) .bind(peer_name) .bind(limit) .fetch_all(pool) .await?) } #[derive(Debug, sqlx::FromRow, serde::Serialize)] pub struct PeerHeartbeatRow { pub id: i64, pub peer_name: String, pub status: String, pub latency_ms: i64, pub checked_at: String, }