mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-12 05:32:09 +08:00
Route frontend and tray node selections through the same backend flow for selection, persistence, state refresh, and connection cleanup. Read the group's previous selection before switching and preserve the original rule of closing only connections whose chains contain that selection. Run optional cleanup in the background and keep its failures separate from successful selection. Remove redundant frontend effects and unused IPC commands, and refresh the proxy view for every successful switch. Refs:32095daf90Refs:bea0dde074
234 lines
7.8 KiB
Rust
234 lines
7.8 KiB
Rust
use crate::cmd;
|
|
|
|
pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
|
tauri_specta::Builder::new()
|
|
.types(&platform_types())
|
|
.commands(tauri_specta::collect_commands![
|
|
tauri_plugin_clash_verge_sysinfo::commands::get_system_info,
|
|
tauri_plugin_clash_verge_sysinfo::commands::get_app_uptime,
|
|
tauri_plugin_clash_verge_sysinfo::commands::export_diagnostic_info::<tauri::Wry>,
|
|
cmd::probe_listener,
|
|
cmd::save_proxy_ports,
|
|
cmd::get_sys_proxy,
|
|
cmd::get_auto_proxy,
|
|
cmd::get_embedded_server_port,
|
|
cmd::open_app_dir,
|
|
cmd::open_logs_dir,
|
|
cmd::open_core_dir,
|
|
cmd::get_network_interfaces,
|
|
cmd::get_system_hostname,
|
|
cmd::restart_app,
|
|
cmd::install_update,
|
|
cmd::cancel_update_download,
|
|
cmd::restart_core,
|
|
cmd::upgrade_clash_core,
|
|
cmd::get_runtime_state,
|
|
cmd::get_pending_failures,
|
|
cmd::get_sidecar_failure,
|
|
cmd::entry_lightweight_mode,
|
|
cmd::install_service,
|
|
cmd::uninstall_service,
|
|
cmd::reinstall_service,
|
|
cmd::repair_service,
|
|
cmd::continue_with_sidecar,
|
|
cmd::sync_runtime_providers,
|
|
cmd::get_clash_info,
|
|
cmd::patch_clash_config,
|
|
cmd::patch_clash_mode,
|
|
cmd::get_clash_mode,
|
|
cmd::change_clash_core,
|
|
cmd::get_runtime_config,
|
|
cmd::get_proxy_view,
|
|
cmd::get_runtime_yaml,
|
|
cmd::get_runtime_logs,
|
|
cmd::get_runtime_proxy_chain_config,
|
|
cmd::update_proxy_chain_config_in_runtime,
|
|
cmd::invoke_uwp_tool,
|
|
cmd::copy_clash_env,
|
|
cmd::record_selected_node,
|
|
cmd::switch_proxy_node,
|
|
cmd::save_dns_config,
|
|
cmd::apply_dns_config,
|
|
cmd::set_dns_override,
|
|
cmd::take_dns_override_notice,
|
|
cmd::take_service_fallback_notice,
|
|
cmd::get_core_startup_error,
|
|
cmd::take_service_repair_notice,
|
|
cmd::take_service_owner_notice,
|
|
cmd::take_discarded_keys_notice,
|
|
cmd::get_dns_config_content,
|
|
cmd::validate_dns_config,
|
|
cmd::get_clash_logs,
|
|
cmd::get_verge_config,
|
|
cmd::patch_verge_config,
|
|
cmd::test_delay,
|
|
cmd::get_app_dir,
|
|
cmd::copy_icon_file,
|
|
cmd::download_icon_cache,
|
|
cmd::open_devtools,
|
|
cmd::exit_app,
|
|
cmd::get_network_interfaces_info,
|
|
cmd::get_profiles,
|
|
cmd::enhance_profiles,
|
|
cmd::patch_profiles_config,
|
|
cmd::view_profile,
|
|
cmd::patch_profile,
|
|
cmd::create_profile,
|
|
cmd::import_profile,
|
|
cmd::reorder_profile,
|
|
cmd::update_profile,
|
|
cmd::delete_profile,
|
|
cmd::read_profile_file,
|
|
cmd::save_profile_file,
|
|
cmd::get_next_update_time,
|
|
cmd::create_local_backup,
|
|
cmd::list_local_backup,
|
|
cmd::delete_local_backup,
|
|
cmd::restore_local_backup,
|
|
cmd::import_local_backup,
|
|
cmd::export_local_backup,
|
|
cmd::create_webdav_backup,
|
|
cmd::save_webdav_config,
|
|
cmd::list_webdav_backup,
|
|
cmd::delete_webdav_backup,
|
|
cmd::restore_webdav_backup,
|
|
cmd::get_unlock_items,
|
|
cmd::check_media_unlock,
|
|
cmd::check_media_unlock_item,
|
|
])
|
|
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
|
|
.dangerously_cast_bigints_to_number()
|
|
.typ::<crate::core::notify::notification::VergeEventPayloads>()
|
|
}
|
|
|
|
// Tauri transports YAML values as JSON; keep YAML storage out of the IPC type graph.
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
#[serde(transparent)]
|
|
pub struct YamlMapping(#[specta(type = std::collections::HashMap<String, JsonValue>)] pub serde_yaml_ng::Mapping);
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
#[serde(transparent)]
|
|
pub struct YamlValue(#[specta(type = JsonValue)] pub serde_yaml_ng::Value);
|
|
|
|
#[derive(serde::Serialize, specta::Type)]
|
|
pub struct WebDavFile {
|
|
pub href: String,
|
|
#[specta(type = String)]
|
|
pub last_modified: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
impl From<reqwest_dav::list_cmd::ListFile> for WebDavFile {
|
|
fn from(file: reqwest_dav::list_cmd::ListFile) -> Self {
|
|
Self {
|
|
href: file.href,
|
|
last_modified: file.last_modified,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(serde::Serialize, specta::Type)]
|
|
pub struct NetworkInterfaceView {
|
|
pub name: String,
|
|
pub addr: Vec<InterfaceAddress>,
|
|
pub mac_addr: Option<String>,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
pub enum InterfaceAddress {
|
|
V4(IpAddress),
|
|
V6(IpAddress),
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
pub struct IpAddress {
|
|
pub ip: String,
|
|
}
|
|
|
|
impl From<network_interface::NetworkInterface> for NetworkInterfaceView {
|
|
fn from(interface: network_interface::NetworkInterface) -> Self {
|
|
Self {
|
|
name: interface.name,
|
|
addr: interface
|
|
.addr
|
|
.into_iter()
|
|
.map(|address| match address {
|
|
network_interface::Addr::V4(address) => InterfaceAddress::V4(IpAddress {
|
|
ip: address.ip.to_string(),
|
|
}),
|
|
network_interface::Addr::V6(address) => InterfaceAddress::V6(IpAddress {
|
|
ip: address.ip.to_string(),
|
|
}),
|
|
})
|
|
.collect(),
|
|
mac_addr: interface.mac_addr,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
pub struct DelayHistory {
|
|
pub time: String,
|
|
pub delay: u16,
|
|
}
|
|
|
|
fn platform_types() -> specta::Types {
|
|
use specta::datatype::{DataType, Fields};
|
|
let mut types = specta::Types::default().register::<crate::config::IVerge>();
|
|
<PlatformVergeFields as specta::Type>::definition(&mut types);
|
|
let platform = types
|
|
.into_unsorted_iter()
|
|
.find(|ty| ty.name == "PlatformVergeFields")
|
|
.and_then(|ty| ty.ty.clone());
|
|
let Some(DataType::Struct(platform)) = platform else {
|
|
unreachable!()
|
|
};
|
|
let Fields::Named(platform) = platform.fields else {
|
|
unreachable!()
|
|
};
|
|
types.iter_mut(|ty| {
|
|
if ty.name != "IVerge" {
|
|
return;
|
|
}
|
|
let Some(DataType::Struct(config)) = &mut ty.ty else {
|
|
return;
|
|
};
|
|
let Fields::Named(fields) = &mut config.fields else {
|
|
return;
|
|
};
|
|
fields
|
|
.fields
|
|
.retain(|(name, _)| !platform.fields.iter().any(|(platform_name, _)| name == platform_name));
|
|
fields.fields.extend(platform.fields.iter().cloned());
|
|
});
|
|
types
|
|
}
|
|
|
|
// These fields exist only on some hosts; IPC generation covers every desktop target.
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
struct PlatformVergeFields {
|
|
#[specta(optional)]
|
|
tray_icon: Option<String>,
|
|
#[specta(optional)]
|
|
verge_redir_port: Option<u16>,
|
|
#[specta(optional)]
|
|
verge_redir_enabled: Option<bool>,
|
|
#[specta(optional)]
|
|
verge_tproxy_port: Option<u16>,
|
|
#[specta(optional)]
|
|
verge_tproxy_enabled: Option<bool>,
|
|
#[specta(optional)]
|
|
enable_tray_speed: Option<bool>,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
|
#[serde(untagged)]
|
|
pub enum JsonValue {
|
|
Null(()),
|
|
Bool(bool),
|
|
Number(f64),
|
|
String(String),
|
|
// Specta resolves fields in nested functions, where Self is unavailable.
|
|
Array(#[specta(type = Vec<JsonValue>)] Vec<Self>),
|
|
Object(#[specta(type = std::collections::HashMap<String, JsonValue>)] std::collections::HashMap<String, Self>),
|
|
}
|