diff --git a/src-tauri/src/sidecar.rs b/src-tauri/src/sidecar.rs index 6babe15..8357da9 100644 --- a/src-tauri/src/sidecar.rs +++ b/src-tauri/src/sidecar.rs @@ -13,9 +13,6 @@ const STARTUP_TIMEOUT_SECS: u64 = 5; /// 默认起始端口 const DEFAULT_PORT: u16 = 3000; -/// 开发模式使用的端口 -const DEV_PORT: u16 = 3000; - /// 端口扫描范围(从起始端口开始扫描的端口数量) const PORT_SCAN_RANGE: u16 = 100; @@ -40,19 +37,6 @@ async fn is_port_available(port: u16) -> bool { .is_ok() } -// 检查端口是否被占用(服务器正在监听) -async fn is_port_in_use(port: u16) -> bool { - use tokio::io::AsyncWriteExt; - - match tokio::net::TcpStream::connect(format!("127.0.0.1:{}", port)).await { - Ok(mut stream) => { - let _ = stream.shutdown().await; - true - } - Err(_) => false, - } -} - // 查找可用端口 async fn find_available_port(start: u16) -> u16 { for port in start..start + PORT_SCAN_RANGE { @@ -65,117 +49,95 @@ async fn find_available_port(start: u16) -> u16 { /// 启动 Sidecar 进程并创建主窗口 pub fn spawn_sidecar(app_handle: tauri::AppHandle) { + // 检测是否为开发模式 + let is_dev = cfg!(debug_assertions); + + if is_dev { + // 开发模式:直接创建窗口连接到 Vite 开发服务器 + println!("🔧 开发模式:连接到 Vite 开发服务器 (localhost:3000)"); + + let url = "http://localhost:3000"; + tauri::WebviewWindowBuilder::new( + &app_handle, + "main", + tauri::WebviewUrl::External(url.parse().unwrap()), + ) + .title(WINDOW_TITLE) + .inner_size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT) + .center() + .build() + .expect("创建窗口失败"); + + return; + } + + // 生产模式:启动 sidecar 二进制 tauri::async_runtime::spawn(async move { - // 检测是否为开发模式 - let is_dev = cfg!(debug_assertions); + println!("🚀 生产模式:启动 Sidecar Server"); - if is_dev { - // 开发模式:直接连接到 localhost:3000 - println!("🔧 开发模式:使用本地开发服务器 (localhost:{})", DEV_PORT); + // 查找可用端口 + let port = find_available_port(DEFAULT_PORT).await; + println!("使用端口: {}", port); - // 等待开发服务器就绪(可选:添加重试逻辑) - let max_retries = 10; - let retry_delay = Duration::from_millis(500); - let mut server_ready = false; + // 启动 sidecar + let sidecar = app_handle + .shell() + .sidecar("server") + .expect("无法找到 server") + .env("NITRO_PORT", port.to_string()); - for attempt in 1..=max_retries { - if is_port_in_use(DEV_PORT).await { + let (mut rx, child) = sidecar.spawn().expect("启动 sidecar 失败"); + + // 保存进程句柄到全局状态 + if let Some(state) = app_handle.try_state::() { + *state.0.lock().unwrap() = Some(child); + } + + // 监听 stdout,等待服务器就绪信号 + let start_time = std::time::Instant::now(); + let timeout = Duration::from_secs(STARTUP_TIMEOUT_SECS); + let mut server_ready = false; + + while let Some(event) = rx.recv().await { + if let CommandEvent::Stdout(line) = event { + let output = String::from_utf8_lossy(&line); + println!("Server: {}", output); + + // 检测服务器启动成功的标志 + if output.contains("Listening on:") || output.contains("localhost") { server_ready = true; - println!("✓ 开发服务器已就绪 (端口 {})", DEV_PORT); + println!("✓ Server 启动成功!"); + + // 创建主窗口 + let url = format!("http://localhost:{}", port); + tauri::WebviewWindowBuilder::new( + &app_handle, + "main", + tauri::WebviewUrl::External(url.parse().unwrap()), + ) + .title(WINDOW_TITLE) + .inner_size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT) + .center() + .build() + .expect("创建窗口失败"); + break; } - println!( - "⏳ 等待开发服务器启动... (尝试 {}/{})", - attempt, max_retries + } + + // 超时检查 + if start_time.elapsed() > timeout { + eprintln!( + "✗ 启动超时: Server 未能在 {} 秒内启动", + STARTUP_TIMEOUT_SECS ); - tokio::time::sleep(retry_delay).await; + break; } + } - if !server_ready { - eprintln!("✗ 开发服务器未就绪,请确保运行了 `bun run dev`"); - std::process::exit(1); - } - - // 创建主窗口 - let url = format!("http://localhost:{}", DEV_PORT); - tauri::WebviewWindowBuilder::new( - &app_handle, - "main", - tauri::WebviewUrl::External(url.parse().unwrap()), - ) - .title(WINDOW_TITLE) - .inner_size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT) - .center() - .build() - .expect("创建窗口失败"); - } else { - // 生产模式:启动 sidecar 二进制 - println!("🚀 生产模式:启动 Sidecar Server"); - - // 查找可用端口 - let port = find_available_port(DEFAULT_PORT).await; - println!("使用端口: {}", port); - - // 启动 sidecar - let sidecar = app_handle - .shell() - .sidecar("server") - .expect("无法找到 server") - .env("NITRO_PORT", port.to_string()); - - let (mut rx, child) = sidecar.spawn().expect("启动 sidecar 失败"); - - // 保存进程句柄到全局状态 - if let Some(state) = app_handle.try_state::() { - *state.0.lock().unwrap() = Some(child); - } - - // 监听 stdout,等待服务器就绪信号 - let start_time = std::time::Instant::now(); - let timeout = Duration::from_secs(STARTUP_TIMEOUT_SECS); - let mut server_ready = false; - - while let Some(event) = rx.recv().await { - if let CommandEvent::Stdout(line) = event { - let output = String::from_utf8_lossy(&line); - println!("Server: {}", output); - - // 检测服务器启动成功的标志 - if output.contains("Listening on:") || output.contains("localhost") { - server_ready = true; - println!("✓ Server 启动成功!"); - - // 创建主窗口 - let url = format!("http://localhost:{}", port); - tauri::WebviewWindowBuilder::new( - &app_handle, - "main", - tauri::WebviewUrl::External(url.parse().unwrap()), - ) - .title(WINDOW_TITLE) - .inner_size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT) - .center() - .build() - .expect("创建窗口失败"); - - break; - } - } - - // 超时检查 - if start_time.elapsed() > timeout { - eprintln!( - "✗ 启动超时: Server 未能在 {} 秒内启动", - STARTUP_TIMEOUT_SECS - ); - break; - } - } - - if !server_ready { - eprintln!("✗ Server 启动失败"); - std::process::exit(1); - } + if !server_ready { + eprintln!("✗ Server 启动失败"); + std::process::exit(1); } }); }