feat(proxy): add proxy commands and integrate with API

Add new proxy.rs module with get_proxies and get_providers_proxies commands.
Update mod.rs and lib.rs to re-export and register proxy commands.
Update API.ts to use invoke for proxy commands.
Minor formatting improvements in module/mihomo.rs.
This commit is contained in:
Tunglies 2025-03-04 01:01:24 +08:00
parent 3b69465016
commit 1ba688727e
5 changed files with 81 additions and 29 deletions

View File

@ -15,6 +15,7 @@ pub mod verge;
pub mod runtime; pub mod runtime;
pub mod save_profile; pub mod save_profile;
pub mod system; pub mod system;
pub mod proxy;
// Re-export all command functions for backwards compatibility // Re-export all command functions for backwards compatibility
pub use profile::*; pub use profile::*;
@ -27,4 +28,5 @@ pub use clash::*;
pub use verge::*; pub use verge::*;
pub use runtime::*; pub use runtime::*;
pub use save_profile::*; pub use save_profile::*;
pub use system::*; pub use system::*;
pub use proxy::*;

View File

@ -0,0 +1,35 @@
use super::CmdResult;
use crate::module::mihomo::MihomoManager;
use tauri::async_runtime;
#[tauri::command]
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
let proxies = async_runtime::spawn_blocking(|| {
let rt = tokio::runtime::Runtime::new().unwrap();
let manager = MihomoManager::new();
{
let mut write_guard = manager.write();
rt.block_on(write_guard.refresh_proxies());
}
let read_guard = manager.read();
read_guard.fetch_proxies().clone()
})
.await.map_err(|e| e.to_string())?;
Ok(proxies)
}
#[tauri::command]
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
let providers_proxies = async_runtime::spawn_blocking(|| {
let rt = tokio::runtime::Runtime::new().unwrap();
let manager = MihomoManager::new();
{
let mut write_guard = manager.write();
rt.block_on(write_guard.refresh_providers_proxies());
}
let read_guard = manager.read();
read_guard.fetch_providers_proxies().clone()
})
.await.map_err(|e| e.to_string())?;
Ok(providers_proxies)
}

View File

