wonfen 05fba11baa
feat: auto-download updates in background and install on next launch
(cherry picked from commit 2f7c1b85f25e80b86233798a75e133b72a8101bb)
2026-04-03 05:54:18 +08:00

226 lines
6.2 KiB
Rust

use std::sync::atomic::{AtomicBool, Ordering};
use anyhow::Result;
use crate::{
config::Config,
core::{
CoreManager, Timer,
hotkey::Hotkey,
logger::Logger,
service::{SERVICE_MANAGER, ServiceManager, is_service_ipc_path_exists},
sysopt,
tray::Tray,
},
feat,
module::{auto_backup::AutoBackupManager, lightweight::auto_lightweight_boot},
process::AsyncHandler,
utils::{init, server, window_manager::WindowManager},
};
use clash_verge_logging::{Type, logging, logging_error};
use clash_verge_signal;
pub mod dns;
pub mod scheme;
pub mod ui;
pub mod window;
pub mod window_script;
static RESOLVE_DONE: AtomicBool = AtomicBool::new(false);
pub fn init_work_dir_and_logger() -> anyhow::Result<()> {
AsyncHandler::block_on(async {
init_work_config().await;
init_resources().await;
logging!(info, Type::Setup, "Initializing logger");
// #[cfg(not(feature = "tokio-trace"))]
Logger::global().init().await?;
Ok(())
})
}
pub fn resolve_setup_sync() {
AsyncHandler::spawn(|| async {
AsyncHandler::spawn_blocking(init_scheme);
AsyncHandler::spawn_blocking(init_embed_server);
});
}
pub fn resolve_setup_async() {
AsyncHandler::spawn(|| async {
logging!(info, Type::ClashVergeRev, "Version: {}", env!("CARGO_PKG_VERSION"));
init_startup_script().await;
init_verge_config().await;
Config::verify_config_initialization().await;
init_window().await;
let core_init = AsyncHandler::spawn(|| async {
init_service_manager().await;
init_core_manager().await;
init_system_proxy().await;
init_system_proxy_guard().await;
});
let tray_init = async {
init_tray().await;
refresh_tray_menu().await;
};
let _ = futures::join!(
core_init,
tray_init,
init_timer(),
init_hotkey(),
init_auto_lightweight_boot(),
init_auto_backup(),
init_silent_updater(),
);
refresh_tray_menu().await;
});
}
pub async fn resolve_reset_async() -> Result<(), anyhow::Error> {
sysopt::Sysopt::global().reset_sysproxy().await?;
CoreManager::global().stop_core().await?;
#[cfg(target_os = "macos")]
{
use dns::restore_public_dns;
restore_public_dns().await;
}
Ok(())
}
pub(super) fn init_scheme() {
logging_error!(Type::Setup, init::init_scheme());
}
pub async fn resolve_scheme(param: &str) -> Result<()> {
logging_error!(Type::Setup, scheme::resolve_scheme(param).await);
Ok(())
}
pub(super) fn init_embed_server() {
server::embed_server();
}
pub(super) async fn init_resources() {
logging_error!(Type::Setup, init::init_resources().await);
}
pub(super) async fn init_startup_script() {
logging_error!(Type::Setup, init::startup_script().await);
}
pub(super) async fn init_timer() {
logging_error!(Type::Setup, Timer::global().init().await);
}
pub(super) async fn init_hotkey() {
// if hotkey is not use by global, skip init it
let skip_register_hotkeys = !Config::verge().await.latest_arc().enable_global_hotkey.unwrap_or(true);
logging_error!(Type::Setup, Hotkey::global().init(skip_register_hotkeys).await);
}
pub(super) async fn init_auto_lightweight_boot() {
logging_error!(Type::Setup, auto_lightweight_boot().await);
}
pub(super) async fn init_auto_backup() {
logging_error!(Type::Setup, AutoBackupManager::global().init().await);
}
async fn init_silent_updater() {
use crate::core::SilentUpdater;
use crate::core::handle::Handle;
logging!(info, Type::Setup, "Initializing silent updater...");
let app_handle = Handle::app_handle();
// Check for cached update and attempt install before main app initialization.
// If install succeeds:
// - Windows: NSIS takes over and the process exits automatically
// - macOS/Linux: binary is replaced, we restart the app
if SilentUpdater::global().try_install_on_startup(app_handle).await {
logging!(info, Type::Setup, "Update installed at startup, restarting...");
app_handle.restart();
}
// No pending install — start background check/download loop
let app_handle = app_handle.clone();
tokio::spawn(async move {
SilentUpdater::global().start_background_check(app_handle).await;
});
logging!(info, Type::Setup, "Silent updater initialized");
}
pub fn init_signal() {
logging!(info, Type::Setup, "Initializing signal handlers...");
clash_verge_signal::register(feat::quit);
}
pub async fn init_work_config() {
logging_error!(Type::Setup, init::init_config().await);
}
pub(super) async fn init_tray() {
logging_error!(Type::Setup, Tray::global().init().await);
}
pub(super) async fn init_verge_config() {
logging_error!(Type::Setup, Config::init_config().await);
}
pub(super) async fn init_service_manager() {
clash_verge_service_ipc::set_config(Some(ServiceManager::config())).await;
if !is_service_ipc_path_exists() {
return;
}
if SERVICE_MANAGER.lock().await.init().await.is_ok() {
logging_error!(Type::Setup, SERVICE_MANAGER.lock().await.refresh().await);
}
}
pub(super) async fn init_core_manager() {
logging_error!(Type::Setup, CoreManager::global().init().await);
}
pub(super) async fn init_system_proxy() {
logging_error!(Type::Setup, sysopt::Sysopt::global().update_sysproxy().await);
}
pub(super) async fn init_system_proxy_guard() {
sysopt::Sysopt::global().refresh_guard().await;
}
pub(super) async fn refresh_tray_menu() {
logging_error!(Type::Setup, Tray::global().update_part().await);
}
pub(super) async fn init_window() {
let is_silent_start = Config::verge().await.data_arc().enable_silent_start.unwrap_or(false);
#[cfg(target_os = "macos")]
if is_silent_start {
use crate::core::handle::Handle;
Handle::global().set_activation_policy_accessory();
}
WindowManager::create_window(!is_silent_start).await;
}
pub fn resolve_done() {
RESOLVE_DONE.store(true, Ordering::Release);
}
pub fn is_resolve_done() -> bool {
RESOLVE_DONE.load(Ordering::Acquire)
}
pub fn reset_resolve_done() {
RESOLVE_DONE.store(false, Ordering::Release);
}