Skip to content

Commit

Permalink
fix: draft patch to resolve custom app config migration
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat616 committed Apr 2, 2024
1 parent 593a2ed commit 8cc9e8c
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 109 deletions.
230 changes: 165 additions & 65 deletions backend/Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ thiserror = "1"
tracing = "0.1"

[profile.release]
panic = "abort"
panic = "unwind"
codegen-units = 1
lto = true
opt-level = "s"
1 change: 1 addition & 0 deletions backend/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ image = "0.25.0"
fast_image_resize = "3.0.4"
display-info = "0.5.0" # should be removed after upgrading to tauri v2
dashmap = "5.5.3"
clap = { version = "4.5.4", features = ["derive"] } # add supports for commands

[target.'cfg(windows)'.dependencies]
deelevate = "0.2.0"
Expand Down
33 changes: 26 additions & 7 deletions backend/tauri/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,22 +381,41 @@ pub fn get_custom_app_dir() -> CmdResult<Option<String>> {

#[cfg(windows)]
#[tauri::command]
pub async fn set_custom_app_dir(path: String) -> CmdResult {
use crate::utils::{dialog::migrate_dialog, init::do_config_migration, winreg::set_app_dir};
pub async fn set_custom_app_dir(app_handle: tauri::AppHandle, path: String) -> CmdResult {
use crate::utils::{self, dialog::migrate_dialog, winreg::set_app_dir};
use rust_i18n::t;
use std::path::PathBuf;
use std::{path::PathBuf, time::Duration};

let path_str = path.clone();
let path = PathBuf::from(path);
wrap_err!(set_app_dir(&path))?;

// show a dialog to ask whether to migrate the data
let res = tauri::async_runtime::spawn_blocking(move || {
let msg = t!("dialog.custom_app_dir_migrate", path = path_str).to_string();

if migrate_dialog(&msg) {
let new_dir = PathBuf::from(path_str);
let old_dir = dirs::app_home_dir().unwrap();
do_config_migration(&old_dir, &new_dir)?;
let app_exe = tauri::utils::platform::current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?.to_string_lossy().to_string();
std::thread::spawn(move || {
std::thread::spawn(move || {
std::thread::sleep(Duration::from_secs(3));
utils::help::quit_application(&app_handle);
});
let args = vec![
"/C",
app_exe.as_str(),
"migrate_home_dir",
"-t",
path_str.as_str(),
];
runas::Command::new("cmd")
.args(&args)
.show(true)
.status()
.unwrap();
});
} else {
set_app_dir(&path)?;
}
Ok::<_, anyhow::Error>(())
})
Expand Down
111 changes: 111 additions & 0 deletions backend/tauri/src/core/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use anyhow::Ok;
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(version = None, about = None, long_about = None)]
pub struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
MigrateHomeDir {
#[arg(short = 't', long)]
target_path: String,
},
}

pub fn parse() -> anyhow::Result<()> {
let cli = Cli::parse();
if let Some(commands) = &cli.command {
match commands {
Commands::MigrateHomeDir { target_path } => {
self::handler::migrate_home_dir_handler(target_path).unwrap();
}
}
std::process::exit(0);
}
Ok(()) // bypass
}

mod handler {
#[cfg(target_os = "windows")]
pub fn migrate_home_dir_handler(target_path: &str) -> anyhow::Result<()> {
use crate::utils::{self, dirs};
use anyhow::Context;
use deelevate::{PrivilegeLevel, Token};
use std::{path::PathBuf, process::Command, str::FromStr, thread, time::Duration};
use sysinfo::System;
use tauri::utils::platform::current_exe;

let token = Token::with_current_process()?;
if let PrivilegeLevel::NotPrivileged = token.privilege_level()? {
eprintln!("Please run this command as admin to prevent authority issue.");
std::process::exit(1);
}

let current_home_dir = dirs::app_home_dir()?;
let target_home_dir = PathBuf::from_str(target_path)?;

// 1. waiting for app exited
println!("waiting for app exited.");
let placeholder = dirs::get_single_instance_placeholder();
let mut single_instance: single_instance::SingleInstance;
loop {
single_instance = single_instance::SingleInstance::new(&placeholder)
.context("failed to create single instance")?;
if single_instance.is_single() {
break;
}
thread::sleep(Duration::from_secs(1));
}

// 2. kill all related processes.
let related_names = [
"clash-verge-service",
"clash-nyanpasu-service", // for upcoming v1.6.x
"clash-rs",
"mihomo",
"mihomo-alpha",
"clash",
];
let sys = System::new_all();
'outer: for process in sys.processes().values() {
let mut process_name = process.name();
if process_name.ends_with(".exe") {
process_name = &process_name[..process_name.len() - 4]; // remove .exe
}
for name in related_names.iter() {
if process_name.ends_with(name) {
println!(
"Process found: {} should be killed. killing...",
process_name
);
if !process.kill() {
eprintln!("failed to kill {}.", process_name)
}
continue 'outer;
}
}
}

// 3. do config migrate and update the registry.
utils::init::do_config_migration(&current_home_dir, &target_home_dir)?;
utils::winreg::set_app_dir(target_home_dir.as_path())?;
println!("migration finished. starting application...");
drop(single_instance); // release single instance lock

let app_path = current_exe()?;
thread::spawn(move || {
Command::new(app_path).spawn().unwrap();
});
thread::sleep(Duration::from_secs(5));
Ok(())
}

#[cfg(not(target_os = "windows"))]
pub fn migrate_home_dir_handler(target_path: &str) -> anyhow::Result<()> {
Ok(())
}
}
1 change: 1 addition & 0 deletions backend/tauri/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod clash;
pub mod commands;
pub mod handle;
pub mod hotkey;
pub mod logger;
Expand Down
14 changes: 3 additions & 11 deletions backend/tauri/src/core/tray/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::storage;
use crate::{cmds, config::Config, feat, utils::resolve};
use crate::{cmds, config::Config, feat, utils, utils::resolve};
use anyhow::Result;
use rust_i18n::t;
use tauri::{
Expand All @@ -11,8 +11,7 @@ use tracing_attributes::instrument;
mod icon;
pub mod proxies;
pub use self::icon::on_scale_factor_changed;
use self::icon::TrayIcon;
use self::proxies::SystemTrayMenuProxiesExt;
use self::{icon::TrayIcon, proxies::SystemTrayMenuProxiesExt};

pub struct Tray {}

Expand Down Expand Up @@ -155,14 +154,7 @@ impl Tray {
"restart_clash" => feat::restart_clash_core(),
"restart_app" => api::process::restart(&app_handle.env()),
"quit" => {
let _ = resolve::save_window_state(app_handle, true);

resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0);
// flush all data to disk
storage::Storage::global().destroy().unwrap();
std::process::exit(0);
utils::help::quit_application(app_handle);
}
_ => {
proxies::on_system_tray_event(&id);
Expand Down
37 changes: 14 additions & 23 deletions backend/tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ mod enhance;
mod feat;
mod utils;

use crate::core::handle::Handle;
use crate::{
config::Config,
utils::{init, resolve},
core::{commands, handle::Handle},
utils::{dirs, init, resolve},
};
use anyhow::Context;
use tauri::{api, Manager, SystemTray};
Expand All @@ -23,21 +23,21 @@ rust_i18n::i18n!("../../locales");
#[cfg(feature = "deadlock-detection")]
fn deadlock_detection() {
use parking_lot::deadlock;
use std::thread;
use std::time::Duration;
use std::{thread, time::Duration};
use tracing::error;
thread::spawn(move || loop {
thread::sleep(Duration::from_secs(10));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}

println!("{} deadlocks detected", deadlocks.len());
error!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
println!("Deadlock #{}", i);
error!("Deadlock #{}", i);
for t in threads {
println!("Thread Id {:#?}", t.thread_id());
println!("{:#?}", t.backtrace());
error!("Thread Id {:#?}", t.thread_id());
error!("{:#?}", t.backtrace());
}
}
});
Expand All @@ -47,23 +47,16 @@ fn main() -> std::io::Result<()> {
#[cfg(feature = "deadlock-detection")]
deadlock_detection();

// Parse commands
commands::parse().unwrap();

// Should be in first place in order prevent single instance check block everything
tauri_plugin_deep_link::prepare("moe.elaina.clash.nyanpasu");

// 单例检测
#[cfg(not(target_os = "macos"))]
let app_name = utils::dirs::APP_NAME.to_string();

#[cfg(target_os = "macos")]
let app_name = api::path::local_data_dir()
.unwrap()
.into_os_string()
.into_string()
.unwrap()
+ utils::dirs::APP_NAME;

let placeholder = dirs::get_single_instance_placeholder();
let single_instance_result: anyhow::Result<()> =
single_instance::SingleInstance::new(app_name.as_str())
single_instance::SingleInstance::new(&placeholder)
.context("failed to create single instance")
.map(|instance| {
if !instance.is_single() {
Expand Down Expand Up @@ -93,9 +86,7 @@ fn main() -> std::io::Result<()> {
rust_i18n::set_locale(verge.as_str());

// show a dialog to print the single instance error
if let Err(e) = single_instance_result {
utils::dialog::panic_dialog(&format!("{:?}", e));
}
single_instance_result.unwrap();

#[allow(unused_mut)]
let mut builder = tauri::Builder::default()
Expand Down
21 changes: 20 additions & 1 deletion backend/tauri/src/utils/dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use crate::core::handle;
use anyhow::Result;
use std::{path::PathBuf, sync::OnceLock};
use tauri::{
api::path::{home_dir, resource_dir},
api::{
self,
path::{home_dir, resource_dir},
},
Env,
};

Expand Down Expand Up @@ -191,3 +194,19 @@ pub fn path_to_str(path: &PathBuf) -> Result<&str> {
.ok_or(anyhow::anyhow!("failed to get path from {:?}", path))?;
Ok(path_str)
}

pub fn get_single_instance_placeholder() -> String {
#[cfg(not(target_os = "macos"))]
{
APP_NAME.to_string()
}

#[cfg(target_os = "macos")]
{
api::path::local_data_dir()
.unwrap()
.join(APP_NAME)
.to_string_lossy()
.to_string()
}
}
14 changes: 13 additions & 1 deletion backend/tauri/src/utils/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::{
};
use tauri::{
api::shell::{open, Program},
Manager,
AppHandle, Manager,
};
use tracing::{debug, warn};
use tracing_attributes::instrument;
Expand Down Expand Up @@ -218,6 +218,18 @@ pub fn get_max_scale_factor() -> f64 {
}
}

#[instrument(skip(app_handle))]
pub fn quit_application(app_handle: &AppHandle) {
let _ = super::resolve::save_window_state(app_handle, true);

super::resolve::resolve_reset();
tauri::api::process::kill_children();
app_handle.exit(0);
// flush all data to disk
crate::core::storage::Storage::global().destroy().unwrap();
std::process::exit(0);
}

#[macro_export]
macro_rules! error {
($result: expr) => {
Expand Down

0 comments on commit 8cc9e8c

Please sign in to comment.