diff --git a/UPDATELOG.md b/UPDATELOG.md index 569e0c85..7801efab 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -18,6 +18,7 @@ - Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限 - 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务 - 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置 + - 检测是否以管理员模式运行软件,如果是提示无法使用开机自启 #### 优化了: - 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性 diff --git a/src-tauri/src/cmd/system.rs b/src-tauri/src/cmd/system.rs index 43e80458..6b7453fc 100644 --- a/src-tauri/src/cmd/system.rs +++ b/src-tauri/src/cmd/system.rs @@ -58,3 +58,37 @@ pub fn get_app_uptime() -> CmdResult { Ok(now - start_time) } + +/// 检查应用是否以管理员身份运行 +#[tauri::command] +#[cfg(target_os = "windows")] +pub fn is_admin() -> CmdResult { + use deelevate::{PrivilegeLevel, Token}; + + let result = Token::with_current_process() + .and_then(|token| token.privilege_level()) + .map(|level| level != PrivilegeLevel::NotPrivileged) + .unwrap_or(false); + + Ok(result) +} + +/// 非Windows平台检测是否以管理员身份运行 +#[tauri::command] +#[cfg(not(target_os = "windows"))] +pub fn is_admin() -> CmdResult { + #[cfg(target_os = "macos")] + { + Ok(unsafe { libc::geteuid() } == 0) + } + + #[cfg(target_os = "linux")] + { + Ok(unsafe { libc::geteuid() } == 0) + } + + #[cfg(not(any(target_os = "macos", target_os = "linux")))] + { + Ok(false) + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4a77183f..a02caaa8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -154,6 +154,7 @@ pub fn run() { cmd::get_running_mode, cmd::get_app_uptime, cmd::get_auto_launch_status, + cmd::is_admin, // service 管理 cmd::install_service, cmd::uninstall_service, diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index e5aecf94..86f0ef67 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -1,10 +1,10 @@ import { useTranslation } from "react-i18next"; -import { Typography, Stack, Divider, Chip, IconButton } from "@mui/material"; -import { InfoOutlined, SettingsOutlined } from "@mui/icons-material"; +import { Typography, Stack, Divider, Chip, IconButton, Tooltip } from "@mui/material"; +import { InfoOutlined, SettingsOutlined, WarningOutlined } from "@mui/icons-material"; import { useVerge } from "@/hooks/use-verge"; import { EnhancedCard } from "./enhanced-card"; import useSWR from "swr"; -import { getRunningMode, getSystemInfo, installService } from "@/services/cmds"; +import { getRunningMode, getSystemInfo, installService, isAdmin } from "@/services/cmds"; import { useNavigate } from "react-router-dom"; import { version as appVersion } from "@root/package.json"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -30,6 +30,13 @@ export const SystemInfoCard = () => { { suspense: false, revalidateOnFocus: false }, ); + // 获取管理员状态 + const { data: isAdminMode = false } = useSWR( + "isAdmin", + isAdmin, + { suspense: false, revalidateOnFocus: false }, + ); + // 是否以sidecar模式运行 const isSidecarMode = runningMode === "Sidecar"; @@ -107,13 +114,13 @@ export const SystemInfoCard = () => { // 切换自启动状态 const toggleAutoLaunch = useCallback(async () => { - if (!verge) return; + if (!verge || isAdminMode) return; try { await patchVerge({ enable_auto_launch: !verge.enable_auto_launch }); } catch (err) { console.error("切换开机自启动状态失败:", err); } - }, [verge, patchVerge]); + }, [verge, patchVerge, isAdminMode]); // 安装系统服务 const onInstallService = useLockFn(async () => { @@ -191,18 +198,26 @@ export const SystemInfoCard = () => { - + {t("Auto Launch")} - + + {isAdminMode && ( + + + + )} + + diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx index ca8a94c6..5f431599 100644 --- a/src/components/setting/setting-system.tsx +++ b/src/components/setting/setting-system.tsx @@ -21,6 +21,7 @@ import { getRunningMode, installService, getAutoLaunchStatus, + isAdmin, } from "@/services/cmds"; import { useLockFn } from "ahooks"; import { Box, Button, Tooltip } from "@mui/material"; @@ -45,6 +46,11 @@ const SettingSystem = ({ onError }: Props) => { getAutoLaunchStatus, { revalidateOnFocus: false } ); + const { data: isAdminMode = false } = useSWR( + "isAdmin", + isAdmin, + { revalidateOnFocus: false } + ); // 当实际自启动状态与配置不同步时更新配置 useEffect(() => { @@ -192,14 +198,32 @@ const SettingSystem = ({ onError }: Props) => { - + + + + ) + } + > onChangeData({ enable_auto_launch: e })} + onChange={(e) => { + // 在管理员模式下禁用更改 + if (isAdminMode) return; + onChangeData({ enable_auto_launch: e }); + }} onGuard={async (e) => { + if (isAdminMode) { + Notice.error(t("Administrator mode does not support auto launch"), 2000); + return Promise.reject(new Error(t("Administrator mode does not support auto launch"))); + } + try { // 在应用更改之前先触发UI更新,让用户立即看到反馈 onChangeData({ enable_auto_launch: e }); @@ -214,7 +238,7 @@ const SettingSystem = ({ onError }: Props) => { } }} > - + diff --git a/src/locales/en.json b/src/locales/en.json index e12bd74e..b7d22734 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -254,6 +254,7 @@ "PAC Script Content": "PAC Script Content", "PAC URL": "PAC URL: ", "Auto Launch": "Auto Launch", + "Administrator mode does not support auto launch": "Administrator mode does not support auto launch", "Silent Start": "Silent Start", "Silent Start Info": "Start the program in background mode without displaying the panel", "TG Channel": "Telegram Channel", diff --git a/src/locales/zh.json b/src/locales/zh.json index 9e76b9bc..6f306e12 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -254,6 +254,7 @@ "PAC Script Content": "PAC 脚本内容", "PAC URL": "PAC 地址:", "Auto Launch": "开机自启", + "Administrator mode does not support auto launch": "管理员模式不支持开机自启", "Silent Start": "静默启动", "Silent Start Info": "程序启动时以后台模式运行,不显示程序面板", "TG Channel": "Telegram 频道", diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 2fdb0b0c..81be7b99 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -346,3 +346,12 @@ export const entry_lightweight_mode = async () => { export const exit_lightweight_mode = async () => { return invoke("exit_lightweight_mode"); }; + +export const isAdmin = async () => { + try { + return await invoke("is_admin"); + } catch (error) { + console.error("检查管理员权限失败:", error); + return false; + } +};