From 005b321f6261891ab931391a3d6c75eff833718e Mon Sep 17 00:00:00 2001 From: KKRainbow <443152178@qq.com> Date: Fri, 16 Jan 2026 11:12:32 +0800 Subject: [PATCH] allow open rpc port in gui normal mode (#1795) * allow open rpc port for gui normal mode * downgrade dev tool console --- Cargo.lock | 1 + easytier-gui/package.json | 2 +- easytier-gui/src-tauri/Cargo.toml | 2 + easytier-gui/src-tauri/src/lib.rs | 113 +++++++++--- easytier-gui/src/components/ModeSwitcher.vue | 83 ++++++++- easytier-gui/src/composables/backend.ts | 4 +- easytier-gui/src/composables/mode.ts | 6 +- easytier-gui/src/pages/index.vue | 18 +- easytier-web/frontend-lib/src/locales/cn.yaml | 5 + easytier-web/frontend-lib/src/locales/en.yaml | 5 + pnpm-lock.yaml | 173 +++++++++--------- 11 files changed, 295 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a094f74a..f4c08192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2326,6 +2326,7 @@ dependencies = [ "tauri-plugin-vpnservice", "thunk-rs", "tokio", + "url", "uuid", "winapi", "windows 0.52.0", diff --git a/easytier-gui/package.json b/easytier-gui/package.json index b94e6e62..3a7c79a2 100644 --- a/easytier-gui/package.json +++ b/easytier-gui/package.json @@ -54,7 +54,7 @@ "unplugin-vue-router": "^0.10.8", "uuid": "^10.0.0", "vite": "^5.4.8", - "vite-plugin-vue-devtools": "^8.0.5", + "vite-plugin-vue-devtools": "^7.4.6", "vite-plugin-vue-layouts": "^0.11.0", "vue-i18n": "^10.0.0", "vue-tsc": "^2.1.10" diff --git a/easytier-gui/src-tauri/Cargo.toml b/easytier-gui/src-tauri/Cargo.toml index 015cb5b6..7dab68b9 100644 --- a/easytier-gui/src-tauri/Cargo.toml +++ b/easytier-gui/src-tauri/Cargo.toml @@ -57,6 +57,8 @@ tauri-plugin-os = "2.3.0" uuid = "1.17.0" async-trait = "0.1.89" +url = { version = "2.5", features = ["serde"] } + [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.52", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } winapi = { version = "0.3.9", features = ["securitybaseapi", "processthreadsapi"] } diff --git a/easytier-gui/src-tauri/src/lib.rs b/easytier-gui/src-tauri/src/lib.rs index 1b1a7eaf..702def27 100644 --- a/easytier-gui/src-tauri/src/lib.rs +++ b/easytier-gui/src-tauri/src/lib.rs @@ -19,11 +19,13 @@ use easytier::{ launcher::NetworkConfig, rpc_service::ApiRpcServer, tunnel::ring::RingTunnelListener, + tunnel::tcp::TcpTunnelListener, + tunnel::TunnelListener, utils::{self}, }; use std::ops::Deref; use std::sync::Arc; -use tokio::sync::{RwLock, RwLockReadGuard}; +use tokio::sync::{Mutex, RwLock, RwLockReadGuard}; use uuid::Uuid; use tauri::{AppHandle, Emitter, Manager as _}; @@ -40,8 +42,21 @@ static RPC_RING_UUID: once_cell::sync::Lazy = static CLIENT_MANAGER: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| RwLock::new(None)); -static RING_RPC_SERVER: once_cell::sync::Lazy>>> = - once_cell::sync::Lazy::new(|| RwLock::new(None)); +type BoxedTunnelListener = Box; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum RpcServerKind { + Ring, + Tcp, +} + +struct RpcServer { + kind: RpcServerKind, + _server: ApiRpcServer, + bind_url: Option, +} +static RPC_SERVER: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(|| Mutex::new(None)); static WEB_CLIENT: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| RwLock::new(None)); @@ -322,8 +337,25 @@ fn get_service_status() -> Result<&'static str, String> { } } +fn normalize_normal_mode_rpc_portal(portal: &str) -> Result<(url::Url, url::Url), String> { + let portal_url: url::Url = portal + .parse() + .map_err(|e| format!("invalid rpc portal: {:#}", e))?; + let bind_url = portal_url.clone(); + let mut connect_url = portal_url.clone(); + // if bind addr is 0.0.0.0, should convert to 127.0.0.1 + if connect_url.host_str() == Some("0.0.0.0") { + connect_url.set_host(Some("127.0.0.1")).unwrap(); + } + Ok((bind_url, connect_url)) +} + #[tauri::command] -async fn init_rpc_connection(_app: AppHandle, url: Option) -> Result<(), String> { +async fn init_rpc_connection( + _app: AppHandle, + is_normal_mode: bool, + url: Option, +) -> Result<(), String> { let mut client_manager_guard = tokio::time::timeout(std::time::Duration::from_secs(5), CLIENT_MANAGER.write()) .await @@ -331,41 +363,72 @@ async fn init_rpc_connection(_app: AppHandle, url: Option) -> Result<(), let mut instance_manager_guard = INSTANCE_MANAGER .try_write() .map_err(|_| "Failed to acquire write lock for instance manager")?; - let mut ring_rpc_server_guard = RING_RPC_SERVER - .try_write() - .map_err(|_| "Failed to acquire write lock for ring rpc server")?; + let mut rpc_server_guard = RPC_SERVER + .try_lock() + .map_err(|_| "Failed to acquire lock for rpc server")?; - let normal_mode = url.is_none(); - if normal_mode { + let mut client_url = url.clone(); + if is_normal_mode { let instance_manager = if let Some(im) = instance_manager_guard.take() { im } else { Arc::new(NetworkInstanceManager::new()) }; - let rpc_server = if let Some(rpc_server) = ring_rpc_server_guard.take() { - rpc_server + + let portal = url.and_then(|s| { + let trimmed = s.trim().to_string(); + if trimmed.is_empty() { + None + } else { + Some(trimmed) + } + }); + + let (desired_kind, bind_url, connect_url) = if let Some(portal) = portal { + let (bind_url, connect_url) = normalize_normal_mode_rpc_portal(&portal)?; + (RpcServerKind::Tcp, Some(bind_url), Some(connect_url)) } else { - ApiRpcServer::from_tunnel( - RingTunnelListener::new( - format!("ring://{}", RPC_RING_UUID.deref()).parse().unwrap(), - ), - instance_manager.clone(), - ) - .with_rx_timeout(None) - .serve() - .await - .map_err(|e| e.to_string())? + (RpcServerKind::Ring, None, None) }; + let need_restart = rpc_server_guard + .as_ref() + .map(|x| x.kind != desired_kind || x.bind_url != bind_url) + .unwrap_or(true); + + if need_restart { + *rpc_server_guard = None; + + let tunnel: BoxedTunnelListener = match desired_kind { + RpcServerKind::Ring => Box::new(RingTunnelListener::new( + format!("ring://{}", RPC_RING_UUID.deref()).parse().unwrap(), + )), + RpcServerKind::Tcp => Box::new(TcpTunnelListener::new( + bind_url.clone().expect("tcp rpc must have bind url"), + )), + }; + + let rpc_server = ApiRpcServer::from_tunnel(tunnel, instance_manager.clone()) + .with_rx_timeout(None) + .serve() + .await + .map_err(|e| e.to_string())?; + *rpc_server_guard = Some(RpcServer { + kind: desired_kind, + _server: rpc_server, + bind_url, + }); + } + *instance_manager_guard = Some(instance_manager); - *ring_rpc_server_guard = Some(rpc_server); + client_url = connect_url.map(|u| u.to_string()); } else { - *ring_rpc_server_guard = None; + *rpc_server_guard = None; } let client_manager = tokio::time::timeout( std::time::Duration::from_millis(1000), - manager::GUIClientManager::new(url), + manager::GUIClientManager::new(client_url), ) .await .map_err(|_| "connect remote rpc timed out".to_string())? @@ -373,7 +436,7 @@ async fn init_rpc_connection(_app: AppHandle, url: Option) -> Result<(), .map_err(|e| format!("{:#}", e))?; *client_manager_guard = Some(client_manager); - if !normal_mode { + if !is_normal_mode { drop(WEB_CLIENT.write().await.take()); if let Some(instance_manager) = instance_manager_guard.take() { instance_manager diff --git a/easytier-gui/src/components/ModeSwitcher.vue b/easytier-gui/src/components/ModeSwitcher.vue index 67f73261..0bbf33a7 100644 --- a/easytier-gui/src/components/ModeSwitcher.vue +++ b/easytier-gui/src/components/ModeSwitcher.vue @@ -1,6 +1,6 @@