Update server URL handling and improve logging

- Changed the server URL from 'http://localhost:8000' to 'http://127.0.0.1:17493' in the server store and connection form.
- Enhanced server startup logging to display the dynamically assigned server URL.
- Updated server management logic in main.rs to reflect the new port configuration and improve orphaned process handling.
This commit is contained in:
Jamie Pine
2026-01-26 17:13:13 -08:00
parent 88d41f342b
commit 6a0601bd6c
7 changed files with 82 additions and 56 deletions
+4 -2
View File
@@ -94,8 +94,10 @@ function App() {
console.log('Production mode: Starting bundled server...'); console.log('Production mode: Starting bundled server...');
startServer(false) startServer(false)
.then(() => { .then((serverUrl) => {
console.log('Server is ready'); console.log('Server is ready at:', serverUrl);
// Update the server URL in the store with the dynamically assigned port
useServerStore.getState().setServerUrl(serverUrl);
setServerReady(true); setServerReady(true);
// Mark that we started the server (so we know to stop it on close) // Mark that we started the server (so we know to stop it on close)
// @ts-expect-error - adding property to window // @ts-expect-error - adding property to window
@@ -70,7 +70,7 @@ export function ConnectionForm() {
<FormItem> <FormItem>
<FormLabel>Server URL</FormLabel> <FormLabel>Server URL</FormLabel>
<FormControl> <FormControl>
<Input placeholder="http://localhost:8000" {...field} /> <Input placeholder="http://127.0.0.1:17493" {...field} />
</FormControl> </FormControl>
<FormDescription>Enter the URL of your voicebox backend server</FormDescription> <FormDescription>Enter the URL of your voicebox backend server</FormDescription>
<FormMessage /> <FormMessage />
+1 -1
View File
@@ -18,7 +18,7 @@ interface ServerStore {
export const useServerStore = create<ServerStore>()( export const useServerStore = create<ServerStore>()(
persist( persist(
(set) => ({ (set) => ({
serverUrl: 'http://localhost:8000', serverUrl: 'http://127.0.0.1:17493',
setServerUrl: (url) => set({ serverUrl: url }), setServerUrl: (url) => set({ serverUrl: url }),
isConnected: false, isConnected: false,
+2 -4
View File
@@ -74,10 +74,8 @@ export async function getLatestRelease(): Promise<ReleaseInfo> {
const releaseInfo: ReleaseInfo = { const releaseInfo: ReleaseInfo = {
version, version,
downloadLinks: { downloadLinks: {
macArm: macArm: downloadLinks.macArm || `${baseUrl}/voicebox_aarch64.app.tar.gz`,
downloadLinks.macArm || `${baseUrl}/voicebox_aarch64.app.tar.gz`, macIntel: downloadLinks.macIntel || `${baseUrl}/voicebox_x64.app.tar.gz`,
macIntel:
downloadLinks.macIntel || `${baseUrl}/voicebox_x64.app.tar.gz`,
windows: windows:
downloadLinks.windows || `${baseUrl}/voicebox_${version.replace('v', '')}_x64_en-US.msi`, downloadLinks.windows || `${baseUrl}/voicebox_${version.replace('v', '')}_x64_en-US.msi`,
linux: downloadLinks.linux || `${baseUrl}/voicebox_x86_64-unknown-linux-gnu.AppImage`, linux: downloadLinks.linux || `${baseUrl}/voicebox_x86_64-unknown-linux-gnu.AppImage`,
Binary file not shown.
+72 -46
View File
@@ -8,6 +8,9 @@ use tauri::{command, State, Manager, WindowEvent, Emitter, Listener, RunEvent};
use tauri_plugin_shell::ShellExt; use tauri_plugin_shell::ShellExt;
use tokio::sync::mpsc; use tokio::sync::mpsc;
const LEGACY_PORT: u16 = 8000;
const SERVER_PORT: u16 = 17493;
struct ServerState { struct ServerState {
child: Mutex<Option<tauri_plugin_shell::process::CommandChild>>, child: Mutex<Option<tauri_plugin_shell::process::CommandChild>>,
server_pid: Mutex<Option<u32>>, server_pid: Mutex<Option<u32>>,
@@ -22,64 +25,85 @@ async fn start_server(
) -> Result<String, String> { ) -> Result<String, String> {
// Check if server is already running (managed by this app instance) // Check if server is already running (managed by this app instance)
if state.child.lock().unwrap().is_some() { if state.child.lock().unwrap().is_some() {
return Ok("Server already running on http://localhost:8000".to_string()); return Ok(format!("http://127.0.0.1:{}", SERVER_PORT));
} }
// If keep_running_on_close is false, kill any orphaned server from previous session // Kill any orphaned voicebox-server from previous session on legacy port 8000
let keep_running = *state.keep_running_on_close.lock().unwrap(); // This handles upgrades from older versions that used a fixed port
if !keep_running { #[cfg(unix)]
#[cfg(unix)] {
use std::process::Command;
// Find processes listening on legacy port 8000 with their command names
if let Ok(output) = Command::new("lsof")
.args(["-i", &format!(":{}", LEGACY_PORT), "-sTCP:LISTEN"])
.output()
{ {
use std::process::Command; let output_str = String::from_utf8_lossy(&output.stdout);
// Find any process listening on port 8000 for line in output_str.lines().skip(1) { // Skip header line
if let Ok(output) = Command::new("lsof") // lsof output format: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
.args(["-ti", ":8000"]) let parts: Vec<&str> = line.split_whitespace().collect();
.output() if parts.len() >= 2 {
{ let command = parts[0];
let pids = String::from_utf8_lossy(&output.stdout); let pid_str = parts[1];
for pid_str in pids.lines() {
if let Ok(pid) = pid_str.trim().parse::<i32>() { // Only kill if it's a voicebox-server process
println!("Found orphaned server on port 8000 (PID: {}), killing it...", pid); if command.contains("voicebox") {
// Kill the process group if let Ok(pid) = pid_str.parse::<i32>() {
let _ = Command::new("kill") println!("Found orphaned voicebox-server on legacy port {} (PID: {}, CMD: {}), killing it...", LEGACY_PORT, pid, command);
.args(["-9", "--", &format!("-{}", pid)]) // Kill the process group
.output(); let _ = Command::new("kill")
let _ = Command::new("kill") .args(["-9", "--", &format!("-{}", pid)])
.args(["-9", &pid.to_string()]) .output();
.output(); let _ = Command::new("kill")
.args(["-9", &pid.to_string()])
.output();
}
} else {
println!("Legacy port {} is in use by non-voicebox process: {} (PID: {}), not killing", LEGACY_PORT, command, pid_str);
} }
} }
} }
} }
}
#[cfg(windows)] #[cfg(windows)]
{
use std::process::Command;
// On Windows, find PIDs on legacy port 8000, then check their names
if let Ok(output) = Command::new("netstat")
.args(["-ano"])
.output()
{ {
use std::process::Command; let output_str = String::from_utf8_lossy(&output.stdout);
// On Windows, find and kill process on port 8000 for line in output_str.lines() {
if let Ok(output) = Command::new("netstat") if line.contains(&format!(":{}", LEGACY_PORT)) && line.contains("LISTENING") {
.args(["-ano"]) if let Some(pid_str) = line.split_whitespace().last() {
.output() if let Ok(pid) = pid_str.parse::<u32>() {
{ // Get process name for this PID
let output_str = String::from_utf8_lossy(&output.stdout); if let Ok(tasklist_output) = Command::new("tasklist")
for line in output_str.lines() { .args(["/FI", &format!("PID eq {}", pid), "/FO", "CSV", "/NH"])
if line.contains(":8000") && line.contains("LISTENING") { .output()
if let Some(pid_str) = line.split_whitespace().last() { {
if let Ok(pid) = pid_str.parse::<u32>() { let tasklist_str = String::from_utf8_lossy(&tasklist_output.stdout);
println!("Found orphaned server on port 8000 (PID: {}), killing it...", pid); if tasklist_str.to_lowercase().contains("voicebox") {
let _ = Command::new("taskkill") println!("Found orphaned voicebox-server on legacy port {} (PID: {}), killing it...", LEGACY_PORT, pid);
.args(["/PID", &pid.to_string(), "/T", "/F"]) let _ = Command::new("taskkill")
.output(); .args(["/PID", &pid.to_string(), "/T", "/F"])
.output();
} else {
println!("Legacy port {} is in use by non-voicebox process (PID: {}), not killing", LEGACY_PORT, pid);
}
} }
} }
} }
} }
} }
} }
// Brief wait for port to be released
std::thread::sleep(std::time::Duration::from_millis(200));
} }
// Brief wait for port to be released
std::thread::sleep(std::time::Duration::from_millis(200));
// Get app data directory // Get app data directory
let data_dir = app let data_dir = app
.path() .path()
@@ -106,12 +130,14 @@ async fn start_server(
println!("Sidecar command created successfully"); println!("Sidecar command created successfully");
// Pass data directory to Python server // Pass data directory and port to Python server
sidecar = sidecar.args([ sidecar = sidecar.args([
"--data-dir", "--data-dir",
data_dir data_dir
.to_str() .to_str()
.ok_or_else(|| "Invalid data dir path".to_string())?, .ok_or_else(|| "Invalid data dir path".to_string())?,
"--port",
&SERVER_PORT.to_string(),
]); ]);
if remote.unwrap_or(false) { if remote.unwrap_or(false) {
@@ -134,9 +160,9 @@ async fn start_server(
println!("Server process spawned, waiting for ready signal..."); println!("Server process spawned, waiting for ready signal...");
println!("================================================================="); println!("=================================================================");
// Store child process and its PID for process group killing // Store child process and PID
let pid = child.pid(); let process_pid = child.pid();
*state.server_pid.lock().unwrap() = Some(pid); *state.server_pid.lock().unwrap() = Some(process_pid);
*state.child.lock().unwrap() = Some(child); *state.child.lock().unwrap() = Some(child);
// Wait for server to be ready by listening for startup log // Wait for server to be ready by listening for startup log
@@ -215,7 +241,7 @@ async fn start_server(
} }
}); });
Ok("Server started on http://localhost:8000".to_string()) Ok(format!("http://127.0.0.1:{}", SERVER_PORT))
} }
#[command] #[command]
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "voicebox", "productName": "Voicebox",
"version": "0.1.0", "version": "0.1.0",
"identifier": "sh.voicebox.app", "identifier": "sh.voicebox.app",
"build": { "build": {