Skip to content

Commit

Permalink
feat(backend)!: add tray proxies selector support (#417)
Browse files Browse the repository at this point in the history
* feat(backend): provide a proxies endpoint test

* chore: update deps

* feat: first impl

* chore: update deps

* fix: confilict

* fix: lint
  • Loading branch information
greenhat616 authored Feb 13, 2024
1 parent f7296db commit 17f9319
Show file tree
Hide file tree
Showing 21 changed files with 756 additions and 115 deletions.
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

0 comments on commit 17f9319

Please sign in to comment.