Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backend)!: add tray proxies selector support #417

Merged
merged 7 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 99 additions & 55 deletions backend/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions backend/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ rocksdb = "0.21"
thiserror = { workspace = true }
simd-json = "0.13.8"
runas = "=1.0.0" # blocked by https://github.com/mitsuhiko/rust-runas/issues/13
backon = "0.4.1"
rust-i18n = "3"

[target.'cfg(windows)'.dependencies]
Expand Down
36 changes: 35 additions & 1 deletion backend/tauri/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {

#[tauri::command]
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
wrap_err!(feat::patch_clash(payload).await)
wrap_err!(feat::patch_clash(payload).await)?;
feat::update_proxies_buff(None);
Ok(())
}

#[tauri::command]
Expand Down Expand Up @@ -324,6 +326,38 @@ pub async fn clash_api_get_proxy_delay(
}
}

#[tauri::command]
pub async fn get_proxies() -> CmdResult<crate::core::clash::proxies::Proxies> {
use crate::core::clash::proxies::ProxiesGuard;
use crate::core::clash::proxies::ProxiesGuardExt;
match ProxiesGuard::global().update().await {
Ok(_) => {
let proxies = ProxiesGuard::global().read().inner().clone();
Ok(proxies)
}
Err(err) => Err(err.to_string()),
}
}

#[tauri::command]
pub async fn select_proxy(group: String, name: String) -> CmdResult<()> {
use crate::core::clash::proxies::ProxiesGuard;
use crate::core::clash::proxies::ProxiesGuardExt;
wrap_err!(ProxiesGuard::global().select_proxy(&group, &name).await)?;
Ok(())
}

#[tauri::command]
pub async fn update_proxy_provider(name: String) -> CmdResult<()> {
use crate::core::clash::{
api,
proxies::{ProxiesGuard, ProxiesGuardExt},
};
wrap_err!(api::update_providers_proxies_group(&name).await)?;
wrap_err!(ProxiesGuard::global().update().await)?;
Ok(())
}

