use super::RpcHandler; use crate::{node_message, nostr_discovery, peers}; use crate::peers::KnownPeer; use anyhow::Result; impl RpcHandler { pub(super) async fn handle_node_add_peer( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let onion = params .get("onion") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing onion"))?; let pubkey = params .get("pubkey") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing pubkey"))?; let name = params.get("name").and_then(|v| v.as_str()).map(String::from); let peer = KnownPeer { onion: onion.to_string(), pubkey: pubkey.to_string(), name, added_at: Some(chrono::Utc::now().to_rfc3339()), }; let peers = peers::add_peer(&self.config.data_dir, peer).await?; Ok(serde_json::json!({ "peers": peers })) } pub(super) async fn handle_node_list_peers(&self) -> Result { let peers = peers::load_peers(&self.config.data_dir).await?; Ok(serde_json::json!({ "peers": peers })) } pub(super) async fn handle_node_remove_peer( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let pubkey = params .get("pubkey") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing pubkey"))?; let peers = peers::remove_peer(&self.config.data_dir, pubkey).await?; Ok(serde_json::json!({ "peers": peers })) } pub(super) async fn handle_node_send_message( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let onion = params .get("onion") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing onion"))?; let message = params .get("message") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing message"))?; // Validate onion is a known peer to prevent SSRF to arbitrary Tor destinations let known_peers = peers::load_peers(&self.config.data_dir).await?; let is_known = known_peers.iter().any(|p| { p.onion == onion || p.onion == format!("{}.onion", onion) || format!("{}.onion", p.onion) == onion }); if !is_known { return Err(anyhow::anyhow!( "Onion address not in known peers list. Add the peer first." )); } let (data, _) = self.state_manager.get_snapshot().await; let pubkey = data.server_info.pubkey.clone(); node_message::send_to_peer(onion, &pubkey, message).await?; Ok(serde_json::json!({ "ok": true, "sent_to": onion })) } pub(super) async fn handle_node_check_peer( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let onion = params .get("onion") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing onion"))?; let reachable = node_message::check_peer_reachable(onion).await.unwrap_or(false); Ok(serde_json::json!({ "onion": onion, "reachable": reachable })) } pub(super) async fn handle_node_messages_received(&self) -> Result { let messages = node_message::get_received(); Ok(serde_json::json!({ "messages": messages })) } pub(super) async fn handle_node_nostr_discover(&self) -> Result { let identity_dir = self.config.data_dir.join("identity"); let nodes = nostr_discovery::discover_archipelago_nodes( &identity_dir, &self.config.nostr_relays, self.config.nostr_tor_proxy.as_deref(), ) .await?; Ok(serde_json::json!({ "nodes": nodes })) } }