api-client/packages/hoppscotch-desktop/src-tauri/src/lib.rs
Shreyas eb2cc58dca
feat(desktop): explore random port for IPv6 compat (#5311)
This attempts to resolve app startup failures on Linux systems
where IPv6 is disabled at the kernel level by replacing the dual-stack
port selection logic with network interface discovery.

Closes FE-912
Closes #4962
2025-08-08 13:40:02 +05:30

116 lines
4.2 KiB
Rust

pub mod logger;
pub mod server;
pub mod updater;
use std::sync::OnceLock;
use tauri::{Emitter, Manager};
use tauri_plugin_appload::VendorConfigBuilder;
use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_window_state::StateFlags;
use random_port::{PortPicker, Protocol};
pub const HOPPSCOTCH_DESKTOP_IDENTIFIER: &'static str = "io.hoppscotch.desktop";
static SERVER_PORT: OnceLock<u16> = OnceLock::new();
#[tauri::command]
fn hopp_auth_port() -> u16 {
*SERVER_PORT.get().expect("Server port not initialized")
}
/// Gracefully quit the Hoppscotch Desktop
///
/// This command is invoked from the frontend when the user triggers
/// the quit action (typically via Cmd+Q/Ctrl+Q keyboard shortcut).
///
/// It performs a clean shutdown by logging the quit request
/// for debugging and then calling `app.exit(0)` which triggers
/// v2's graceful shutdown.
///
/// It basically trigger `RunEvent::ExitRequested`
/// followed by `RunEvent::Exit` events.
/// See https://docs.rs/tauri/latest/tauri/struct.AppHandle.html#method.exit
#[tauri::command]
fn quit_app(app: tauri::AppHandle) -> Result<(), String> {
tracing::info!("Quit command received, shutting down application");
app.exit(0);
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let server_port: u16 = PortPicker::new()
.protocol(Protocol::Tcp)
.port_range(15000..=25000)
.pick()
.expect("Cannot find unused port");
tracing::info!("Selected server port: {}", server_port);
SERVER_PORT
.set(server_port)
.expect("Failed to set server port");
let port = *SERVER_PORT.get().expect("Server port not initialized");
tracing::info!(port = port, "Initializing server with pre-selected port");
let port = server::init(port, app.handle().clone());
tracing::info!(port = port, "Server initialization complete");
tracing::info!(app_name = %app.package_info().name, "Configuring deep link handler");
let handle = app.handle().clone();
app.deep_link().on_open_url(move |event| {
let urls = event.urls();
tracing::info!(
urls = ?urls,
count = urls.len(),
"Processing deep link request"
);
handle
.emit("scheme-request-received", urls)
.unwrap_or_else(|e| {
tracing::error!(
error.message = %e,
error.type = %std::any::type_name_of_val(&e),
"Deep link event emission failed"
);
});
});
tracing::info!("Starting Hoppscotch Desktop v{}", env!("CARGO_PKG_VERSION"));
Ok(())
})
.plugin(
tauri_plugin_window_state::Builder::new()
.with_state_flags(
StateFlags::SIZE
| StateFlags::POSITION
| StateFlags::MAXIMIZED
| StateFlags::FULLSCREEN,
)
.with_denylist(&["main"])
.build(),
)
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_appload::init(
VendorConfigBuilder::new().bundle(
include_bytes!("../../bundle.zip").to_vec(),
include_bytes!("../../manifest.json"),
),
))
.plugin(tauri_plugin_relay::init())
.invoke_handler(tauri::generate_handler![
hopp_auth_port,
updater::check_updates_available,
updater::install_updates_and_restart,
quit_app,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}