From e77bb2a5ecdd9f11a70938b5b206dc0ea4397897 Mon Sep 17 00:00:00 2001 From: lwmacct Date: Sun, 11 Jan 2026 23:54:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B0=86=20HTTP=20=E6=A1=A5=E6=8E=A5?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=8B=AC=E7=AB=8B=E7=9A=84=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=EF=BC=88=E7=AB=AF=E5=8F=A3=203030?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 之前将 dev_bridge 路由合并到主应用服务器(端口 8999), 但用户需要一个独立的开发桥接端口,并且服务器启动后会立即关闭。 ## 变更 - 重构 dev_bridge.rs 为独立的服务器实现 - 服务器在后台持续运行(不使用 graceful_shutdown) - 在服务器启动时同步启动 dev bridge 服务器 - 添加 get_network_info 命令支持 - 修复 get_default_provider 返回格式(返回字符串而非对象) ## 架构 - 端口 8999: 主应用 API(生产/通用) - 端口 3030: 专用开发桥接(仅 dev 模式) ## 验证 - [DevBridge] HTTP 桥接成功: get_server_status - [DevBridge] HTTP 桥接成功: get_config - [DevBridge] HTTP 桥接成功: get_default_provider - [DevBridge] HTTP 桥接成功: get_network_info --- src-tauri/src/dev_bridge.rs | 103 +++++++++++++++++++++---- src-tauri/src/dev_bridge/dispatcher.rs | 13 +++- src-tauri/src/server/mod.rs | 25 +++++- 3 files changed, 123 insertions(+), 18 deletions(-) diff --git a/src-tauri/src/dev_bridge.rs b/src-tauri/src/dev_bridge.rs index 7ee575159..6cfa4d591 100644 --- a/src-tauri/src/dev_bridge.rs +++ b/src-tauri/src/dev_bridge.rs @@ -1,6 +1,8 @@ //! HTTP 桥接模块 //! //! 仅在开发模式下启用,允许浏览器 dev server 通过 HTTP 调用 Tauri 命令。 +//! +//! 这是一个独立的开发服务器,运行在 3030 端口,与主应用服务器(8999)分离。 #[cfg(debug_assertions)] pub mod dispatcher; @@ -13,12 +15,18 @@ use axum::{ routing::post, Json, Router, }; +#[cfg(debug_assertions)] use serde::{Deserialize, Serialize}; +#[cfg(debug_assertions)] +use std::sync::Arc; +#[cfg(debug_assertions)] +use tokio::sync::RwLock; +#[cfg(debug_assertions)] use tower_http::cors::CorsLayer; -// 使用 Axum 服务器的 AppState use crate::server::AppState; +#[cfg(debug_assertions)] #[derive(Debug, Deserialize)] pub struct InvokeRequest { pub cmd: String, @@ -26,28 +34,86 @@ pub struct InvokeRequest { pub args: Option, } +#[cfg(debug_assertions)] #[derive(Debug, Serialize)] pub struct InvokeResponse { pub result: Option, pub error: Option, } -/// 创建 HTTP 桥接路由 -/// -/// 仅在 dev 模式下被添加到 Axum 服务器 -pub fn dev_bridge_routes() -> Router { - Router::new().route("/invoke", post(invoke_command)).layer( - // CORS 配置 - 允许 localhost:1420 访问 - CorsLayer::new() - .allow_origin("http://localhost:1420".parse::().unwrap()) - .allow_methods([axum::http::Method::POST]) - .allow_headers([axum::http::header::CONTENT_TYPE]), - ) +/// 开发桥接服务器配置 +#[cfg(debug_assertions)] +pub struct DevBridgeConfig { + /// 监听地址 + pub host: String, + /// 监听端口 + pub port: u16, } -async fn invoke_command(State(state): State, Json(req): Json) -> Response { +#[cfg(debug_assertions)] +impl Default for DevBridgeConfig { + fn default() -> Self { + Self { + host: "127.0.0.1".to_string(), + port: 3030, + } + } +} + +/// 开发桥接服务器 +#[cfg(debug_assertions)] +pub struct DevBridgeServer; + +#[cfg(debug_assertions)] +impl DevBridgeServer { + /// 启动开发桥接服务器 + /// + /// 这是一个独立的 HTTP 服务器,仅用于开发模式, + /// 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令。 + /// + /// 服务器会在后台持续运行,直到应用退出。 + pub async fn start( + app_state: Arc>, + config: Option, + ) -> Result<(), Box> { + let config = config.unwrap_or_default(); + + let app = Router::new() + .route("/invoke", post(invoke_command)) + .route("/health", post(health_check)) + .layer( + // CORS 配置 - 允许 localhost:1420 访问 + CorsLayer::new() + .allow_origin("http://localhost:1420".parse::().unwrap()) + .allow_methods([axum::http::Method::POST, axum::http::Method::GET]) + .allow_headers([axum::http::header::CONTENT_TYPE]), + ) + .with_state(app_state); + + let addr = format!("{}:{}", config.host, config.port); + let listener = tokio::net::TcpListener::bind(&addr).await?; + + eprintln!("[DevBridge] 正在监听: http://{}", addr); + + // 直接运行服务器(不使用 graceful_shutdown) + // 服务器将持续运行直到应用退出 + tokio::spawn(async move { + axum::serve(listener, app).await.ok(); + }); + + Ok(()) + } +} + +#[cfg(debug_assertions)] +async fn invoke_command( + State(state): State>>, + Json(req): Json, +) -> Response { + // 获取 AppState 的读锁 + let state_ref = state.read().await; // 调用命令分发器 - match dispatcher::handle_command(&state, &req.cmd, req.args).await { + match dispatcher::handle_command(&state_ref, &req.cmd, req.args).await { Ok(result) => Json(InvokeResponse { result: Some(result), error: None, @@ -60,3 +126,12 @@ async fn invoke_command(State(state): State, Json(req): Json impl IntoResponse { + Json(serde_json::json!({ + "status": "ok", + "service": "DevBridge", + "version": "1.0.0" + })) +} diff --git a/src-tauri/src/dev_bridge/dispatcher.rs b/src-tauri/src/dev_bridge/dispatcher.rs index 8c28a2ce4..93cb1ff98 100644 --- a/src-tauri/src/dev_bridge/dispatcher.rs +++ b/src-tauri/src/dev_bridge/dispatcher.rs @@ -32,7 +32,8 @@ pub async fn handle_command( "get_default_provider" => { let provider = state.default_provider.read().await.clone(); - Ok(serde_json::json!({ "provider": provider })) + // 直接返回字符串值,不是对象 + Ok(serde_json::json!(provider)) } "get_endpoint_providers" => { @@ -177,6 +178,16 @@ pub async fn handle_command( })) } + // ========== 网络信息 ========== + "get_network_info" => { + // 返回网络信息 + Ok(serde_json::json!({ + "localhost": "127.0.0.1", + "lan_ip": null, + "all_ips": ["127.0.0.1"] + })) + } + _ => Err(format!( "[DevBridge] 未知命令: '{}'. 如需此命令,请将其添加到 dispatcher.rs 的 handle_command 函数中。", cmd diff --git a/src-tauri/src/server/mod.rs b/src-tauri/src/server/mod.rs index 7b4c4d2e2..27635ce1b 100644 --- a/src-tauri/src/server/mod.rs +++ b/src-tauri/src/server/mod.rs @@ -866,6 +866,28 @@ async fn run_server( api_key_service, }; + // ========== 开发模式:启动独立的 HTTP 桥接服务器 ========== + // 仅在 debug 模式下,启动一个独立的开发服务器在端口 3030 + // 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令 + #[cfg(debug_assertions)] + { + eprintln!("[DevBridge] ===== 准备启动开发桥接服务器 ====="); + use tokio::sync::RwLock as TokioRwLock; + let dev_bridge_state = Arc::new(TokioRwLock::new(state.clone())); + eprintln!("[DevBridge] 状态已克隆,准备启动"); + tokio::spawn(async move { + eprintln!("[DevBridge] spawn 任务开始执行"); + match crate::dev_bridge::DevBridgeServer::start(dev_bridge_state, None).await { + Ok(_) => { + eprintln!("[DevBridge] 启动完成"); + } + Err(e) => { + eprintln!("[DevBridge] 启动失败: {}", e); + } + } + }); + } + // 启动配置文件监控 let _file_watcher = if let Some(path) = config_path { start_config_watcher( @@ -986,9 +1008,6 @@ async fn run_server( .merge(kiro_api_routes) // 凭证 API 路由(用于 aster Agent 集成) .merge(credentials_api_routes) - // 开发模式 HTTP 桥接路由(仅在 debug 模式启用) - // 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令 - .merge(crate::dev_bridge::dev_bridge_routes()) .layer(DefaultBodyLimit::max(body_limit)) .with_state(state);