mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-28 07:14:40 +08:00
feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval (#5510)
* feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval * feat(sysinfo): add tauri-plugin-clash-verge-sysinfo for system information retrieval * fix(service): import Manager trait for app handle in linux_running_as_root function
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -1130,7 +1130,6 @@ dependencies = [
|
||||
"futures",
|
||||
"gethostname",
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
"log",
|
||||
"nanoid",
|
||||
"network-interface",
|
||||
@@ -1150,11 +1149,11 @@ dependencies = [
|
||||
"serde_yaml_ng",
|
||||
"smartstring",
|
||||
"sys-locale",
|
||||
"sysinfo",
|
||||
"sysproxy",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-autostart",
|
||||
"tauri-plugin-clash-verge-sysinfo",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-deep-link",
|
||||
"tauri-plugin-devtools",
|
||||
@@ -7473,6 +7472,17 @@ dependencies = [
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-clash-verge-sysinfo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"deelevate",
|
||||
"libc",
|
||||
"parking_lot",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-clipboard-manager"
|
||||
version = "2.3.2"
|
||||
|
||||
@@ -4,6 +4,7 @@ members = [
|
||||
"crates/clash-verge-draft",
|
||||
"crates/clash-verge-logging",
|
||||
"crates/clash-verge-signal",
|
||||
"crates/tauri-plugin-clash-verge-sysinfo",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@@ -44,6 +45,7 @@ strip = false
|
||||
clash-verge-draft = { path = "crates/clash-verge-draft" }
|
||||
clash-verge-logging = { path = "crates/clash-verge-logging" }
|
||||
clash-verge-signal = { path = "crates/clash-verge-signal" }
|
||||
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
|
||||
tauri = { version = "2.9.3" }
|
||||
parking_lot = { version = "0.12.5", features = [
|
||||
"hardware-lock-elision",
|
||||
@@ -61,6 +63,10 @@ compact_str = { version = "0.9.0", features = ["serde"] }
|
||||
flexi_logger = "0.31.7"
|
||||
log = "0.4.28"
|
||||
|
||||
# *** For Windows platform only ***
|
||||
deelevate = "0.2.0"
|
||||
# *********************************
|
||||
|
||||
[workspace.lints.clippy]
|
||||
correctness = { level = "deny", priority = -1 }
|
||||
suspicious = { level = "deny", priority = -1 }
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
- i18n 支持
|
||||
- 优化备份设置布局
|
||||
- 优化流量图性能表现,实现动态 FPS 和窗口失焦自动暂停
|
||||
- 性能优化系统状态获取
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
19
crates/tauri-plugin-clash-verge-sysinfo/Cargo.toml
Normal file
19
crates/tauri-plugin-clash-verge-sysinfo/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "tauri-plugin-clash-verge-sysinfo"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
sysinfo = { version = "0.37.2", features = ["network", "system"] }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
libc = "0.2.177"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
deelevate = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
150
crates/tauri-plugin-clash-verge-sysinfo/src/lib.rs
Normal file
150
crates/tauri-plugin-clash-verge-sysinfo/src/lib.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use deelevate::{PrivilegeLevel, Token};
|
||||
use parking_lot::RwLock;
|
||||
use sysinfo::{Networks, System};
|
||||
use tauri::{
|
||||
Manager as _, Runtime,
|
||||
plugin::{Builder, TauriPlugin},
|
||||
};
|
||||
|
||||
pub struct SysInfo {
|
||||
system_name: String,
|
||||
system_version: String,
|
||||
system_kernel_version: String,
|
||||
system_arch: String,
|
||||
}
|
||||
|
||||
impl Default for SysInfo {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
let system_name = System::name().unwrap_or_else(|| "Null".into());
|
||||
let system_version = System::long_os_version().unwrap_or_else(|| "Null".into());
|
||||
let system_kernel_version = System::kernel_version().unwrap_or_else(|| "Null".into());
|
||||
let system_arch = System::cpu_arch();
|
||||
Self {
|
||||
system_name,
|
||||
system_version,
|
||||
system_kernel_version,
|
||||
system_arch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppInfo {
|
||||
app_version: String,
|
||||
app_core_mode: String,
|
||||
pub app_startup_time: Instant,
|
||||
pub app_is_admin: bool,
|
||||
}
|
||||
|
||||
impl Default for AppInfo {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
let app_version = "0.0.0".into();
|
||||
let app_core_mode = "NotRunning".into();
|
||||
let app_is_admin = false;
|
||||
let app_startup_time = Instant::now();
|
||||
Self {
|
||||
app_version,
|
||||
app_core_mode,
|
||||
app_startup_time,
|
||||
app_is_admin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Platform {
|
||||
pub sysinfo: SysInfo,
|
||||
pub appinfo: AppInfo,
|
||||
}
|
||||
|
||||
impl Debug for Platform {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Platform")
|
||||
.field("system_name", &self.sysinfo.system_name)
|
||||
.field("system_version", &self.sysinfo.system_version)
|
||||
.field("system_kernel_version", &self.sysinfo.system_kernel_version)
|
||||
.field("system_arch", &self.sysinfo.system_arch)
|
||||
.field("app_version", &self.appinfo.app_version)
|
||||
.field("app_core_mode", &self.appinfo.app_core_mode)
|
||||
.field("app_is_admin", &self.appinfo.app_is_admin)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Platform {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}\nVerge Version: {}\nRunning Mode: {}\nIs Admin: {}",
|
||||
self.sysinfo.system_name,
|
||||
self.sysinfo.system_version,
|
||||
self.sysinfo.system_kernel_version,
|
||||
self.sysinfo.system_arch,
|
||||
self.appinfo.app_version,
|
||||
self.appinfo.app_core_mode,
|
||||
self.appinfo.app_is_admin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
#[inline]
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_binary_admin() -> bool {
|
||||
#[cfg(not(windows))]
|
||||
unsafe {
|
||||
libc::geteuid() == 0
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Token::with_current_process()
|
||||
.and_then(|token| token.privilege_level())
|
||||
.map(|level| level != PrivilegeLevel::NotPrivileged)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn list_network_interfaces() -> Vec<String> {
|
||||
let mut networks = Networks::new();
|
||||
networks.refresh(false);
|
||||
networks.keys().map(|name| name.to_owned()).collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_app_core_mode<R: Runtime>(app: &tauri::AppHandle<R>, mode: impl Into<String>) {
|
||||
let platform_spec = app.state::<RwLock<Platform>>();
|
||||
let mut spec = platform_spec.write();
|
||||
spec.appinfo.app_core_mode = mode.into();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("clash_verge_sysinfo")
|
||||
// TODO 从 clash-verge 中迁移获取系统信息的 commnand 并实现优雅 structure.field 访问
|
||||
// .invoke_handler(tauri::generate_handler![greet])
|
||||
.setup(move |app, _api| {
|
||||
let app_version = app.package_info().version.to_string();
|
||||
let is_admin = is_binary_admin();
|
||||
|
||||
let mut platform_spec = Platform::new();
|
||||
platform_spec.appinfo.app_version = app_version;
|
||||
platform_spec.appinfo.app_is_admin = is_admin;
|
||||
|
||||
app.manage(RwLock::new(platform_spec));
|
||||
Ok(())
|
||||
})
|
||||
.build()
|
||||
}
|
||||
@@ -33,6 +33,7 @@ tauri-build = { version = "2.5.2", features = [] }
|
||||
clash-verge-draft = { workspace = true }
|
||||
clash-verge-logging = { workspace = true }
|
||||
clash-verge-signal = { workspace = true }
|
||||
tauri-plugin-clash-verge-sysinfo = { workspace = true }
|
||||
tauri = { workspace = true, features = [
|
||||
"protocol-asset",
|
||||
"devtools",
|
||||
@@ -51,7 +52,6 @@ open = "5.3.3"
|
||||
dunce = "1.0.5"
|
||||
nanoid = "0.4"
|
||||
chrono = "0.4.42"
|
||||
sysinfo = { version = "0.37.2", features = ["network", "system"] }
|
||||
boa_engine = "0.21.0"
|
||||
serde_json = "1.0.145"
|
||||
serde_yaml_ng = "0.10.0"
|
||||
@@ -80,7 +80,6 @@ base64 = "0.22.1"
|
||||
getrandom = "0.3.4"
|
||||
futures = "0.3.31"
|
||||
sys-locale = "0.3.2"
|
||||
libc = "0.2.177"
|
||||
gethostname = "1.1.0"
|
||||
scopeguard = "1.2.0"
|
||||
tauri-plugin-notification = "2.3.3"
|
||||
@@ -100,8 +99,8 @@ arc-swap = "1.7.1"
|
||||
rust-i18n = "3.1.5"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
deelevate = { workspace = true }
|
||||
runas = "=1.2.0"
|
||||
deelevate = "0.2.0"
|
||||
winreg = "0.55.0"
|
||||
winapi = { version = "0.3.9", features = [
|
||||
"winbase",
|
||||
|
||||
@@ -4,6 +4,7 @@ use clash_verge_logging::{Type, logging};
|
||||
use gethostname::gethostname;
|
||||
use network_interface::NetworkInterface;
|
||||
use serde_yaml_ng::Mapping;
|
||||
use tauri_plugin_clash_verge_sysinfo;
|
||||
|
||||
/// get the system proxy
|
||||
#[tauri::command]
|
||||
@@ -68,13 +69,7 @@ pub fn get_system_hostname() -> String {
|
||||
/// 获取网络接口列表
|
||||
#[tauri::command]
|
||||
pub fn get_network_interfaces() -> Vec<String> {
|
||||
use sysinfo::Networks;
|
||||
let mut result = Vec::new();
|
||||
let networks = Networks::new_with_refreshed_list();
|
||||
for (interface_name, _) in &networks {
|
||||
result.push(interface_name.clone());
|
||||
}
|
||||
result
|
||||
tauri_plugin_clash_verge_sysinfo::list_network_interfaces()
|
||||
}
|
||||
|
||||
/// 获取网络接口详细信息
|
||||
|
||||
@@ -1,33 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
core::{CoreManager, handle, manager::RunningMode},
|
||||
module::sysinfo::PlatformSpecification,
|
||||
};
|
||||
use crate::core::{CoreManager, handle, manager::RunningMode};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
#[cfg(target_os = "windows")]
|
||||
use deelevate::{PrivilegeLevel, Token};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_clash_verge_sysinfo::Platform;
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt as _;
|
||||
use tokio::time::Instant;
|
||||
|
||||
// 存储应用启动时间的全局变量
|
||||
static APP_START_TIME: Lazy<Instant> = Lazy::new(Instant::now);
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
static APPS_RUN_AS_ADMIN: Lazy<bool> = Lazy::new(|| unsafe { libc::geteuid() } == 0);
|
||||
#[cfg(target_os = "windows")]
|
||||
static APPS_RUN_AS_ADMIN: Lazy<bool> = Lazy::new(|| {
|
||||
Token::with_current_process()
|
||||
.and_then(|token| token.privilege_level())
|
||||
.map(|level| level != PrivilegeLevel::NotPrivileged)
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
let sysinfo = PlatformSpecification::new_sync();
|
||||
let info = format!("{sysinfo:?}");
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let info = app_handle.state::<RwLock<Platform>>().read().to_string();
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let cliboard = app_handle.clipboard();
|
||||
@@ -37,10 +21,11 @@ pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO 迁移,让新的结构体允许通过 tauri command 正确使用 structure.field 方式获取信息
|
||||
#[tauri::command]
|
||||
pub async fn get_system_info() -> CmdResult<String> {
|
||||
let sysinfo = PlatformSpecification::new_sync();
|
||||
let info = format!("{sysinfo:?}");
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let info = app_handle.state::<RwLock<Platform>>().read().to_string();
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
@@ -53,11 +38,22 @@ pub async fn get_running_mode() -> Result<Arc<RunningMode>, String> {
|
||||
/// 获取应用的运行时间(毫秒)
|
||||
#[tauri::command]
|
||||
pub fn get_app_uptime() -> u128 {
|
||||
APP_START_TIME.elapsed().as_millis()
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let startup_time = app_handle
|
||||
.state::<RwLock<Platform>>()
|
||||
.read()
|
||||
.appinfo
|
||||
.app_startup_time;
|
||||
startup_time.elapsed().as_millis()
|
||||
}
|
||||
|
||||
/// 检查应用是否以管理员身份运行
|
||||
#[tauri::command]
|
||||
pub fn is_admin() -> bool {
|
||||
*APPS_RUN_AS_ADMIN
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
app_handle
|
||||
.state::<RwLock<Platform>>()
|
||||
.read()
|
||||
.appinfo
|
||||
.app_is_admin
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
use super::{CoreManager, RunningMode};
|
||||
use crate::cmd::StringifyErr as _;
|
||||
use crate::config::{Config, IVerge};
|
||||
use crate::core::handle::Handle;
|
||||
use crate::core::{
|
||||
logger::CLASH_LOGGER,
|
||||
service::{SERVICE_MANAGER, ServiceStatus},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use scopeguard::defer;
|
||||
use smartstring::alias::String;
|
||||
use tauri_plugin_clash_verge_sysinfo;
|
||||
|
||||
impl CoreManager {
|
||||
pub async fn start_core(&self) -> Result<()> {
|
||||
self.prepare_startup().await?;
|
||||
defer! {
|
||||
self.after_core_process();
|
||||
}
|
||||
|
||||
match *self.get_running_mode() {
|
||||
RunningMode::Service => self.start_core_by_service().await,
|
||||
@@ -21,6 +27,9 @@ impl CoreManager {
|
||||
|
||||
pub async fn stop_core(&self) -> Result<()> {
|
||||
CLASH_LOGGER.clear_logs().await;
|
||||
defer! {
|
||||
self.after_core_process();
|
||||
}
|
||||
|
||||
match *self.get_running_mode() {
|
||||
RunningMode::Service => self.stop_core_by_service().await,
|
||||
@@ -74,6 +83,14 @@ impl CoreManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn after_core_process(&self) {
|
||||
let app_handle = Handle::app_handle();
|
||||
tauri_plugin_clash_verge_sysinfo::set_app_core_mode(
|
||||
app_handle,
|
||||
self.get_running_mode().to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
async fn wait_for_service_if_needed(&self) {
|
||||
use crate::{config::Config, constants::timing};
|
||||
|
||||
@@ -254,9 +254,16 @@ async fn reinstall_service() -> Result<()> {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn linux_running_as_root() -> bool {
|
||||
const ROOT_UID: u32 = 0;
|
||||
|
||||
unsafe { libc::geteuid() == ROOT_UID }
|
||||
use crate::core::handle;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::Manager as _;
|
||||
use tauri_plugin_clash_verge_sysinfo::Platform;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
app_handle
|
||||
.state::<RwLock<Platform>>()
|
||||
.read()
|
||||
.appinfo
|
||||
.app_is_admin
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -57,6 +57,7 @@ mod app_init {
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_clash_verge_sysinfo::init())
|
||||
.plugin(
|
||||
tauri_plugin_mihomo::Builder::new()
|
||||
.protocol(tauri_plugin_mihomo::models::Protocol::LocalSocket)
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
pub mod auto_backup;
|
||||
pub mod lightweight;
|
||||
pub mod sysinfo;
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
use crate::{
|
||||
cmd::system,
|
||||
core::{CoreManager, handle},
|
||||
};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use sysinfo::System;
|
||||
|
||||
pub struct PlatformSpecification {
|
||||
system_name: String,
|
||||
system_version: String,
|
||||
system_kernel_version: String,
|
||||
system_arch: String,
|
||||
verge_version: String,
|
||||
running_mode: String,
|
||||
is_admin: bool,
|
||||
}
|
||||
|
||||
impl Debug for PlatformSpecification {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}\nVerge Version: {}\nRunning Mode: {}\nIs Admin: {}",
|
||||
self.system_name,
|
||||
self.system_version,
|
||||
self.system_kernel_version,
|
||||
self.system_arch,
|
||||
self.verge_version,
|
||||
self.running_mode,
|
||||
self.is_admin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSpecification {
|
||||
pub fn new() -> Self {
|
||||
let system_name = System::name().unwrap_or_else(|| "Null".into());
|
||||
let system_version = System::long_os_version().unwrap_or_else(|| "Null".into());
|
||||
let system_kernel_version = System::kernel_version().unwrap_or_else(|| "Null".into());
|
||||
let system_arch = System::cpu_arch();
|
||||
|
||||
let handler = handle::Handle::app_handle();
|
||||
let verge_version = handler.package_info().version.to_string();
|
||||
|
||||
// 使用默认值避免在同步上下文中执行异步操作
|
||||
let running_mode = "NotRunning".to_string();
|
||||
|
||||
let is_admin = system::is_admin();
|
||||
|
||||
Self {
|
||||
system_name,
|
||||
system_version,
|
||||
system_kernel_version,
|
||||
system_arch,
|
||||
verge_version,
|
||||
running_mode,
|
||||
is_admin,
|
||||
}
|
||||
}
|
||||
|
||||
// 异步方法来获取完整的系统信息
|
||||
pub fn new_sync() -> Self {
|
||||
let mut info = Self::new();
|
||||
|
||||
let running_mode = CoreManager::global().get_running_mode();
|
||||
info.running_mode = running_mode.to_string();
|
||||
|
||||
info
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user