diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1101310..5a23084 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,102 +1,78 @@ // Prevents additional console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::process::{Child, Command}; +use std::process::{Child, Command, Stdio}; use std::sync::Mutex; use std::time::Duration; -use tauri::{AppHandle, Manager, RunEvent}; +use tauri::{Manager, RunEvent, WebviewUrl, WebviewWindowBuilder}; struct ServerProcess(Mutex>); -fn find_python_executable(app: &AppHandle) -> Option { - // In development, use system Python - if cfg!(debug_assertions) { - return Some("python3".to_string()); - } +const SERVER_URL: &str = "http://127.0.0.1:31102"; - // In production, look for bundled Python sidecar - let resource_path = app.path().resource_dir().ok()?; - - #[cfg(target_os = "macos")] - { - let sidecar_path = resource_path.join("sidecar").join("kjvstudy-server"); - if sidecar_path.exists() { - return Some(sidecar_path.to_string_lossy().to_string()); - } - } - - #[cfg(target_os = "windows")] - { - let sidecar_path = resource_path.join("sidecar").join("kjvstudy-server.exe"); - if sidecar_path.exists() { - return Some(sidecar_path.to_string_lossy().to_string()); - } - } - - // Fallback to system Python - Some("python3".to_string()) -} - -fn start_server(app: &AppHandle) -> Option { - let python = find_python_executable(app)?; - - // Get the resource directory for data files - let resource_dir = app.path().resource_dir().ok()?; - - // For development, use the project directory - let working_dir = if cfg!(debug_assertions) { - std::env::current_dir().ok()? - } else { - resource_dir.clone() - }; +fn start_server() -> Option { + // Get the current working directory (project root in dev) + let working_dir = std::env::current_dir().ok()?; println!("Starting KJV Study server..."); - println!("Python executable: {}", python); println!("Working directory: {:?}", working_dir); - let child = if python.ends_with("kjvstudy-server") || python.ends_with("kjvstudy-server.exe") { - // Running bundled executable - Command::new(&python) - .current_dir(&working_dir) - .env("KJVSTUDY_PORT", "31102") - .spawn() - .ok()? - } else { - // Running with Python interpreter (development) - Command::new(&python) - .args([ - "-m", "uvicorn", - "kjvstudy_org.server:app", - "--host", "127.0.0.1", - "--port", "31102", - "--log-level", "warning" - ]) - .current_dir(&working_dir) - .spawn() - .ok()? - }; + // Try uv first (preferred), then fall back to python3 + let child = Command::new("uv") + .args([ + "run", "uvicorn", + "kjvstudy_org.server:app", + "--host", "127.0.0.1", + "--port", "31102", + "--log-level", "warning" + ]) + .current_dir(&working_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .or_else(|_| { + println!("uv not found, trying python3..."); + Command::new("python3") + .args([ + "-m", "uvicorn", + "kjvstudy_org.server:app", + "--host", "127.0.0.1", + "--port", "31102", + "--log-level", "warning" + ]) + .current_dir(&working_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + }) + .ok()?; + println!("Server process started with PID: {}", child.id()); Some(child) } fn wait_for_server(max_attempts: u32) -> bool { let client = reqwest::blocking::Client::builder() - .timeout(Duration::from_secs(1)) + .timeout(Duration::from_secs(2)) .build() .unwrap(); for attempt in 1..=max_attempts { println!("Waiting for server... (attempt {}/{})", attempt, max_attempts); - match client.get("http://127.0.0.1:31102/api/health").send() { + match client.get(format!("{}/api/health", SERVER_URL)).send() { Ok(response) if response.status().is_success() => { println!("Server is ready!"); return true; } - _ => { - std::thread::sleep(Duration::from_millis(500)); + Ok(response) => { + println!("Server responded with status: {}", response.status()); + } + Err(e) => { + println!("Connection error: {}", e); } } + std::thread::sleep(Duration::from_millis(500)); } println!("Server failed to start after {} attempts", max_attempts); @@ -104,30 +80,39 @@ fn wait_for_server(max_attempts: u32) -> bool { } fn main() { + // Start server BEFORE Tauri + let server_child = start_server(); + + if server_child.is_none() { + eprintln!("ERROR: Failed to start server process!"); + eprintln!("Make sure you're running from the project directory with uv or python3 available."); + std::process::exit(1); + } + + // Wait for server to be ready + if !wait_for_server(30) { + eprintln!("ERROR: Server failed to become ready!"); + std::process::exit(1); + } + + // Now start Tauri tauri::Builder::default() .plugin(tauri_plugin_shell::init()) - .manage(ServerProcess(Mutex::new(None))) + .manage(ServerProcess(Mutex::new(server_child))) .setup(|app| { - // Start the Python server - let handle = app.handle().clone(); - - if let Some(child) = start_server(&handle) { - let state = app.state::(); - *state.0.lock().unwrap() = Some(child); - - // Wait for server to be ready - if !wait_for_server(30) { - eprintln!("Warning: Server may not be fully ready"); - } - } else { - eprintln!("Failed to start server process"); - } - - // Navigate to the local server - if let Some(window) = app.get_webview_window("main") { - let _ = window.eval("window.location.href = 'http://127.0.0.1:31102'"); - } + // Create window pointing to our server + let _window = WebviewWindowBuilder::new( + app, + "main", + WebviewUrl::External(SERVER_URL.parse().unwrap()) + ) + .title("KJV Study") + .inner_size(1200.0, 800.0) + .min_inner_size(800.0, 600.0) + .center() + .build()?; + println!("Window created, loading {}", SERVER_URL); Ok(()) }) .build(tauri::generate_context!()) @@ -140,6 +125,7 @@ fn main() { println!("Shutting down server..."); let _ = child.kill(); let _ = child.wait(); + println!("Server stopped."); }; } });