use std::sync::Mutex; use std::time::Duration; use tauri::Manager; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; use tauri_plugin_shell::ShellExt; // 全局状态:存储 Sidecar 进程句柄 pub struct SidecarProcess(pub Mutex>); // 检查端口是否可用 async fn is_port_available(port: u16) -> bool { tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port)) .await .is_ok() } // 查找可用端口 async fn find_available_port(start: u16) -> u16 { for port in start..start + 100 { if is_port_available(port).await { return port; } } start // 回退到起始端口 } /// 启动 Sidecar 进程并创建主窗口 pub fn spawn_sidecar(app_handle: tauri::AppHandle) { tauri::async_runtime::spawn(async move { // 查找可用端口 let port = find_available_port(3000).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(5); 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("Tauri Application") .inner_size(1200.0, 800.0) .center() .build() .expect("创建窗口失败"); break; } } // 超时检查 if start_time.elapsed() > timeout { eprintln!("✗ 启动超时:Server 未能在 5 秒内启动"); break; } } if !server_ready { eprintln!("✗ Server 启动失败"); std::process::exit(1); } }); } /// 清理 Sidecar 进程 (在应用退出时调用) pub fn cleanup_sidecar_process(app_handle: &tauri::AppHandle) { println!("应用退出,正在清理 Sidecar 进程..."); if let Some(state) = app_handle.try_state::() { if let Ok(mut process) = state.0.lock() { if let Some(child) = process.take() { let _ = child.kill(); println!("✓ Sidecar 进程已终止"); } } } }