use serde::{Deserialize, Serialize}; use tauri::State; use crate::state::AppState; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RelayNodeDto { pub id: String, pub name: String, pub url: String, pub region: String, pub is_active: bool, pub priority: i32, pub last_ping_ms: Option, } fn auth_header(state: &AppState) -> Result { state .get_access_token() .map(|t| format!("Bearer {}", t)) .ok_or_else(|| "not authenticated".into()) } #[tauri::command] pub async fn list_relay_nodes(state: State<'_, AppState>) -> Result, String> { let token = auth_header(&state)?; let client = reqwest::Client::new(); let url = format!("{}/api/v1/relay/nodes", state.get_server_url()); let nodes = client .get(&url) .header("Authorization", token) .send() .await .map_err(|e| e.to_string())? .json::>() .await .map_err(|e| e.to_string())?; Ok(nodes) } #[tauri::command] pub async fn add_relay_node( name: String, url: String, region: Option, priority: Option, state: State<'_, AppState>, ) -> Result { let token = auth_header(&state)?; let client = reqwest::Client::new(); let api_url = format!("{}/api/v1/relay/nodes", state.get_server_url()); let resp = client .post(&api_url) .header("Authorization", token) .json(&serde_json::json!({ "name": name, "url": url, "region": region, "priority": priority })) .send() .await .map_err(|e| e.to_string())?; if !resp.status().is_success() { let err: serde_json::Value = resp.json().await.unwrap_or_default(); return Err(err["error"].as_str().unwrap_or("failed").to_string()); } let body: serde_json::Value = resp.json().await.map_err(|e| e.to_string())?; let node_id = body["id"].as_str().unwrap_or("").to_string(); // Return a DTO with what we know Ok(RelayNodeDto { id: node_id, name, url, region: region.unwrap_or_else(|| "auto".into()), is_active: true, priority: priority.unwrap_or(0), last_ping_ms: None, }) } #[tauri::command] pub async fn remove_relay_node( node_id: String, state: State<'_, AppState>, ) -> Result<(), String> { let token = auth_header(&state)?; let client = reqwest::Client::new(); let url = format!("{}/api/v1/relay/nodes/{}", state.get_server_url(), node_id); client .delete(&url) .header("Authorization", token) .send() .await .map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn report_relay_ping( node_id: String, ping_ms: i32, state: State<'_, AppState>, ) -> Result<(), String> { let token = auth_header(&state)?; let client = reqwest::Client::new(); let url = format!("{}/api/v1/relay/nodes/{}/ping", state.get_server_url(), node_id); client .post(&url) .header("Authorization", token) .json(&serde_json::json!({ "ping_ms": ping_ms })) .send() .await .map_err(|e| e.to_string())?; Ok(()) }