diff --git a/boltconn/src/cli/mod.rs b/boltconn/src/cli/mod.rs index 3fdcee4..abfe055 100644 --- a/boltconn/src/cli/mod.rs +++ b/boltconn/src/cli/mod.rs @@ -47,6 +47,9 @@ pub(crate) enum ConnOptions { /// Connection logs limit #[command(subcommand)] Limit(LogsLimitOptions), + /// Manage multiplexed master connections + #[command(subcommand)] + Master(MasterConnOptions), } #[derive(Debug, Clone, Copy, Subcommand)] @@ -222,10 +225,6 @@ pub(crate) enum SubCommand { Generate(GenerateOptions), #[cfg(feature = "internal-test")] #[clap(hide = true)] - #[command(subcommand)] - MasterConn(MasterConnOptions), - #[cfg(feature = "internal-test")] - #[clap(hide = true)] Internal, } @@ -364,6 +363,10 @@ pub(crate) async fn controller_main(args: ProgramArgs) -> ! { LogsLimitOptions::Set { limit } => requester.set_conn_log_limit(limit).await, LogsLimitOptions::Get => requester.get_conn_log_limit().await, }, + ConnOptions::Master(opt) => match opt { + MasterConnOptions::ListWg => requester.master_conn_stats().await, + MasterConnOptions::StopWg(opt) => requester.stop_master_conn(opt.name).await, + }, }, SubCommand::Tun(opt) => match opt { TunOptions::Get => requester.get_tun().await, @@ -392,11 +395,6 @@ pub(crate) async fn controller_main(args: ProgramArgs) -> ! { DnsOptions::Lookup { domain_name } => requester.real_lookup(domain_name).await, DnsOptions::Mapping { fake_ip } => requester.fake_ip_to_real(fake_ip).await, }, - #[cfg(feature = "internal-test")] - SubCommand::MasterConn(opt) => match opt { - MasterConnOptions::ListWg => requester.master_conn_stats().await, - MasterConnOptions::StopWg(opt) => requester.stop_master_conn(opt.name).await, - }, SubCommand::Start(_) | SubCommand::Generate(_) | SubCommand::Clean diff --git a/boltconn/src/cli/request.rs b/boltconn/src/cli/request.rs index c975925..170363d 100644 --- a/boltconn/src/cli/request.rs +++ b/boltconn/src/cli/request.rs @@ -308,36 +308,30 @@ impl Requester { } pub async fn master_conn_stats(&self) -> Result<()> { - match &self.inner { - Inner::Web(_) => Err(anyhow::anyhow!("conn-stats: Not supported by RESTful API")), - Inner::Uds(c) => { - let list = c.get_master_conn_stat().await?; - for entry in list { - let alive_str = |alive: bool| if alive { "alive" } else { "dead" }; - println!( - "{}:\t smol[{}, last active in {}s], wg=[{}, last handshake in {}s]", - entry.name, - alive_str(entry.alive), - entry.last_active, - alive_str(!entry.hand_shake_is_expired), - entry.last_handshake - ); - } - Ok(()) - } + let list = match &self.inner { + Inner::Web(c) => c.get_master_conn_stat().await?, + Inner::Uds(c) => c.get_master_conn_stat().await?, + }; + for entry in list { + let alive_str = |alive: bool| if alive { "alive" } else { "dead" }; + println!( + "{}:\t smol[{}, last active in {}s], wg=[{}, last handshake in {}s]", + entry.name, + alive_str(entry.alive), + entry.last_active, + alive_str(!entry.hand_shake_is_expired), + entry.last_handshake + ); } + Ok(()) } pub async fn stop_master_conn(&self, id: String) -> Result<()> { match &self.inner { - Inner::Web(_) => Err(anyhow::anyhow!( - "stop master conn: Not supported by RESTful API" - )), - Inner::Uds(c) => { - c.stop_master_conn(id).await?; - Ok(()) - } + Inner::Web(c) => c.stop_master_conn(id).await?, + Inner::Uds(c) => c.stop_master_conn(id).await?, } + Ok(()) } } diff --git a/boltconn/src/cli/request_web.rs b/boltconn/src/cli/request_web.rs index 225b7e1..2a119be 100644 --- a/boltconn/src/cli/request_web.rs +++ b/boltconn/src/cli/request_web.rs @@ -1,7 +1,7 @@ use anyhow::Result; use boltapi::{ ConnectionSchema, GetGroupRespSchema, GetInterceptDataResp, HttpInterceptSchema, - TunStatusSchema, + MasterConnectionStatus, TunStatusSchema, }; pub struct WebConnector { @@ -106,6 +106,23 @@ impl WebConnector { Ok(result) } + pub async fn get_master_conn_stat(&self) -> Result> { + let data = reqwest::get(self.route("/connections/master")) + .await? + .text() + .await?; + let result: Vec = serde_json::from_str(data.as_str())?; + Ok(result) + } + + pub async fn stop_master_conn(&self, id: String) -> Result<()> { + reqwest::Client::new() + .delete(self.route(format!("/connections/master/{}", id).as_str())) + .send() + .await?; + Ok(()) + } + pub async fn real_lookup(&self, domain: String) -> Result { let data = reqwest::get(self.route(format!("/dns/lookup/{}", domain).as_str())) .await? diff --git a/boltconn/src/external/web_controller.rs b/boltconn/src/external/web_controller.rs index 9089174..6faf514 100644 --- a/boltconn/src/external/web_controller.rs +++ b/boltconn/src/external/web_controller.rs @@ -73,6 +73,8 @@ impl WebController { get(Self::get_conn_log_limit).put(Self::set_conn_log_limit), ) .route("/reload", post(Self::reload)) + .route("/connections/master", get(Self::get_master_conn_stat)) + .route("/connections/master/:id", delete(Self::stop_master_conn)) .route_layer(map_request(wrapper)) .with_state(self); if let Some(origin) = parse_api_cors_origin(cors_allowed_list) { @@ -273,6 +275,21 @@ impl WebController { Json(serde_json::Value::Bool(true)) } + async fn get_master_conn_stat(State(server): State) -> Json { + Json(json!(server.controller.get_master_conn_stat().await)) + } + + async fn stop_master_conn( + State(server): State, + Path(params): Path>, + ) { + let id = { + let Some(id) = params.get("id") else { return }; + id.clone() + }; + server.controller.stop_master_conn(id).await + } + async fn fake_ip_to_real( State(server): State, Path(params): Path>,