mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
feat: better service status and TUN mode usable checks in Setting Page
This commit is contained in:
@@ -237,8 +237,7 @@ pub async fn get_dns_config_content() -> CmdResult<String> {
|
||||
/// 验证DNS配置文件
|
||||
#[tauri::command]
|
||||
pub async fn validate_dns_config() -> CmdResult<(bool, String)> {
|
||||
use crate::core::CoreManager;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::CoreManager, utils::dirs};
|
||||
|
||||
let app_dir = dirs::app_home_dir().map_err(|e| e.to_string())?;
|
||||
let dns_path = app_dir.join("dns_config.yaml");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
core::{service, CoreManager},
|
||||
feat,
|
||||
utils::i18n::t,
|
||||
};
|
||||
|
||||
@@ -38,3 +39,11 @@ pub async fn reinstall_service() -> CmdResult {
|
||||
pub async fn repair_service() -> CmdResult {
|
||||
execute_service_operation(service::force_reinstall_service(), "Repair").await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn is_service_available() -> CmdResult<bool> {
|
||||
service::is_service_available()
|
||||
.await
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use crate::utils::network::{NetworkManager, ProxyType};
|
||||
use crate::utils::{dirs, help, tmpl};
|
||||
use crate::utils::{
|
||||
dirs, help,
|
||||
network::{NetworkManager, ProxyType},
|
||||
tmpl,
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use reqwest::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Mapping;
|
||||
use std::fs;
|
||||
use std::time::Duration;
|
||||
use std::{fs, time::Duration};
|
||||
|
||||
use super::Config;
|
||||
|
||||
|
||||
@@ -473,7 +473,7 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
}
|
||||
|
||||
/// 检查服务状态 - 使用IPC通信
|
||||
pub async fn check_service() -> Result<JsonResponse> {
|
||||
pub async fn check_ipc_service_status() -> Result<JsonResponse> {
|
||||
logging!(info, Type::Service, true, "开始检查服务状态 (IPC)");
|
||||
|
||||
// 使用IPC通信
|
||||
@@ -876,7 +876,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||
}
|
||||
|
||||
// 检查服务状态
|
||||
match check_service().await {
|
||||
match check_ipc_service_status().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "服务可用但未运行核心,尝试启动");
|
||||
if let Ok(()) = start_with_existing_service(config_file).await {
|
||||
@@ -947,7 +947,7 @@ pub(super) async fn stop_core_by_service() -> Result<()> {
|
||||
pub async fn is_service_running() -> Result<bool> {
|
||||
logging!(info, Type::Service, true, "开始检查服务是否正在运行");
|
||||
|
||||
match check_service().await {
|
||||
match check_ipc_service_status().await {
|
||||
Ok(resp) => {
|
||||
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
|
||||
logging!(info, Type::Service, true, "服务正在运行");
|
||||
@@ -1010,7 +1010,7 @@ pub async fn is_service_running() -> Result<bool> {
|
||||
pub async fn is_service_available() -> Result<()> {
|
||||
logging!(info, Type::Service, true, "开始检查服务是否可用");
|
||||
|
||||
match check_service().await {
|
||||
match check_ipc_service_status().await {
|
||||
Ok(resp) => {
|
||||
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
|
||||
logging!(info, Type::Service, true, "服务可用");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::logging;
|
||||
use crate::utils::logging::Type;
|
||||
use crate::{logging, utils::logging::Type};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use hmac::{Hmac, Mac};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -117,14 +116,18 @@ pub async fn send_ipc_request(
|
||||
command: IpcCommand,
|
||||
payload: serde_json::Value,
|
||||
) -> Result<IpcResponse> {
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::windows::io::{FromRawHandle, RawHandle};
|
||||
use std::ptr;
|
||||
use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
|
||||
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
|
||||
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE};
|
||||
use std::{
|
||||
ffi::CString,
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
os::windows::io::{FromRawHandle, RawHandle},
|
||||
ptr,
|
||||
};
|
||||
use winapi::um::{
|
||||
fileapi::{CreateFileA, OPEN_EXISTING},
|
||||
handleapi::INVALID_HANDLE_VALUE,
|
||||
winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE},
|
||||
};
|
||||
|
||||
logging!(
|
||||
info,
|
||||
|
||||
@@ -5,13 +5,14 @@ pub mod speed_rate;
|
||||
use crate::{
|
||||
cmd,
|
||||
config::Config,
|
||||
feat,
|
||||
feat, logging,
|
||||
module::{
|
||||
lightweight::{entry_lightweight_mode, is_in_lightweight_mode},
|
||||
mihomo::Rate,
|
||||
},
|
||||
resolve,
|
||||
utils::{dirs::find_target_icons, i18n::t, resolve::VERSION},
|
||||
Type,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -828,7 +829,13 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
||||
match event.id.as_ref() {
|
||||
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
|
||||
let mode = &mode[0..mode.len() - 5];
|
||||
println!("change mode to: {}", mode);
|
||||
logging!(
|
||||
info,
|
||||
Type::ProxyMode,
|
||||
true,
|
||||
"Switch Proxy Mode To: {}",
|
||||
mode
|
||||
);
|
||||
feat::change_clash_mode(mode.into());
|
||||
}
|
||||
"open_window" => {
|
||||
|
||||
@@ -240,8 +240,10 @@ pub struct Traffic {
|
||||
impl Traffic {
|
||||
pub async fn get_traffic_stream() -> Result<impl Stream<Item = Result<Traffic, anyhow::Error>>>
|
||||
{
|
||||
use futures::future::FutureExt;
|
||||
use futures::stream::{self, StreamExt};
|
||||
use futures::{
|
||||
future::FutureExt,
|
||||
stream::{self, StreamExt},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
// 先处理错误和超时情况
|
||||
|
||||
@@ -43,19 +43,6 @@ pub fn toggle_system_proxy() {
|
||||
|
||||
/// Toggle TUN mode on/off
|
||||
pub fn toggle_tun_mode(not_save_file: Option<bool>) {
|
||||
// AsyncHandler::spawn(async {
|
||||
// logging!(
|
||||
// info,
|
||||
// Type::Service,
|
||||
// true,
|
||||
// "Toggle TUN mode need install service"
|
||||
// );
|
||||
// if is_service_available().await.is_err() {
|
||||
// logging_error!(Type::Service, true, install_service().await);
|
||||
// }
|
||||
// logging_error!(Type::Core, true, CoreManager::global().restart_core().await);
|
||||
// });
|
||||
|
||||
let enable = Config::verge().data().enable_tun_mode;
|
||||
let enable = enable.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ pub fn run() {
|
||||
cmd::uninstall_service,
|
||||
cmd::reinstall_service,
|
||||
cmd::repair_service,
|
||||
cmd::is_service_available,
|
||||
// clash
|
||||
cmd::get_clash_info,
|
||||
cmd::patch_clash_config,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::fmt;
|
||||
use std::fmt::{self, write};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
@@ -16,6 +16,7 @@ pub enum Type {
|
||||
Backup,
|
||||
Lightweight,
|
||||
Network,
|
||||
ProxyMode,
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
@@ -35,6 +36,7 @@ impl fmt::Display for Type {
|
||||
Type::Backup => write!(f, "[Backup]"),
|
||||
Type::Lightweight => write!(f, "[Lightweight]"),
|
||||
Type::Network => write!(f, "[Network]"),
|
||||
Type::ProxyMode => write!(f, "[ProxMode]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::{Client, ClientBuilder, Proxy, RequestBuilder, Response};
|
||||
use std::sync::{Arc, Mutex, Once};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
sync::{Arc, Mutex, Once},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::runtime::{Builder, Runtime};
|
||||
|
||||
use crate::{config::Config, logging, utils::logging::Type};
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
getAutotemProxy,
|
||||
installService,
|
||||
getAutoLaunchStatus,
|
||||
restartCore,
|
||||
} from "@/services/cmds";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { Button, Tooltip } from "@mui/material";
|
||||
@@ -41,25 +42,18 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
const { data: autoLaunchEnabled } = useSWR(
|
||||
"getAutoLaunchStatus",
|
||||
getAutoLaunchStatus,
|
||||
{ revalidateOnFocus: false }
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
const { isAdminMode, isSidecarMode, mutateRunningMode } = useSystemState();
|
||||
const { isAdminMode, isSidecarMode, mutateRunningMode, isServiceOk } =
|
||||
useSystemState();
|
||||
|
||||
console.log("Is service running:", isServiceOk);
|
||||
|
||||
// 判断Tun模式是否可用 - 当处于服务模式或管理员模式时可用
|
||||
const isTunAvailable = !isSidecarMode || isAdminMode;
|
||||
const isTunAvailable = isServiceOk || (isSidecarMode && !isAdminMode);
|
||||
|
||||
// 当实际自启动状态与配置不同步时更新配置
|
||||
useEffect(() => {
|
||||
if (
|
||||
autoLaunchEnabled !== undefined &&
|
||||
verge &&
|
||||
verge.enable_auto_launch !== autoLaunchEnabled
|
||||
) {
|
||||
// 静默更新配置,不触发UI刷新
|
||||
mutateVerge({ ...verge, enable_auto_launch: autoLaunchEnabled }, false);
|
||||
}
|
||||
}, [autoLaunchEnabled]);
|
||||
console.log("is tun isTunAvailable:", isTunAvailable);
|
||||
|
||||
const sysproxyRef = useRef<DialogRef>(null);
|
||||
const tunRef = useRef<DialogRef>(null);
|
||||
@@ -87,13 +81,16 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
// 安装系统服务
|
||||
const onInstallService = useLockFn(async () => {
|
||||
try {
|
||||
showNotice('info', t("Installing Service..."), 1000);
|
||||
showNotice("info", t("Installing Service..."), 1000);
|
||||
await installService();
|
||||
showNotice('success', t("Service Installed Successfully"), 2000);
|
||||
showNotice("success", t("Service Installed Successfully"), 2000);
|
||||
await restartCore();
|
||||
showNotice("info", t("Restarting Core"), 1000);
|
||||
console.log("restartCore");
|
||||
// 重新获取运行模式
|
||||
await mutateRunningMode();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString(), 3000);
|
||||
showNotice("error", err.message || err.toString(), 3000);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,12 +108,12 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
icon={SettingsRounded}
|
||||
onClick={() => tunRef.current?.open()}
|
||||
/>
|
||||
{isSidecarMode && !isAdminMode && (
|
||||
{!isServiceOk && !isAdminMode && (
|
||||
<Tooltip title={t("TUN requires Service Mode")}>
|
||||
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{isSidecarMode && !isAdminMode && (
|
||||
{!isServiceOk && !isAdminMode && (
|
||||
<Tooltip title={t("Install Service")}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -138,20 +135,20 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
onCatch={onError}
|
||||
onFormat={onSwitchFormat}
|
||||
onChange={(e) => {
|
||||
// 当在sidecar模式下且非管理员模式时禁用切换
|
||||
if (isSidecarMode && !isAdminMode) return;
|
||||
// 非 Service 状态禁止切换
|
||||
if (!isServiceOk) return;
|
||||
onChangeData({ enable_tun_mode: e });
|
||||
}}
|
||||
onGuard={(e) => {
|
||||
// 当在sidecar模式下且非管理员模式时禁用切换
|
||||
if (isSidecarMode && !isAdminMode) {
|
||||
showNotice('error', t("TUN requires Service Mode"), 2000);
|
||||
// 非 Service 状态禁止切换
|
||||
if (!isServiceOk) {
|
||||
showNotice("error", t("TUN requires Service Mode"), 2000);
|
||||
return Promise.reject(new Error(t("TUN requires Service Mode")));
|
||||
}
|
||||
return patchVerge({ enable_tun_mode: e });
|
||||
}}
|
||||
>
|
||||
<Switch edge="end" disabled={isSidecarMode && !isAdminMode} />
|
||||
<Switch edge="end" disabled={isServiceOk} />
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
@@ -195,11 +192,13 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
<SettingItem
|
||||
label={t("Auto Launch")}
|
||||
extra={
|
||||
isAdminMode && (
|
||||
<Tooltip title={t("Administrator mode may not support auto launch")}>
|
||||
<Tooltip
|
||||
title={t("Administrator mode may not support auto launch")}
|
||||
>
|
||||
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
|
||||
</Tooltip>
|
||||
)
|
||||
@@ -216,9 +215,13 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
}}
|
||||
onGuard={async (e) => {
|
||||
if (isAdminMode) {
|
||||
showNotice('info', t("Administrator mode may not support auto launch"), 2000);
|
||||
showNotice(
|
||||
"info",
|
||||
t("Administrator mode may not support auto launch"),
|
||||
2000,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 在应用更改之前先触发UI更新,让用户立即看到反馈
|
||||
onChangeData({ enable_auto_launch: e });
|
||||
|
||||
@@ -1,29 +1,42 @@
|
||||
import useSWR from "swr";
|
||||
import { getRunningMode, isAdmin } from "@/services/cmds";
|
||||
import { getRunningMode, isAdmin, isServiceAvailable } from "@/services/cmds";
|
||||
|
||||
/**
|
||||
* 自定义 hook 用于获取系统运行状态
|
||||
* 包括运行模式和管理员状态
|
||||
* 包括运行模式、管理员状态、系统服务是否可用
|
||||
*/
|
||||
export function useSystemState() {
|
||||
// 获取运行模式
|
||||
const { data: runningMode = "Sidecar", mutate: mutateRunningMode } =
|
||||
useSWR("getRunningMode", getRunningMode, {
|
||||
const { data: runningMode = "Sidecar", mutate: mutateRunningMode } = useSWR(
|
||||
"getRunningMode",
|
||||
getRunningMode,
|
||||
{
|
||||
suspense: false,
|
||||
revalidateOnFocus: false
|
||||
});
|
||||
|
||||
revalidateOnFocus: false,
|
||||
},
|
||||
);
|
||||
|
||||
// 获取管理员状态
|
||||
const { data: isAdminMode = false } =
|
||||
useSWR("isAdmin", isAdmin, {
|
||||
const { data: isAdminMode = false } = useSWR("isAdmin", isAdmin, {
|
||||
suspense: false,
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
// 获取系统服务状态
|
||||
const { data: isServiceOk = false } = useSWR(
|
||||
"isServiceAvailable",
|
||||
isServiceAvailable,
|
||||
{
|
||||
suspense: false,
|
||||
revalidateOnFocus: false
|
||||
});
|
||||
|
||||
revalidateOnFocus: false,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
runningMode,
|
||||
isAdminMode,
|
||||
isSidecarMode: runningMode === "Sidecar",
|
||||
mutateRunningMode
|
||||
mutateRunningMode,
|
||||
isServiceOk,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,19 +145,19 @@ export async function getAppDir() {
|
||||
|
||||
export async function openAppDir() {
|
||||
return invoke<void>("open_app_dir").catch((err) =>
|
||||
showNotice('error', err?.message || err.toString()),
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
export async function openCoreDir() {
|
||||
return invoke<void>("open_core_dir").catch((err) =>
|
||||
showNotice('error', err?.message || err.toString()),
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
export async function openLogsDir() {
|
||||
return invoke<void>("open_logs_dir").catch((err) =>
|
||||
showNotice('error', err?.message || err.toString()),
|
||||
showNotice("error", err?.message || err.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ export const openWebUrl = async (url: string) => {
|
||||
try {
|
||||
await invoke("open_web_url", { url });
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -218,7 +218,7 @@ export async function cmdTestDelay(url: string) {
|
||||
|
||||
export async function invoke_uwp_tool() {
|
||||
return invoke<void>("invoke_uwp_tool").catch((err) =>
|
||||
showNotice('error', err?.message || err.toString(), 1500),
|
||||
showNotice("error", err?.message || err.toString(), 1500),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -347,6 +347,15 @@ export const repairService = async () => {
|
||||
return invoke<void>("repair_service");
|
||||
};
|
||||
|
||||
// 系统服务是否可用
|
||||
export const isServiceAvailable = async () => {
|
||||
try {
|
||||
return await invoke<boolean>("is_service_available");
|
||||
} catch (error) {
|
||||
console.error("Service check failed:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
export const entry_lightweight_mode = async () => {
|
||||
return invoke<void>("entry_lightweight_mode");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user