//! Known peer nodes for P2P discovery and connection. use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::Path; use tokio::fs; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct KnownPeer { pub onion: String, pub pubkey: String, #[serde(default)] pub name: Option, #[serde(default)] pub added_at: Option, } #[derive(Debug, Default, Serialize, Deserialize)] pub struct PeersFile { pub peers: Vec, } const PEERS_FILE: &str = "peers.json"; pub async fn load_peers(data_dir: &Path) -> Result> { let path = data_dir.join(PEERS_FILE); if !path.exists() { return Ok(Vec::new()); } let content = fs::read_to_string(&path) .await .context("Failed to read peers file")?; let file: PeersFile = serde_json::from_str(&content).unwrap_or_default(); Ok(file.peers) } pub async fn save_peers(data_dir: &Path, peers: &[KnownPeer]) -> Result<()> { let path = data_dir.join(PEERS_FILE); fs::create_dir_all(data_dir).await.context("Failed to create data dir")?; let file = PeersFile { peers: peers.to_vec(), }; let content = serde_json::to_string_pretty(&file).context("Failed to serialize peers")?; fs::write(&path, content).await.context("Failed to write peers file")?; Ok(()) } pub async fn add_peer(data_dir: &Path, peer: KnownPeer) -> Result> { let mut peers = load_peers(data_dir).await?; let exists = peers.iter().any(|p| p.pubkey == peer.pubkey); if !exists { peers.push(peer); save_peers(data_dir, &peers).await?; } Ok(peers) } pub async fn remove_peer(data_dir: &Path, pubkey: &str) -> Result> { let mut peers = load_peers(data_dir).await?; peers.retain(|p| p.pubkey != pubkey); save_peers(data_dir, &peers).await?; Ok(peers) }