diff --git a/clash-lib/src/app/api/handlers/config.rs b/clash-lib/src/app/api/handlers/config.rs index 497755d69..fe74d62e1 100644 --- a/clash-lib/src/app/api/handlers/config.rs +++ b/clash-lib/src/app/api/handlers/config.rs @@ -57,7 +57,10 @@ pub fn routes( Router::new() .route( "/", - get(get_configs).put(update_configs).patch(patch_configs), + get(get_configs) + .post(get_configs) + .put(update_configs) + .patch(patch_configs), ) .with_state(ConfigState { inbound_manager, diff --git a/clash-lib/src/app/api/handlers/group.rs b/clash-lib/src/app/api/handlers/group.rs index f0eb1ca4b..d4fee88b9 100644 --- a/clash-lib/src/app/api/handlers/group.rs +++ b/clash-lib/src/app/api/handlers/group.rs @@ -31,9 +31,11 @@ pub struct GroupState { pub fn routes(outbound_manager: ThreadSafeOutboundManager) -> Router> { let state = GroupState { outbound_manager }; Router::new() + .route("/", get(get_groups)) .nest( "/{name}", Router::new() + .route("/", get(get_group)) .route("/delay", get(get_group_delay)) .route_layer(middleware::from_fn_with_state( state.clone(), @@ -44,6 +46,29 @@ pub fn routes(outbound_manager: ThreadSafeOutboundManager) -> Router) -> impl IntoResponse { + let outbound_manager = state.outbound_manager.clone(); + let mut res = HashMap::new(); + let groups = outbound_manager.get_groups().await; + res.insert("proxies".to_owned(), groups); + Json(res) +} + +async fn get_group( + Extension(proxy): Extension, + State(state): State, +) -> impl IntoResponse { + if proxy.try_as_group_handler().is_none() { + return ( + StatusCode::NOT_FOUND, + format!("proxy {} is not a group", proxy.name()), + ) + .into_response(); + } + let outbound_manager = state.outbound_manager.clone(); + Json(outbound_manager.get_proxy(&proxy).await).into_response() +} + async fn find_group_by_name( State(state): State, Path(name): Path, diff --git a/clash-lib/src/app/api/handlers/provider.rs b/clash-lib/src/app/api/handlers/provider.rs index 2efc86d4f..d31082cf2 100644 --- a/clash-lib/src/app/api/handlers/provider.rs +++ b/clash-lib/src/app/api/handlers/provider.rs @@ -12,7 +12,7 @@ use axum::{ http::{Request, StatusCode}, middleware::{self, Next}, response::{IntoResponse, Response}, - routing::get, + routing::{any, get}, }; use serde::{Deserialize, Serialize}; @@ -33,7 +33,7 @@ struct ProviderState { pub fn routes(outbound_manager: ThreadSafeOutboundManager) -> Router> { let state = ProviderState { outbound_manager }; Router::new() - .route("/", get(get_providers)) + .route("/", any(get_providers)) .nest( "/{provider_name}", Router::new() diff --git a/clash-lib/src/app/api/handlers/proxy.rs b/clash-lib/src/app/api/handlers/proxy.rs index 0d278af30..e1561f831 100644 --- a/clash-lib/src/app/api/handlers/proxy.rs +++ b/clash-lib/src/app/api/handlers/proxy.rs @@ -6,7 +6,7 @@ use axum::{ http::Request, middleware::{self, Next}, response::{IntoResponse, Response}, - routing::get, + routing::{any, get}, }; use http::{HeaderMap, StatusCode, header}; @@ -39,7 +39,7 @@ pub fn routes( cache_store, }; Router::new() - .route("/", get(get_proxies)) + .route("/", any(get_proxies)) .nest( "/{name}", Router::new() diff --git a/clash-lib/src/app/outbound/manager.rs b/clash-lib/src/app/outbound/manager.rs index 34373a50b..24ae9d77b 100644 --- a/clash-lib/src/app/outbound/manager.rs +++ b/clash-lib/src/app/outbound/manager.rs @@ -199,6 +199,30 @@ impl OutboundManager { r } + /// Get only group proxies (Selector, URLTest, Fallback, LoadBalance, Relay, + /// Smart). + pub async fn get_groups(&self) -> HashMap> { + let mut r = HashMap::new(); + + let handlers: Vec<(String, AnyOutboundHandler)> = self + .registry + .read() + .await + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + + for (k, v) in handlers { + if let Some(g) = v.try_as_group_handler() { + let mut m = g.as_map().await; + self.apply_common_proxy_fields(&mut m, &v, &k).await; + r.insert(k.clone(), Box::new(m) as _); + } + } + + r + } + pub async fn get_proxy( &self, proxy: &AnyOutboundHandler, diff --git a/clash-lib/src/common/utils.rs b/clash-lib/src/common/utils.rs index e44331b44..4559268a4 100644 --- a/clash-lib/src/common/utils.rs +++ b/clash-lib/src/common/utils.rs @@ -94,7 +94,9 @@ pub fn serialize_duration( where S: serde::Serializer, { - serializer.serialize_u128(duration.as_millis()) + let millis = duration.as_millis(); + let capped = u16::try_from(millis).unwrap_or(u16::MAX); + serializer.serialize_u16(capped) } pub async fn download

( diff --git a/clash-lib/src/proxy/mod.rs b/clash-lib/src/proxy/mod.rs index 1335b7d10..83b125d22 100644 --- a/clash-lib/src/proxy/mod.rs +++ b/clash-lib/src/proxy/mod.rs @@ -163,7 +163,7 @@ impl Display for OutboundType { OutboundType::Tuic => write!(f, "Tuic"), OutboundType::Socks5 => write!(f, "Socks5"), OutboundType::Hysteria2 => write!(f, "Hysteria2"), - OutboundType::Ssh => write!(f, "ssh"), + OutboundType::Ssh => write!(f, "Ssh"), OutboundType::Tailscale => write!(f, "Tailscale"), OutboundType::ShadowQuic => write!(f, "ShadowQuic"), diff --git a/justfile b/justfile index d2b55043d..ee8a9d82f 100644 --- a/justfile +++ b/justfile @@ -17,9 +17,9 @@ test-no-docker: CLASH_RS_CI=true cargo test --all --all-features verge-win verge_path='C:\Apps\Clash Verge\verge-mihomo-alpha.exe': - cargo build -p clash-rs --release --features=standard + cargo build -p clash-rs --profile detailed-release --features=standard rm -f "{{verge_path}}" cp target/release/clash-rs.exe "{{verge_path}}" -test-api token='test_token' url='http://127.0.0.1:9093/proxies': +test-api token='test_token' url='http://127.0.0.1:9090/group': curl -H "Authorization: Bearer {{token}}" {{url}}