@ -162,6 +162,8 @@ pub fn run() {
cmd::get_runtime_logs, cmd::get_runtime_logs,
cmd::invoke_uwp_tool, cmd::invoke_uwp_tool,
cmd::copy_clash_env, cmd::copy_clash_env,
cmd::get_proxies,
cmd::get_providers_proxies,
// verge // verge
cmd::get_verge_config, cmd::get_verge_config,
cmd::patch_verge_config, cmd::patch_verge_config,

View File

@ -1,8 +1,7 @@
use std::sync::Arc; use crate::model::api::mihomo::MihomoAPICaller;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::RwLock; use parking_lot::RwLock;
use crate::model::api::mihomo::MihomoAPICaller; use std::sync::Arc;
#[allow(unused)] #[allow(unused)]
pub struct MihomoManager { pub struct MihomoManager {
@ -34,20 +33,27 @@ impl MihomoManager {
pub async fn refresh_proxies(&mut self) { pub async fn refresh_proxies(&mut self) {
match MihomoAPICaller::get_proxies().await { match MihomoAPICaller::get_proxies().await {
Ok(proxies) => self.proxies = proxies, Ok(proxies) => {
Err(e) => log::error!("Failed to get proxies: {}", e), self.proxies = proxies;
}
Err(e) => {
log::error!("Failed to get proxies: {}", e);
}
} }
} }
pub async fn refresh_providers_proxies(&mut self) { pub async fn refresh_providers_proxies(&mut self) {
match MihomoAPICaller::get_providers_proxies().await { match MihomoAPICaller::get_providers_proxies().await {
Ok(providers_proxies) => self.providers_proxies = providers_proxies, Ok(providers_proxies) => {
Err(e) => log::error!("Failed to get providers proxies: {}", e), self.providers_proxies = providers_proxies;
},
Err(e) => {
log::error!("Failed to get providers proxies: {}", e);
},
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -55,18 +61,21 @@ mod tests {
async fn test_mihomo_manager_singleton() { async fn test_mihomo_manager_singleton() {
let manager1 = MihomoManager::new(); let manager1 = MihomoManager::new();
let manager2 = MihomoManager::new(); let manager2 = MihomoManager::new();
assert!(Arc::ptr_eq(&manager1, &manager2), "Should return same instance"); assert!(
Arc::ptr_eq(&manager1, &manager2),
"Should return same instance"
);
let manager = manager1.read(); let manager = manager1.read();
assert!(manager.proxies.is_null()); assert!(manager.proxies.is_null());
assert!(manager.providers_proxies.is_null()); assert!(manager.providers_proxies.is_null());
} }
#[tokio::test] #[tokio::test]
async fn test_refresh_proxies() { async fn test_refresh_proxies() {
let manager = MihomoManager::new(); let manager = MihomoManager::new();
// Test initial state // Test initial state
{ {
let data = manager.read(); let data = manager.read();
@ -87,7 +96,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_refresh_providers_proxies() { async fn test_refresh_providers_proxies() {
let manager = MihomoManager::new(); let manager = MihomoManager::new();
// Test initial state // Test initial state
{ {
let data = manager.read(); let data = manager.read();
@ -108,7 +117,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_fetch_proxies() { async fn test_fetch_proxies() {
let manager = MihomoManager::new(); let manager = MihomoManager::new();
// Test initial state // Test initial state
{ {
let data = manager.read(); let data = manager.read();
@ -129,7 +138,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_fetch_providers_proxies() { async fn test_fetch_providers_proxies() {
let manager = MihomoManager::new(); let manager = MihomoManager::new();
// Test initial state // Test initial state
{ {
let data = manager.read(); let data = manager.read();
@ -146,4 +155,4 @@ mod tests {
// Would need API mocking for more thorough testing // Would need API mocking for more thorough testing
} }
} }
} }

View File

@ -1,5 +1,7 @@
import axios, { AxiosInstance } from "axios"; import axios, { AxiosInstance } from "axios";
import { getClashInfo } from "./cmds"; import { getClashInfo } from "./cmds";
import { invoke } from "@tauri-apps/api/core";
import { useLockFn } from "ahooks";
let axiosIns: AxiosInstance = null!; let axiosIns: AxiosInstance = null!;
@ -94,13 +96,18 @@ export const updateProxy = async (group: string, proxy: string) => {
// get proxy // get proxy
export const getProxiesInner = async () => { export const getProxiesInner = async () => {
const instance = await getAxios(); const response = await invoke<Record<string, IProxyItem>>("get_proxies");
const response = await instance.get<any, any>("/proxies"); return response.proxies;
return (response?.proxies || {}) as Record<string, IProxyItem>;
}; };
/// Get the Proxy information /// Get the Proxy information
export const getProxies = async () => { export const getProxies = async (): Promise<{
global: IProxyGroupItem;
direct: IProxyItem;
groups: IProxyGroupItem[];
records: Record<string, IProxyItem>;
proxies: IProxyItem[];
}> => {
const [proxyRecord, providerRecord] = await Promise.all([ const [proxyRecord, providerRecord] = await Promise.all([
getProxiesInner(), getProxiesInner(),
getProxyProviders(), getProxyProviders(),
@ -181,13 +188,10 @@ export const getProxies = async () => {
// get proxy providers // get proxy providers
export const getProxyProviders = async () => { export const getProxyProviders = async () => {
const instance = await getAxios(); const response = await invoke<Record<string, IProxyProviderItem>>(
const response = await instance.get<any, any>("/providers/proxies"); "get_providers_proxies",
);
const providers = (response.providers || {}) as Record< const providers = response.providers;
string,
IProxyProviderItem
>;
return Object.fromEntries( return Object.fromEntries(
Object.entries(providers).filter(([key, item]) => { Object.entries(providers).filter(([key, item]) => {