use base64::{ engine::general_purpose, Engine as _, }; use reqwest::Client; use serde_json::Value; use std::time::Duration; /// Get information about the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// /// # Returns /// /// * `Result` - The node information as a JSON value, or an error message pub async fn get_node_info(api_url: &str) -> Result { let client = Client::new(); let url = format!("{}/api/v1/admin", api_url); let response = client .get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// List all peers connected to the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// /// # Returns /// /// * `Result` - The list of peers as a JSON value, or an error message pub async fn list_peers(api_url: &str) -> Result { let client = Client::new(); let url = format!("{}/api/v1/admin/peers", api_url); let response = client .get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// Add a new peer to the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// * `peer_address` - The address of the peer to add /// /// # Returns /// /// * `Result` - The result of the operation as a JSON value, or an error message pub async fn add_peer(api_url: &str, peer_address: &str) -> Result { let client = Client::new(); let url = format!("{}/api/v1/admin/peers", api_url); let response = client .post(&url) .json(&serde_json::json!({ "endpoint": peer_address })) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if status == reqwest::StatusCode::NO_CONTENT { // Successfully added, but no content to parse return Ok(serde_json::json!({"success": true})); } if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } // For other success statuses that might have a body let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// Remove a peer from the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// * `peer_id` - The ID of the peer to remove /// /// # Returns /// /// * `Result` - The result of the operation as a JSON value, or an error message pub async fn remove_peer(api_url: &str, peer_id: &str) -> Result { let client = Client::new(); let peer_id_url_encoded = urlencoding::encode(peer_id); let url = format!("{}/api/v1/admin/peers/{}", api_url, peer_id_url_encoded); let response = client .delete(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if status == reqwest::StatusCode::NO_CONTENT { // Successfully removed, but no content to parse return Ok(serde_json::json!({"success": true})); } if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// List all selected routes in the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// /// # Returns /// /// * `Result` - The list of selected routes as a JSON value, or an error message pub async fn list_selected_routes(api_url: &str) -> Result { let client = Client::new(); let url = format!("{}/api/v1/admin/routes/selected", api_url); let response = client .get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// List all fallback routes in the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// /// # Returns /// /// * `Result` - The list of fallback routes as a JSON value, or an error message pub async fn list_fallback_routes(api_url: &str) -> Result { let client = Client::new(); let url = format!("{}/api/v1/admin/routes/fallback", api_url); let response = client .get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// Send a message to a destination via the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// * `destination` - The destination address /// * `topic` - The message topic /// * `message` - The message content /// * `reply_deadline` - The deadline in seconds; pass `-1` to indicate we do not want to wait on a reply /// /// # Returns /// /// * `Result` - The result of the operation as a JSON value, or an error message pub async fn send_message( api_url: &str, destination: &str, topic: &str, message: &str, reply_deadline: Option, // This is passed in URL query ) -> Result { let client = Client::new(); let url = format!("{}/api/v1/messages", api_url); let mut request = client.post(&url); if let Some(deadline) = reply_deadline { request = request.query(&[("reply_timeout", deadline.as_secs())]); } let response = request .json(&serde_json::json!({ "dst": { "ip": destination }, "topic": general_purpose::STANDARD.encode(topic), "payload": general_purpose::STANDARD.encode(message) })) .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) } /// Receive messages from a topic via the Mycelium node /// /// # Arguments /// /// * `api_url` - The URL of the Mycelium API /// * `topic` - The message topic /// * `wait_deadline` - Time we wait for receiving a message /// /// # Returns /// /// * `Result` - The received messages as a JSON value, or an error message pub async fn receive_messages( api_url: &str, topic: &str, wait_deadline: Option, ) -> Result { let client = Client::new(); let url = format!("{}/api/v1/messages", api_url); let mut request = client.get(&url); if let Some(deadline) = wait_deadline { request = request.query(&[ ("topic", general_purpose::STANDARD.encode(topic)), ("timeout", deadline.as_secs().to_string()), ]) } else { request = request.query(&[("topic", general_purpose::STANDARD.encode(topic))]) }; let response = request .send() .await .map_err(|e| format!("Failed to send request: {}", e))?; let status = response.status(); if !status.is_success() { return Err(format!("Request failed with status: {}", status)); } let result: Value = response .json() .await .map_err(|e| format!("Failed to parse response: {}", e))?; Ok(result) }