mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 06:43:44 +08:00
feat: Service Mode for Linux (#804)
This commit is contained in:
parent
503579a638
commit
b0f1ce1fa0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
.pnpm-store
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
@ -353,6 +353,23 @@ const resolvePlugin = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Linux service chmod
|
||||||
|
const resolveLinuxServicePermission = async () => {
|
||||||
|
const serviceExecutables = [
|
||||||
|
"clash-verge-service",
|
||||||
|
"install-service",
|
||||||
|
"uninstall-service",
|
||||||
|
];
|
||||||
|
const resDir = path.join(cwd, "src-tauri/resources");
|
||||||
|
for (f of serviceExecutables) {
|
||||||
|
const targetPath = path.join(resDir, f);
|
||||||
|
if (await fs.pathExists(targetPath)) {
|
||||||
|
execSync(`chmod 755 ${targetPath}`);
|
||||||
|
console.log(`[INFO]: "${targetPath}" chmod finished`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main
|
* main
|
||||||
*/
|
*/
|
||||||
@ -364,16 +381,32 @@ const resolveService = () =>
|
|||||||
file: "clash-verge-service.exe",
|
file: "clash-verge-service.exe",
|
||||||
downloadURL: `${SERVICE_URL}/clash-verge-service.exe`,
|
downloadURL: `${SERVICE_URL}/clash-verge-service.exe`,
|
||||||
});
|
});
|
||||||
|
const resolveLinuxService = () => {
|
||||||
|
resolveResource({
|
||||||
|
file: "clash-verge-service",
|
||||||
|
downloadURL: `${SERVICE_URL}/clash-verge-service`,
|
||||||
|
});
|
||||||
|
};
|
||||||
const resolveInstall = () =>
|
const resolveInstall = () =>
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "install-service.exe",
|
file: "install-service.exe",
|
||||||
downloadURL: `${SERVICE_URL}/install-service.exe`,
|
downloadURL: `${SERVICE_URL}/install-service.exe`,
|
||||||
});
|
});
|
||||||
|
const resolveLinuxInstall = () =>
|
||||||
|
resolveResource({
|
||||||
|
file: "install-service",
|
||||||
|
downloadURL: `${SERVICE_URL}/install-service`,
|
||||||
|
});
|
||||||
const resolveUninstall = () =>
|
const resolveUninstall = () =>
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "uninstall-service.exe",
|
file: "uninstall-service.exe",
|
||||||
downloadURL: `${SERVICE_URL}/uninstall-service.exe`,
|
downloadURL: `${SERVICE_URL}/uninstall-service.exe`,
|
||||||
});
|
});
|
||||||
|
const resolveLinuxUninstall = () =>
|
||||||
|
resolveResource({
|
||||||
|
file: "uninstall-service",
|
||||||
|
downloadURL: `${SERVICE_URL}/uninstall-service`,
|
||||||
|
});
|
||||||
const resolveSetDnsScript = () =>
|
const resolveSetDnsScript = () =>
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "set_dns.sh",
|
file: "set_dns.sh",
|
||||||
@ -421,8 +454,26 @@ const tasks = [
|
|||||||
},
|
},
|
||||||
{ name: "plugin", func: resolvePlugin, retry: 5, winOnly: true },
|
{ name: "plugin", func: resolvePlugin, retry: 5, winOnly: true },
|
||||||
{ name: "service", func: resolveService, retry: 5, winOnly: true },
|
{ name: "service", func: resolveService, retry: 5, winOnly: true },
|
||||||
|
{
|
||||||
|
name: "linux_service",
|
||||||
|
func: resolveLinuxService,
|
||||||
|
retry: 5,
|
||||||
|
linuxOnly: true,
|
||||||
|
},
|
||||||
{ name: "install", func: resolveInstall, retry: 5, winOnly: true },
|
{ name: "install", func: resolveInstall, retry: 5, winOnly: true },
|
||||||
|
{
|
||||||
|
name: "linux_install",
|
||||||
|
func: resolveLinuxInstall,
|
||||||
|
retry: 5,
|
||||||
|
linuxOnly: true,
|
||||||
|
},
|
||||||
{ name: "uninstall", func: resolveUninstall, retry: 5, winOnly: true },
|
{ name: "uninstall", func: resolveUninstall, retry: 5, winOnly: true },
|
||||||
|
{
|
||||||
|
name: "linux_uninstall",
|
||||||
|
func: resolveLinuxUninstall,
|
||||||
|
retry: 5,
|
||||||
|
linuxOnly: true,
|
||||||
|
},
|
||||||
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
|
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
|
||||||
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
|
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
|
||||||
{ name: "mmdb", func: resolveMmdb, retry: 5 },
|
{ name: "mmdb", func: resolveMmdb, retry: 5 },
|
||||||
@ -434,12 +485,19 @@ const tasks = [
|
|||||||
retry: 5,
|
retry: 5,
|
||||||
winOnly: true,
|
winOnly: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "linux_service_chmod",
|
||||||
|
func: resolveLinuxServicePermission,
|
||||||
|
retry: 1,
|
||||||
|
linuxOnly: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
async function runTask() {
|
async function runTask() {
|
||||||
const task = tasks.shift();
|
const task = tasks.shift();
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
if (task.winOnly && process.platform !== "win32") return runTask();
|
if (task.winOnly && process.platform !== "win32") return runTask();
|
||||||
|
if (task.linuxOnly && process.platform !== "linux") return runTask();
|
||||||
|
|
||||||
for (let i = 0; i < task.retry; i++) {
|
for (let i = 0; i < task.retry; i++) {
|
||||||
try {
|
try {
|
||||||
|
@ -45,6 +45,7 @@ deelevate = "0.2.0"
|
|||||||
winreg = "0.52.0"
|
winreg = "0.52.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
users = "0.11.0"
|
||||||
#openssl
|
#openssl
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -331,28 +331,28 @@ pub fn exit_app(app_handle: tauri::AppHandle) {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(windows, target_os = "linux"))]
|
||||||
pub mod service {
|
pub mod service {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::win_service;
|
use crate::core::service;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn check_service() -> CmdResult<win_service::JsonResponse> {
|
pub async fn check_service() -> CmdResult<service::JsonResponse> {
|
||||||
wrap_err!(win_service::check_service().await)
|
wrap_err!(service::check_service().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn install_service() -> CmdResult {
|
pub async fn install_service() -> CmdResult {
|
||||||
wrap_err!(win_service::install_service().await)
|
wrap_err!(service::install_service().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn uninstall_service() -> CmdResult {
|
pub async fn uninstall_service() -> CmdResult {
|
||||||
wrap_err!(win_service::uninstall_service().await)
|
wrap_err!(service::uninstall_service().await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
pub mod service {
|
pub mod service {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -93,10 +93,10 @@ impl CoreManager {
|
|||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if *self.use_service_mode.lock() {
|
if *self.use_service_mode.lock() {
|
||||||
log::debug!(target: "app", "stop the core by service");
|
log::debug!(target: "app", "stop the core by service");
|
||||||
log_err!(super::win_service::stop_core_by_service().await);
|
log_err!(super::service::stop_core_by_service().await);
|
||||||
should_kill = true;
|
should_kill = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,9 +105,9 @@ impl CoreManager {
|
|||||||
sleep(Duration::from_millis(500)).await;
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
use super::win_service;
|
use super::service;
|
||||||
|
|
||||||
// 服务模式
|
// 服务模式
|
||||||
let enable = { Config::verge().latest().enable_service_mode };
|
let enable = { Config::verge().latest().enable_service_mode };
|
||||||
@ -120,8 +120,8 @@ impl CoreManager {
|
|||||||
log::debug!(target: "app", "try to run core in service mode");
|
log::debug!(target: "app", "try to run core in service mode");
|
||||||
|
|
||||||
match (|| async {
|
match (|| async {
|
||||||
win_service::check_service().await?;
|
service::check_service().await?;
|
||||||
win_service::run_core_by_service(&config_path).await
|
service::run_core_by_service(&config_path).await
|
||||||
})()
|
})()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -205,7 +205,7 @@ impl CoreManager {
|
|||||||
/// 重启内核
|
/// 重启内核
|
||||||
pub fn recover_core(&'static self) -> Result<()> {
|
pub fn recover_core(&'static self) -> Result<()> {
|
||||||
// 服务模式不管
|
// 服务模式不管
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if *self.use_service_mode.lock() {
|
if *self.use_service_mode.lock() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -238,11 +238,11 @@ impl CoreManager {
|
|||||||
|
|
||||||
/// 停止核心运行
|
/// 停止核心运行
|
||||||
pub fn stop_core(&self) -> Result<()> {
|
pub fn stop_core(&self) -> Result<()> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
if *self.use_service_mode.lock() {
|
if *self.use_service_mode.lock() {
|
||||||
log::debug!(target: "app", "stop the core by service");
|
log::debug!(target: "app", "stop the core by service");
|
||||||
tauri::async_runtime::block_on(async move {
|
tauri::async_runtime::block_on(async move {
|
||||||
log_err!(super::win_service::stop_core_by_service().await);
|
log_err!(super::service::stop_core_by_service().await);
|
||||||
});
|
});
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ pub mod manager;
|
|||||||
pub mod sysopt;
|
pub mod sysopt;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod tray;
|
pub mod tray;
|
||||||
pub mod win_service;
|
pub mod service;
|
||||||
pub mod win_uwp;
|
pub mod win_uwp;
|
||||||
|
|
||||||
pub use self::core::*;
|
pub use self::core::*;
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
#![cfg(target_os = "windows")]
|
#![cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::utils::dirs;
|
use crate::utils::dirs;
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use deelevate::{PrivilegeLevel, Token};
|
|
||||||
use runas::Command as RunasCommand;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::os::windows::process::CommandExt;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{env::current_exe, process::Command as StdCommand};
|
use std::{env::current_exe, process::Command as StdCommand};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
// Windows only
|
||||||
|
|
||||||
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
@ -32,7 +31,14 @@ pub struct JsonResponse {
|
|||||||
|
|
||||||
/// Install the Clash Verge Service
|
/// Install the Clash Verge Service
|
||||||
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
||||||
|
///
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn install_service() -> Result<()> {
|
pub async fn install_service() -> Result<()> {
|
||||||
|
use deelevate::{PrivilegeLevel, Token};
|
||||||
|
use runas::Command as RunasCommand;
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
|
|
||||||
let binary_path = dirs::service_path()?;
|
let binary_path = dirs::service_path()?;
|
||||||
let install_path = binary_path.with_file_name("install-service.exe");
|
let install_path = binary_path.with_file_name("install-service.exe");
|
||||||
|
|
||||||
@ -60,9 +66,42 @@ pub async fn install_service() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub async fn install_service() -> Result<()> {
|
||||||
|
use users::get_effective_uid;
|
||||||
|
|
||||||
|
let binary_path = dirs::service_path()?;
|
||||||
|
let installer_path = binary_path.with_file_name("install-service");
|
||||||
|
|
||||||
|
if !installer_path.exists() {
|
||||||
|
bail!("installer not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let elevator = crate::utils::unix_helper::linux_elevator();
|
||||||
|
let status = match get_effective_uid() {
|
||||||
|
0 => StdCommand::new(installer_path).status()?,
|
||||||
|
_ => StdCommand::new(elevator).arg("sh").arg("-c").arg(installer_path).status()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!(
|
||||||
|
"failed to install service with status {}",
|
||||||
|
status.code().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Uninstall the Clash Verge Service
|
/// Uninstall the Clash Verge Service
|
||||||
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn uninstall_service() -> Result<()> {
|
pub async fn uninstall_service() -> Result<()> {
|
||||||
|
use deelevate::{PrivilegeLevel, Token};
|
||||||
|
use runas::Command as RunasCommand;
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
|
|
||||||
let binary_path = dirs::service_path()?;
|
let binary_path = dirs::service_path()?;
|
||||||
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
|
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
|
||||||
|
|
||||||
@ -90,6 +129,33 @@ pub async fn uninstall_service() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub async fn uninstall_service() -> Result<()> {
|
||||||
|
use users::get_effective_uid;
|
||||||
|
|
||||||
|
let binary_path = dirs::service_path()?;
|
||||||
|
let uninstaller_path = binary_path.with_file_name("uninstall-service");
|
||||||
|
|
||||||
|
if !uninstaller_path.exists() {
|
||||||
|
bail!("uninstaller not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let elevator = crate::utils::unix_helper::linux_elevator();
|
||||||
|
let status = match get_effective_uid() {
|
||||||
|
0 => StdCommand::new(uninstaller_path).status()?,
|
||||||
|
_ => StdCommand::new(elevator).arg("sh").arg("-c").arg(uninstaller_path).status()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!(
|
||||||
|
"failed to install service with status {}",
|
||||||
|
status.code().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// check the windows service status
|
/// check the windows service status
|
||||||
pub async fn check_service() -> Result<JsonResponse> {
|
pub async fn check_service() -> Result<JsonResponse> {
|
||||||
let url = format!("{SERVICE_URL}/get_clash");
|
let url = format!("{SERVICE_URL}/get_clash");
|
||||||
@ -119,7 +185,8 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
|||||||
let clash_core = { Config::verge().latest().clash_core.clone() };
|
let clash_core = { Config::verge().latest().clash_core.clone() };
|
||||||
let clash_core = clash_core.unwrap_or("clash".into());
|
let clash_core = clash_core.unwrap_or("clash".into());
|
||||||
|
|
||||||
let clash_bin = format!("{clash_core}.exe");
|
let bin_ext = if cfg!(windows) {".exe"} else {""};
|
||||||
|
let clash_bin = format!("{clash_core}{bin_ext}");
|
||||||
let bin_path = current_exe()?.with_file_name(clash_bin);
|
let bin_path = current_exe()?.with_file_name(clash_bin);
|
||||||
let bin_path = dirs::path_to_str(&bin_path)?;
|
let bin_path = dirs::path_to_str(&bin_path)?;
|
||||||
|
|
@ -185,7 +185,7 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
|||||||
let tun_tray_icon = patch.tun_tray_icon;
|
let tun_tray_icon = patch.tun_tray_icon;
|
||||||
|
|
||||||
match {
|
match {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
let service_mode = patch.enable_service_mode;
|
let service_mode = patch.enable_service_mode;
|
||||||
|
|
||||||
|
@ -92,12 +92,17 @@ pub fn clash_pid_path() -> Result<PathBuf> {
|
|||||||
Ok(app_home_dir()?.join("clash.pid"))
|
Ok(app_home_dir()?.join("clash.pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn service_path() -> Result<PathBuf> {
|
||||||
|
Ok(app_resources_dir()?.join("clash-verge-service"))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn service_path() -> Result<PathBuf> {
|
pub fn service_path() -> Result<PathBuf> {
|
||||||
Ok(app_resources_dir()?.join("clash-verge-service.exe"))
|
Ok(app_resources_dir()?.join("clash-verge-service.exe"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(windows, target_os = "linux"))]
|
||||||
pub fn service_log_file() -> Result<PathBuf> {
|
pub fn service_log_file() -> Result<PathBuf> {
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
|
@ -4,3 +4,4 @@ pub mod init;
|
|||||||
pub mod resolve;
|
pub mod resolve;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod tmpl;
|
pub mod tmpl;
|
||||||
|
pub mod unix_helper;
|
||||||
|
15
src-tauri/src/utils/unix_helper.rs
Normal file
15
src-tauri/src/utils/unix_helper.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn linux_elevator() -> &'static str {
|
||||||
|
match Command::new("which").arg("pkexec").output() {
|
||||||
|
Ok(output) => {
|
||||||
|
if output.stdout.is_empty() {
|
||||||
|
"sudo"
|
||||||
|
} else {
|
||||||
|
"pkexec"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => "sudo",
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,8 @@ interface Props {
|
|||||||
onError?: (err: Error) => void;
|
onError?: (err: Error) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isWIN = getSystem() === "windows";
|
const isServiceModeAvailable =
|
||||||
|
getSystem() === "windows" || getSystem() === "linux";
|
||||||
|
|
||||||
const SettingSystem = ({ onError }: Props) => {
|
const SettingSystem = ({ onError }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -26,7 +27,7 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
|
|
||||||
// service mode
|
// service mode
|
||||||
const { data: serviceStatus } = useSWR(
|
const { data: serviceStatus } = useSWR(
|
||||||
isWIN ? "checkService" : null,
|
isServiceModeAvailable ? "checkService" : null,
|
||||||
checkService,
|
checkService,
|
||||||
{
|
{
|
||||||
revalidateIfStale: false,
|
revalidateIfStale: false,
|
||||||
@ -56,7 +57,7 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
<SettingList title={t("System Setting")}>
|
<SettingList title={t("System Setting")}>
|
||||||
<SysproxyViewer ref={sysproxyRef} />
|
<SysproxyViewer ref={sysproxyRef} />
|
||||||
<TunViewer ref={tunRef} />
|
<TunViewer ref={tunRef} />
|
||||||
{isWIN && (
|
{isServiceModeAvailable && (
|
||||||
<ServiceViewer ref={serviceRef} enable={!!enable_service_mode} />
|
<ServiceViewer ref={serviceRef} enable={!!enable_service_mode} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -66,7 +67,9 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
<>
|
<>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
isWIN ? t("Tun Mode Info Windows") : t("Tun Mode Info Unix")
|
isServiceModeAvailable
|
||||||
|
? t("Tun Mode Info Windows")
|
||||||
|
: t("Tun Mode Info Unix")
|
||||||
}
|
}
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
@ -102,7 +105,7 @@ const SettingSystem = ({ onError }: Props) => {
|
|||||||
</GuardState>
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
{isWIN && (
|
{isServiceModeAvailable && (
|
||||||
<SettingItem
|
<SettingItem
|
||||||
label={t("Service Mode")}
|
label={t("Service Mode")}
|
||||||
extra={
|
extra={
|
||||||
|
Loading…
x
Reference in New Issue
Block a user