mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-04 20:07:32 +08:00
feat: detect admin mode and warn about auto-start unavailability
This commit is contained in:
parent
b092f74c88
commit
52a15bb281
@ -18,6 +18,7 @@
|
|||||||
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
|
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
|
||||||
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
|
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
|
||||||
- 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置
|
- 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置
|
||||||
|
- 检测是否以管理员模式运行软件,如果是提示无法使用开机自启
|
||||||
|
|
||||||
#### 优化了:
|
#### 优化了:
|
||||||
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
|
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
|
||||||
|
@ -58,3 +58,37 @@ pub fn get_app_uptime() -> CmdResult<i64> {
|
|||||||
|
|
||||||
Ok(now - start_time)
|
Ok(now - start_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查应用是否以管理员身份运行
|
||||||
|
#[tauri::command]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn is_admin() -> CmdResult<bool> {
|
||||||
|
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<bool> {
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -154,6 +154,7 @@ pub fn run() {
|
|||||||
cmd::get_running_mode,
|
cmd::get_running_mode,
|
||||||
cmd::get_app_uptime,
|
cmd::get_app_uptime,
|
||||||
cmd::get_auto_launch_status,
|
cmd::get_auto_launch_status,
|
||||||
|
cmd::is_admin,
|
||||||
// service 管理
|
// service 管理
|
||||||
cmd::install_service,
|
cmd::install_service,
|
||||||
cmd::uninstall_service,
|
cmd::uninstall_service,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Typography, Stack, Divider, Chip, IconButton } from "@mui/material";
|
import { Typography, Stack, Divider, Chip, IconButton, Tooltip } from "@mui/material";
|
||||||
import { InfoOutlined, SettingsOutlined } from "@mui/icons-material";
|
import { InfoOutlined, SettingsOutlined, WarningOutlined } from "@mui/icons-material";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { EnhancedCard } from "./enhanced-card";
|
import { EnhancedCard } from "./enhanced-card";
|
||||||
import useSWR from "swr";
|
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 { useNavigate } from "react-router-dom";
|
||||||
import { version as appVersion } from "@root/package.json";
|
import { version as appVersion } from "@root/package.json";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
@ -30,6 +30,13 @@ export const SystemInfoCard = () => {
|
|||||||
{ suspense: false, revalidateOnFocus: false },
|
{ suspense: false, revalidateOnFocus: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 获取管理员状态
|
||||||
|
const { data: isAdminMode = false } = useSWR(
|
||||||
|
"isAdmin",
|
||||||
|
isAdmin,
|
||||||
|
{ suspense: false, revalidateOnFocus: false },
|
||||||
|
);
|
||||||
|
|
||||||
// 是否以sidecar模式运行
|
// 是否以sidecar模式运行
|
||||||
const isSidecarMode = runningMode === "Sidecar";
|
const isSidecarMode = runningMode === "Sidecar";
|
||||||
|
|
||||||
@ -107,13 +114,13 @@ export const SystemInfoCard = () => {
|
|||||||
|
|
||||||
// 切换自启动状态
|
// 切换自启动状态
|
||||||
const toggleAutoLaunch = useCallback(async () => {
|
const toggleAutoLaunch = useCallback(async () => {
|
||||||
if (!verge) return;
|
if (!verge || isAdminMode) return;
|
||||||
try {
|
try {
|
||||||
await patchVerge({ enable_auto_launch: !verge.enable_auto_launch });
|
await patchVerge({ enable_auto_launch: !verge.enable_auto_launch });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("切换开机自启动状态失败:", err);
|
console.error("切换开机自启动状态失败:", err);
|
||||||
}
|
}
|
||||||
}, [verge, patchVerge]);
|
}, [verge, patchVerge, isAdminMode]);
|
||||||
|
|
||||||
// 安装系统服务
|
// 安装系统服务
|
||||||
const onInstallService = useLockFn(async () => {
|
const onInstallService = useLockFn(async () => {
|
||||||
@ -191,18 +198,26 @@ export const SystemInfoCard = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Stack direction="row" justifyContent="space-between">
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
{t("Auto Launch")}
|
{t("Auto Launch")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Chip
|
<Stack direction="row" spacing={1} alignItems="center">
|
||||||
size="small"
|
{isAdminMode && (
|
||||||
label={autoLaunchEnabled ? t("Enabled") : t("Disabled")}
|
<Tooltip title={t("Administrator mode does not support auto launch")}>
|
||||||
color={autoLaunchEnabled ? "success" : "default"}
|
<WarningOutlined sx={{ color: "warning.main", fontSize: 20 }} />
|
||||||
variant={autoLaunchEnabled ? "filled" : "outlined"}
|
</Tooltip>
|
||||||
onClick={toggleAutoLaunch}
|
)}
|
||||||
sx={{ cursor: "pointer" }}
|
<Chip
|
||||||
/>
|
size="small"
|
||||||
|
label={autoLaunchEnabled ? t("Enabled") : t("Disabled")}
|
||||||
|
color={autoLaunchEnabled ? "success" : "default"}
|
||||||
|
variant={autoLaunchEnabled ? "filled" : "outlined"}
|
||||||
|
onClick={toggleAutoLaunch}
|
||||||
|
disabled={isAdminMode}
|
||||||
|
sx={{ cursor: isAdminMode ? "not-allowed" : "pointer" }}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Stack direction="row" justifyContent="space-between">
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
getRunningMode,
|
getRunningMode,
|
||||||
installService,
|
installService,
|
||||||
getAutoLaunchStatus,
|
getAutoLaunchStatus,
|
||||||
|
isAdmin,
|
||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { Box, Button, Tooltip } from "@mui/material";
|
import { Box, Button, Tooltip } from "@mui/material";
|
||||||
@ -45,6 +46,11 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
getAutoLaunchStatus,
|
getAutoLaunchStatus,
|
||||||
{ revalidateOnFocus: false }
|
{ revalidateOnFocus: false }
|
||||||
);
|
);
|
||||||
|
const { data: isAdminMode = false } = useSWR(
|
||||||
|
"isAdmin",
|
||||||
|
isAdmin,
|
||||||
|
{ revalidateOnFocus: false }
|
||||||
|
);
|
||||||
|
|
||||||
// 当实际自启动状态与配置不同步时更新配置
|
// 当实际自启动状态与配置不同步时更新配置
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -192,14 +198,32 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
</GuardState>
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem label={t("Auto Launch")}>
|
<SettingItem
|
||||||
|
label={t("Auto Launch")}
|
||||||
|
extra={
|
||||||
|
isAdminMode && (
|
||||||
|
<Tooltip title={t("Administrator mode does not support auto launch")}>
|
||||||
|
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<GuardState
|
<GuardState
|
||||||
value={enable_auto_launch ?? false}
|
value={enable_auto_launch ?? false}
|
||||||
valueProps="checked"
|
valueProps="checked"
|
||||||
onCatch={onError}
|
onCatch={onError}
|
||||||
onFormat={onSwitchFormat}
|
onFormat={onSwitchFormat}
|
||||||
onChange={(e) => onChangeData({ enable_auto_launch: e })}
|
onChange={(e) => {
|
||||||
|
// 在管理员模式下禁用更改
|
||||||
|
if (isAdminMode) return;
|
||||||
|
onChangeData({ enable_auto_launch: e });
|
||||||
|
}}
|
||||||
onGuard={async (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 {
|
try {
|
||||||
// 在应用更改之前先触发UI更新,让用户立即看到反馈
|
// 在应用更改之前先触发UI更新,让用户立即看到反馈
|
||||||
onChangeData({ enable_auto_launch: e });
|
onChangeData({ enable_auto_launch: e });
|
||||||
@ -214,7 +238,7 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Switch edge="end" />
|
<Switch edge="end" disabled={isAdminMode} />
|
||||||
</GuardState>
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
|
@ -254,6 +254,7 @@
|
|||||||
"PAC Script Content": "PAC Script Content",
|
"PAC Script Content": "PAC Script Content",
|
||||||
"PAC URL": "PAC URL: ",
|
"PAC URL": "PAC URL: ",
|
||||||
"Auto Launch": "Auto Launch",
|
"Auto Launch": "Auto Launch",
|
||||||
|
"Administrator mode does not support auto launch": "Administrator mode does not support auto launch",
|
||||||
"Silent Start": "Silent Start",
|
"Silent Start": "Silent Start",
|
||||||
"Silent Start Info": "Start the program in background mode without displaying the panel",
|
"Silent Start Info": "Start the program in background mode without displaying the panel",
|
||||||
"TG Channel": "Telegram Channel",
|
"TG Channel": "Telegram Channel",
|
||||||
|
@ -254,6 +254,7 @@
|
|||||||
"PAC Script Content": "PAC 脚本内容",
|
"PAC Script Content": "PAC 脚本内容",
|
||||||
"PAC URL": "PAC 地址:",
|
"PAC URL": "PAC 地址:",
|
||||||
"Auto Launch": "开机自启",
|
"Auto Launch": "开机自启",
|
||||||
|
"Administrator mode does not support auto launch": "管理员模式不支持开机自启",
|
||||||
"Silent Start": "静默启动",
|
"Silent Start": "静默启动",
|
||||||
"Silent Start Info": "程序启动时以后台模式运行,不显示程序面板",
|
"Silent Start Info": "程序启动时以后台模式运行,不显示程序面板",
|
||||||
"TG Channel": "Telegram 频道",
|
"TG Channel": "Telegram 频道",
|
||||||
|
@ -346,3 +346,12 @@ export const entry_lightweight_mode = async () => {
|
|||||||
export const exit_lightweight_mode = async () => {
|
export const exit_lightweight_mode = async () => {
|
||||||
return invoke<void>("exit_lightweight_mode");
|
return invoke<void>("exit_lightweight_mode");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isAdmin = async () => {
|
||||||
|
try {
|
||||||
|
return await invoke<boolean>("is_admin");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查管理员权限失败:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user