use reqwest::{Method, header::HeaderMap}; use serde_json::json; use std::{ sync::{Arc, Mutex}, time::Duration, }; pub mod model; pub use model::{MihomoData, MihomoManager}; impl MihomoManager { pub fn new(mihomo_server: String, headers: HeaderMap) -> Self { Self { mihomo_server, data: Arc::new(Mutex::new(MihomoData { proxies: serde_json::Value::Null, providers_proxies: serde_json::Value::Null, })), headers, } } fn update_proxies(&self, proxies: serde_json::Value) { let mut data = self.data.lock().unwrap(); data.proxies = proxies; } fn update_providers_proxies(&self, providers_proxies: serde_json::Value) { let mut data = self.data.lock().unwrap(); data.providers_proxies = providers_proxies; } pub fn get_mihomo_server(&self) -> String { self.mihomo_server.clone() } pub fn get_proxies(&self) -> serde_json::Value { let data = self.data.lock().unwrap(); data.proxies.clone() } pub fn get_providers_proxies(&self) -> serde_json::Value { let data = self.data.lock().unwrap(); data.providers_proxies.clone() } async fn send_request( &self, method: Method, url: String, data: Option, ) -> Result { let client_response = reqwest::ClientBuilder::new() .default_headers(self.headers.clone()) .no_proxy() .timeout(Duration::from_secs(60)) .build() .map_err(|e| e.to_string())? .request(method.clone(), &url) .json(&data.unwrap_or(json!({}))) .send() .await .map_err(|e| e.to_string())?; let response = match method { Method::PATCH => { let status = client_response.status(); if status.as_u16() == 204 { json!({"code": 204}) } else { client_response .json::() .await .map_err(|e| e.to_string())? } } Method::PUT => json!(client_response.text().await.map_err(|e| e.to_string())?), _ => client_response .json::() .await .map_err(|e| e.to_string())?, }; Ok(response) } pub async fn refresh_proxies(&self) -> Result<&Self, String> { let url = format!("{}/proxies", self.mihomo_server); let proxies = self.send_request(Method::GET, url, None).await?; self.update_proxies(proxies); Ok(self) } pub async fn refresh_providers_proxies(&self) -> Result<&Self, String> { let url = format!("{}/providers/proxies", self.mihomo_server); let providers_proxies = self.send_request(Method::GET, url, None).await?; self.update_providers_proxies(providers_proxies); Ok(self) } } impl MihomoManager { pub async fn is_mihomo_running(&self) -> Result<(), String> { let url = format!("{}/version", self.mihomo_server); let _response = self.send_request(Method::GET, url, None).await?; Ok(()) } pub async fn put_configs_force(&self, clash_config_path: &str) -> Result<(), String> { let url = format!("{}/configs?force=true", self.mihomo_server); let payload = serde_json::json!({ "path": clash_config_path, }); let _response = self.send_request(Method::PUT, url, Some(payload)).await?; Ok(()) } pub async fn patch_configs(&self, config: serde_json::Value) -> Result<(), String> { let url = format!("{}/configs", self.mihomo_server); let response = self.send_request(Method::PATCH, url, Some(config)).await?; if response["code"] == 204 { Ok(()) } else { Err(response["message"] .as_str() .unwrap_or("unknown error") .to_string()) } } pub async fn test_proxy_delay( &self, name: &str, test_url: Option, timeout: i32, ) -> Result { let test_url = test_url.unwrap_or("http://cp.cloudflare.com/generate_204".to_string()); let url = format!( "{}/proxies/{}/delay?url={}&timeout={}", self.mihomo_server, name, test_url, timeout ); let response = self.send_request(Method::GET, url, None).await?; Ok(response) } }