#[cfg(windows)]
pub mod uwp {
use super::*;
Expand Down
81 changes: 69 additions & 12 deletions backend/tauri/src/core/clash/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use anyhow::{bail, Result};
use reqwest::header::HeaderMap;
use serde::{Deserialize, Serialize};
use serde_yaml::Mapping;
use std::collections::HashMap;
use std::{
collections::HashMap,
fmt::{self, Display, Formatter},
};

/// PUT /configs
/// path 是绝对路径
Expand Down Expand Up @@ -43,14 +46,14 @@ pub struct ProxiesRes {
pub proxies: Option<HashMap<String, ProxyItem>>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ProxyItemHistory {
pub time: String,
pub delay: i64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ProxyItem {
pub name: String,
Expand All @@ -60,13 +63,52 @@ pub struct ProxyItem {
pub all: Option<Vec<String>>,
pub now: Option<String>, // 当前选中的代理
pub provider: Option<String>,
pub alive: Option<bool>, // Mihomo Or Premium Only
pub xudp: Option<bool>, // Mihomo Only
pub tfo: Option<bool>, // Mihomo Only
pub alive: Option<bool>, // Mihomo Or Premium Only
#[serde(skip_serializing_if = "Option::is_none")]
pub xudp: Option<bool>, // Mihomo Only
#[serde(skip_serializing_if = "Option::is_none")]
pub tfo: Option<bool>, // Mihomo Only
#[serde(skip_serializing_if = "Option::is_none")]
pub icon: Option<String>, // Mihomo Only
#[serde(default)]
pub hidden: bool, // Mihomo Only
// extra: {}, // Mihomo Only
// extra: {}, // Mihomo Only
}

impl From<ProxyProviderItem> for ProxyItem {
fn from(item: ProxyProviderItem) -> Self {
let ProxyProviderItem {
name,
r#type,
proxies,
vehicle_type: _,
test_url: _,
expected_status: _,
} = item;

let now = proxies
.iter()
.find(|p| p.now.is_some())
.map(|p| p.name.clone())
.unwrap_or_default();

let all = proxies.iter().map(|p| p.name.clone()).collect();

Self {
name,
r#type: r#type.to_string(),
udp: false,
history: vec![],
all: Some(all),
now: Some(now),
provider: None,
alive: None,
xudp: None,
tfo: None,
icon: None,
hidden: false,
}
}
}

/// GET /proxies
Expand All @@ -87,6 +129,7 @@ pub async fn get_proxies() -> Result<ProxiesRes> {
/// name: 代理名称
/// 返回代理的配置
///
#[allow(dead_code)]
pub async fn get_proxy(name: String) -> Result<ProxyItem> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/proxies/{name}");
Expand All @@ -102,7 +145,7 @@ pub async fn get_proxy(name: String) -> Result<ProxyItem> {
/// 选择代理
/// group: 代理分组名称
/// name: 代理名称
pub async fn update_proxy(group: String, name: String) -> Result<()> {
pub async fn update_proxy(group: &str, name: &str) -> Result<()> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/proxies/{group}");

Expand Down Expand Up @@ -137,14 +180,26 @@ pub enum ProviderType {
Unknown,
}

impl Display for ProviderType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ProviderType::Proxy => write!(f, "Proxy"),
ProviderType::Rule => write!(f, "Rule"),
ProviderType::Unknown => write!(f, "Unknown"),
}
}
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProxyProviderItem {
pub name: String,
pub r#type: ProviderType,
pub proxies: Vec<ProxyItem>,
pub vehicle_type: VehicleType,
pub test_url: Option<String>, // Mihomo Only
#[serde(skip_serializing_if = "Option::is_none")]
pub test_url: Option<String>, // Mihomo Only
#[serde(skip_serializing_if = "Option::is_none")]
pub expected_status: Option<String>, // Mihomo Only
}

Expand All @@ -156,20 +211,21 @@ pub struct ProvidersProxiesRes {

/// GET /providers/proxies
/// 获取所有代理集合的所有代理信息
pub async fn get_providers_proxies() -> Result<Mapping> {
pub async fn get_providers_proxies() -> Result<ProvidersProxiesRes> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/providers/proxies");

let client = reqwest::ClientBuilder::new().no_proxy().build()?;
let builder = client.get(&url).headers(headers);
let response = builder.send().await?;

Ok(response.json::<Mapping>().await?)
Ok(response.json::<ProvidersProxiesRes>().await?)
}

/// GET /providers/proxies/:name
/// 获取单个代理集合的所有代理信息
/// group: 代理集合名称
#[allow(dead_code)]
pub async fn get_providers_proxies_group(group: String) -> Result<ProxyProviderItem> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/providers/proxies/{group}");
Expand All @@ -184,7 +240,7 @@ pub async fn get_providers_proxies_group(group: String) -> Result<ProxyProviderI
/// PUT /providers/proxies/:name
/// 更新代理集合
/// name: 代理集合名称
pub async fn update_providers_proxies_group(name: String) -> Result<()> {
pub async fn update_providers_proxies_group(name: &str) -> Result<()> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/providers/proxies/{name}");

Expand All @@ -203,6 +259,7 @@ pub async fn update_providers_proxies_group(name: String) -> Result<()> {
/// GET /providers/proxies/:name/healthcheck
/// 获取代理集合的健康检查
/// name: 代理集合名称
#[allow(dead_code)]
pub async fn get_providers_proxies_healthcheck(name: String) -> Result<Mapping> {
let (url, headers) = clash_client_info()?;
let url = format!("{url}/providers/proxies/{name}/healthcheck");
Expand Down
10 changes: 10 additions & 0 deletions backend/tauri/src/core/clash/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
use backon::ExponentialBuilder;
use once_cell::sync::Lazy;
pub mod api;
pub mod core;
pub mod proxies;

pub static CLASH_API_DEFAULT_BACKOFF_STRATEGY: Lazy<ExponentialBuilder> = Lazy::new(|| {
ExponentialBuilder::default()
.with_min_delay(std::time::Duration::from_millis(50))
.with_max_delay(std::time::Duration::from_secs(5))
.with_max_times(5)
});
Loading
Loading