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...');
startServer(false)
.then(() => {
console.log('Server is ready');
.then((serverUrl) => {
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);
// Mark that we started the server (so we know to stop it on close)
// @ts-expect-error - adding property to window
@@ -70,7 +70,7 @@ export function ConnectionForm() {
<FormItem>
<FormLabel>Server URL</FormLabel>
<FormControl>
<Input placeholder="http://localhost:8000" {...field} />
<Input placeholder="http://127.0.0.1:17493" {...field} />
</FormControl>
<FormDescription>Enter the URL of your voicebox backend server</FormDescription>
<FormMessage />
+1 -1
View File
@@ -18,7 +18,7 @@ interface ServerStore {
export const useServerStore = create<ServerStore>()(
persist(
(set) => ({
serverUrl: 'http://localhost:8000',
serverUrl: 'http://127.0.0.1:17493',
setServerUrl: (url) => set({ serverUrl: url }),
isConnected: false,
+2 -4
View File
@@ -74,10 +74,8 @@ export async function getLatestRelease(): Promise<ReleaseInfo> {
const releaseInfo: ReleaseInfo = {
version,
downloadLinks: {
macArm:
downloadLinks.macArm || `${baseUrl}/voicebox_aarch64.app.tar.gz`,
macIntel:
downloadLinks.macIntel || `${baseUrl}/voicebox_x64.app.tar.gz`,
macArm: downloadLinks.macArm || `${baseUrl}/voicebox_aarch64.app.tar.gz`,
macIntel: downloadLinks.macIntel || `${baseUrl}/voicebox_x64.app.tar.gz`,
windows:
downloadLinks.windows || `${baseUrl}/voicebox_${version.replace('v', '')}_x64_en-US.msi`,
linux: downloadLinks.linux || `${baseUrl}/voicebox_x86_64-unknown-linux-gnu.AppImage`,
Binary file not shown.
+73 -47
View File
@@ -8,6 +8,9 @@ use tauri::{command, State, Manager, WindowEvent, Emitter, Listener, RunEvent};
use tauri_plugin_shell::ShellExt;
use tokio::sync::mpsc;
const LEGACY_PORT: u16 = 8000;
const SERVER_PORT: u16 = 17493;
struct ServerState {
child: Mutex<Option<tauri_plugin_shell::process::CommandChild>>,
server_pid: Mutex<Option<u32>>,
@@ -22,63 +25,84 @@ async fn start_server(
) -> Result<String, String> {
// Check if server is already running (managed by this app instance)
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
let keep_running = *state.keep_running_on_close.lock().unwrap();
if !keep_running {
#[cfg(unix)]
// Kill any orphaned voicebox-server from previous session on legacy port 8000
// This handles upgrades from older versions that used a fixed port
#[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;
// Find any process listening on port 8000
if let Ok(output) = Command::new("lsof")
.args(["-ti", ":8000"])
.output()
{
let pids = String::from_utf8_lossy(&output.stdout);
for pid_str in pids.lines() {
if let Ok(pid) = pid_str.trim().parse::<i32>() {
println!("Found orphaned server on port 8000 (PID: {}), killing it...", pid);
// Kill the process group
let _ = Command::new("kill")
.args(["-9", "--", &format!("-{}", pid)])
.output();
let _ = Command::new("kill")
.args(["-9", &pid.to_string()])
.output();
let output_str = String::from_utf8_lossy(&output.stdout);
for line in output_str.lines().skip(1) { // Skip header line
// lsof output format: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
let command = parts[0];
let pid_str = parts[1];
// Only kill if it's a voicebox-server process
if command.contains("voicebox") {
if let Ok(pid) = pid_str.parse::<i32>() {
println!("Found orphaned voicebox-server on legacy port {} (PID: {}, CMD: {}), killing it...", LEGACY_PORT, pid, command);
// Kill the process group
let _ = Command::new("kill")
.args(["-9", "--", &format!("-{}", pid)])
.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;
// On Windows, find and kill process on port 8000
if let Ok(output) = Command::new("netstat")
.args(["-ano"])
.output()
{
let output_str = String::from_utf8_lossy(&output.stdout);
for line in output_str.lines() {
if line.contains(":8000") && line.contains("LISTENING") {
if let Some(pid_str) = line.split_whitespace().last() {
if let Ok(pid) = pid_str.parse::<u32>() {
println!("Found orphaned server on port 8000 (PID: {}), killing it...", pid);
let _ = Command::new("taskkill")
.args(["/PID", &pid.to_string(), "/T", "/F"])
.output();
let output_str = String::from_utf8_lossy(&output.stdout);
for line in output_str.lines() {
if line.contains(&format!(":{}", LEGACY_PORT)) && line.contains("LISTENING") {
if let Some(pid_str) = line.split_whitespace().last() {
if let Ok(pid) = pid_str.parse::<u32>() {
// Get process name for this PID
if let Ok(tasklist_output) = Command::new("tasklist")
.args(["/FI", &format!("PID eq {}", pid), "/FO", "CSV", "/NH"])
.output()
{
let tasklist_str = String::from_utf8_lossy(&tasklist_output.stdout);
if tasklist_str.to_lowercase().contains("voicebox") {
println!("Found orphaned voicebox-server on legacy port {} (PID: {}), killing it...", LEGACY_PORT, pid);
let _ = Command::new("taskkill")
.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
let data_dir = app
@@ -106,12 +130,14 @@ async fn start_server(
println!("Sidecar command created successfully");
// Pass data directory to Python server
// Pass data directory and port to Python server
sidecar = sidecar.args([
"--data-dir",
data_dir
.to_str()
.ok_or_else(|| "Invalid data dir path".to_string())?,
"--port",
&SERVER_PORT.to_string(),
]);
if remote.unwrap_or(false) {
@@ -134,9 +160,9 @@ async fn start_server(
println!("Server process spawned, waiting for ready signal...");
println!("=================================================================");
// Store child process and its PID for process group killing
let pid = child.pid();
*state.server_pid.lock().unwrap() = Some(pid);
// Store child process and PID
let process_pid = child.pid();
*state.server_pid.lock().unwrap() = Some(process_pid);
*state.child.lock().unwrap() = Some(child);
// 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]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "voicebox",
"productName": "Voicebox",
"version": "0.1.0",
"identifier": "sh.voicebox.app",
"build": {