mirror of
https://mirror.suhoan.cn/https://github.com/EasyTier/EasyTier.git
synced 2025-12-14 13:47:24 +08:00
291 lines
8.9 KiB
Rust
291 lines
8.9 KiB
Rust
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
|
|
mod elevate;
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use easytier::{
|
|
common::config::{ConfigLoader, FileLoggerConfig, LoggingConfigBuilder, TomlConfigLoader},
|
|
instance_manager::NetworkInstanceManager,
|
|
launcher::{ConfigSource, NetworkConfig, NetworkInstanceRunningInfo},
|
|
utils::{self, NewFilterSender},
|
|
};
|
|
|
|
use tauri::Manager as _;
|
|
|
|
pub const AUTOSTART_ARG: &str = "--autostart";
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
|
|
|
|
static INSTANCE_MANAGER: once_cell::sync::Lazy<NetworkInstanceManager> =
|
|
once_cell::sync::Lazy::new(NetworkInstanceManager::new);
|
|
|
|
static mut LOGGER_LEVEL_SENDER: once_cell::sync::Lazy<Option<NewFilterSender>> =
|
|
once_cell::sync::Lazy::new(Default::default);
|
|
|
|
#[tauri::command]
|
|
fn easytier_version() -> Result<String, String> {
|
|
Ok(easytier::VERSION.to_string())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn set_dock_visibility(app: tauri::AppHandle, visible: bool) -> Result<(), String> {
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
use tauri::ActivationPolicy;
|
|
app.set_activation_policy(if visible {
|
|
ActivationPolicy::Regular
|
|
} else {
|
|
ActivationPolicy::Accessory
|
|
})
|
|
.map_err(|e| e.to_string())?;
|
|
}
|
|
#[cfg(not(target_os = "macos"))]
|
|
let _ = (app, visible);
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn is_autostart() -> Result<bool, String> {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
println!("{:?}", args);
|
|
Ok(args.contains(&AUTOSTART_ARG.to_owned()))
|
|
}
|
|
|
|
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
|
#[tauri::command]
|
|
fn parse_network_config(cfg: NetworkConfig) -> Result<String, String> {
|
|
let toml = cfg.gen_config().map_err(|e| e.to_string())?;
|
|
Ok(toml.dump())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn generate_network_config(toml_config: String) -> Result<NetworkConfig, String> {
|
|
let config = TomlConfigLoader::new_from_str(&toml_config).map_err(|e| e.to_string())?;
|
|
let cfg = NetworkConfig::new_from_config(&config).map_err(|e| e.to_string())?;
|
|
Ok(cfg)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn run_network_instance(cfg: NetworkConfig) -> Result<(), String> {
|
|
let instance_id = cfg.instance_id().to_string();
|
|
let cfg = cfg.gen_config().map_err(|e| e.to_string())?;
|
|
INSTANCE_MANAGER
|
|
.run_network_instance(cfg, ConfigSource::GUI)
|
|
.map_err(|e| e.to_string())?;
|
|
println!("instance {} started", instance_id);
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn retain_network_instance(instance_ids: Vec<String>) -> Result<(), String> {
|
|
let instance_ids = instance_ids
|
|
.into_iter()
|
|
.filter_map(|id| uuid::Uuid::parse_str(&id).ok())
|
|
.collect();
|
|
let retained = INSTANCE_MANAGER
|
|
.retain_network_instance(instance_ids)
|
|
.map_err(|e| e.to_string())?;
|
|
println!("instance {:?} retained", retained);
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn collect_network_infos() -> Result<BTreeMap<String, NetworkInstanceRunningInfo>, String> {
|
|
let infos = INSTANCE_MANAGER
|
|
.collect_network_infos()
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let mut ret = BTreeMap::new();
|
|
for (uuid, info) in infos {
|
|
ret.insert(uuid.to_string(), info);
|
|
}
|
|
|
|
Ok(ret)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn get_os_hostname() -> Result<String, String> {
|
|
Ok(gethostname::gethostname().to_string_lossy().to_string())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn set_logging_level(level: String) -> Result<(), String> {
|
|
#[allow(static_mut_refs)]
|
|
let sender = unsafe { LOGGER_LEVEL_SENDER.as_ref().unwrap() };
|
|
sender.send(level).map_err(|e| e.to_string())?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn set_tun_fd(instance_id: String, fd: i32) -> Result<(), String> {
|
|
let uuid = uuid::Uuid::parse_str(&instance_id).map_err(|e| e.to_string())?;
|
|
INSTANCE_MANAGER
|
|
.set_tun_fd(&uuid, fd)
|
|
.map_err(|e| e.to_string())?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
fn toggle_window_visibility<R: tauri::Runtime>(app: &tauri::AppHandle<R>) {
|
|
if let Some(window) = app.get_webview_window("main") {
|
|
if window.is_visible().unwrap_or_default() {
|
|
if window.is_minimized().unwrap_or_default() {
|
|
let _ = window.unminimize();
|
|
let _ = window.set_focus();
|
|
} else {
|
|
let _ = window.hide();
|
|
}
|
|
} else {
|
|
let _ = window.show();
|
|
let _ = window.set_focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
fn check_sudo() -> bool {
|
|
let is_elevated = elevate::Command::is_elevated();
|
|
if !is_elevated {
|
|
let exe_path = std::env::var("APPIMAGE")
|
|
.ok()
|
|
.or_else(|| std::env::args().next())
|
|
.unwrap_or_default();
|
|
let args: Vec<String> = std::env::args().collect();
|
|
let mut stdcmd = std::process::Command::new(&exe_path);
|
|
if args.contains(&AUTOSTART_ARG.to_owned()) {
|
|
stdcmd.arg(AUTOSTART_ARG);
|
|
}
|
|
elevate::Command::new(stdcmd)
|
|
.output()
|
|
.expect("Failed to run elevated command");
|
|
}
|
|
is_elevated
|
|
}
|
|
|
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
pub fn run() {
|
|
#[cfg(not(target_os = "android"))]
|
|
if !check_sudo() {
|
|
use std::process;
|
|
process::exit(0);
|
|
}
|
|
|
|
utils::setup_panic_handler();
|
|
|
|
let mut builder = tauri::Builder::default();
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
{
|
|
use tauri_plugin_autostart::MacosLauncher;
|
|
builder = builder.plugin(tauri_plugin_autostart::init(
|
|
MacosLauncher::LaunchAgent,
|
|
Some(vec![AUTOSTART_ARG]),
|
|
));
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
{
|
|
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
|
app.webview_windows()
|
|
.values()
|
|
.next()
|
|
.expect("Sorry, no window found")
|
|
.set_focus()
|
|
.expect("Can't Bring Window to Focus");
|
|
}));
|
|
}
|
|
|
|
builder = builder
|
|
.plugin(tauri_plugin_os::init())
|
|
.plugin(tauri_plugin_clipboard_manager::init())
|
|
.plugin(tauri_plugin_process::init())
|
|
.plugin(tauri_plugin_shell::init())
|
|
.plugin(tauri_plugin_vpnservice::init());
|
|
|
|
let app = builder
|
|
.setup(|app| {
|
|
// for logging config
|
|
let Ok(log_dir) = app.path().app_log_dir() else {
|
|
return Ok(());
|
|
};
|
|
let config = LoggingConfigBuilder::default()
|
|
.file_logger(FileLoggerConfig {
|
|
dir: Some(log_dir.to_string_lossy().to_string()),
|
|
level: None,
|
|
file: None,
|
|
})
|
|
.build()
|
|
.map_err(|e| e.to_string())?;
|
|
let Ok(Some(logger_reinit)) = utils::init_logger(&config, true) else {
|
|
return Ok(());
|
|
};
|
|
#[allow(static_mut_refs)]
|
|
unsafe {
|
|
LOGGER_LEVEL_SENDER.replace(logger_reinit)
|
|
};
|
|
|
|
// for tray icon, menu need to be built in js
|
|
#[cfg(not(target_os = "android"))]
|
|
let _tray_menu = TrayIconBuilder::with_id("main")
|
|
.show_menu_on_left_click(false)
|
|
.on_tray_icon_event(|tray, event| {
|
|
if let TrayIconEvent::Click {
|
|
button: MouseButton::Left,
|
|
button_state: MouseButtonState::Up,
|
|
..
|
|
} = event
|
|
{
|
|
let app = tray.app_handle();
|
|
toggle_window_visibility(app);
|
|
}
|
|
})
|
|
.icon(tauri::image::Image::from_bytes(include_bytes!(
|
|
"../icons/icon.png"
|
|
))?)
|
|
.icon_as_template(false)
|
|
.build(app)?;
|
|
|
|
Ok(())
|
|
})
|
|
.invoke_handler(tauri::generate_handler![
|
|
parse_network_config,
|
|
generate_network_config,
|
|
run_network_instance,
|
|
retain_network_instance,
|
|
collect_network_infos,
|
|
get_os_hostname,
|
|
set_logging_level,
|
|
set_tun_fd,
|
|
is_autostart,
|
|
easytier_version,
|
|
set_dock_visibility
|
|
])
|
|
.on_window_event(|_win, event| match event {
|
|
#[cfg(not(target_os = "android"))]
|
|
tauri::WindowEvent::CloseRequested { api, .. } => {
|
|
let _ = _win.hide();
|
|
api.prevent_close();
|
|
}
|
|
_ => {}
|
|
})
|
|
.build(tauri::generate_context!())
|
|
.unwrap();
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
app.run(|_app, _event| {});
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
use tauri::RunEvent;
|
|
app.run(|app, event| match event {
|
|
RunEvent::Reopen { .. } => {
|
|
toggle_window_visibility(app);
|
|
}
|
|
_ => {}
|
|
});
|
|
}
|
|
}
|