feat: add single instance enforcement with TCP socket lock
Add single instance check using TCP listener on 127.0.0.1:53190 to prevent multiple application instances. Move AppState initialization into setup closure to include single_instance_lock field. Remove window close prevention and focus restoration handlers. Make main window non-resizable and non-maximizable.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
mod tunnel_manager;
|
mod tunnel_manager;
|
||||||
|
|
||||||
use std::{fs, path::PathBuf, sync::Mutex};
|
use std::{fs, net::TcpListener, path::PathBuf, sync::Mutex};
|
||||||
|
|
||||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
@@ -15,9 +15,11 @@ use x25519_dalek::{PublicKey, StaticSecret};
|
|||||||
|
|
||||||
const PROFILE_NAME: &str = "NexaVPN";
|
const PROFILE_NAME: &str = "NexaVPN";
|
||||||
const MAIN_WINDOW_LABEL: &str = "main";
|
const MAIN_WINDOW_LABEL: &str = "main";
|
||||||
|
const SINGLE_INSTANCE_ADDR: &str = "127.0.0.1:53190";
|
||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
session: Mutex<Option<SessionState>>,
|
session: Mutex<Option<SessionState>>,
|
||||||
|
single_instance_lock: TcpListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -362,12 +364,6 @@ fn ensure_app_dir(app: &AppHandle) -> Result<PathBuf, String> {
|
|||||||
Ok(dir)
|
Ok(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_window(window: &Window) {
|
|
||||||
let _ = window.show();
|
|
||||||
let _ = window.unminimize();
|
|
||||||
let _ = window.set_focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore_webview_window(window: &WebviewWindow) {
|
fn restore_webview_window(window: &WebviewWindow) {
|
||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
let _ = window.unminimize();
|
let _ = window.unminimize();
|
||||||
@@ -382,6 +378,9 @@ fn hide_main_window(window: &Window) {
|
|||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
|
let single_instance_lock = TcpListener::bind(SINGLE_INSTANCE_ADDR)
|
||||||
|
.map_err(|_| format!("{} is already running.", app.package_info().name))?;
|
||||||
|
|
||||||
let open_item = MenuItemBuilder::with_id("open", "Open NexaVPN").build(app)?;
|
let open_item = MenuItemBuilder::with_id("open", "Open NexaVPN").build(app)?;
|
||||||
let quit_item = MenuItemBuilder::with_id("quit", "Quit NexaVPN").build(app)?;
|
let quit_item = MenuItemBuilder::with_id("quit", "Quit NexaVPN").build(app)?;
|
||||||
let menu = MenuBuilder::new(app).items(&[&open_item, &quit_item]).build()?;
|
let menu = MenuBuilder::new(app).items(&[&open_item, &quit_item]).build()?;
|
||||||
@@ -418,24 +417,20 @@ pub fn run() {
|
|||||||
})
|
})
|
||||||
.build(app)?;
|
.build(app)?;
|
||||||
|
|
||||||
|
app.manage(AppState {
|
||||||
|
session: Mutex::new(None),
|
||||||
|
single_instance_lock,
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.manage(AppState {
|
|
||||||
session: Mutex::new(None),
|
|
||||||
})
|
|
||||||
.on_window_event(|window, event| match event {
|
.on_window_event(|window, event| match event {
|
||||||
WindowEvent::CloseRequested { api, .. } => {
|
WindowEvent::CloseRequested { .. } => {}
|
||||||
api.prevent_close();
|
|
||||||
hide_main_window(window);
|
|
||||||
}
|
|
||||||
WindowEvent::Resized(_) => {
|
WindowEvent::Resized(_) => {
|
||||||
if window.is_minimized().unwrap_or(false) {
|
if window.is_minimized().unwrap_or(false) {
|
||||||
hide_main_window(window);
|
hide_main_window(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::Focused(true) => {
|
|
||||||
restore_window(window);
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![load_state, clear_session, enroll_device, sync_profile, connect_tunnel, disconnect_tunnel, tunnel_status])
|
.invoke_handler(tauri::generate_handler![load_state, clear_session, enroll_device, sync_profile, connect_tunnel, disconnect_tunnel, tunnel_status])
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
"title": "NexaVPN",
|
"title": "NexaVPN",
|
||||||
"width": 1120,
|
"width": 1120,
|
||||||
"height": 760,
|
"height": 760,
|
||||||
"resizable": true
|
"resizable": false,
|
||||||
|
"maximizable": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
Reference in New Issue
Block a user