Compare commits

...

5 Commits

Author SHA1 Message Date
Sukka
9653decfa1
Merge 7cc19d1da5 into a0f9fb90ee 2024-06-14 23:19:14 +08:00
MystiPanda
a0f9fb90ee
feat: disable running with admin permission and check service mode
Some checks are pending
Alpha Build / alpha (macos-latest, aarch64-apple-darwin) (push) Waiting to run
Alpha Build / alpha (macos-latest, x86_64-apple-darwin) (push) Waiting to run
Alpha Build / alpha (windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha (windows-latest, i686-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha (windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, aarch64-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, armv7-unknown-linux-gnueabihf) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, i686-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (arm64, windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (x64, windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (x86, windows-latest, i686-pc-windows-msvc) (push) Waiting to run
Alpha Build / Update tag (push) Blocked by required conditions
2024-06-14 23:15:49 +08:00
MystiPanda
1b8c4cb832
build: downgrade auto-launch 2024-06-14 21:26:30 +08:00
MystiPanda
cfd50d281b
fix: fix bypass check regex 2024-06-14 21:23:58 +08:00
SukkaW
7cc19d1da5 perf: replace Array#map Array#filter chain w/ Array#reduce 2024-06-14 17:10:53 +08:00
10 changed files with 157 additions and 119 deletions

3
src-tauri/Cargo.lock generated
View File

@ -378,7 +378,8 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auto-launch"
version = "0.5.0"
source = "git+https://github.com/zzzgydi/auto-launch?branch=main#2d94a103ca20652a3baf581ca2c296791c35c09b"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471"
dependencies = [
"dirs 4.0.0",
"thiserror",

View File

@ -30,13 +30,13 @@ once_cell = "1.19"
port_scanner = "0.1.5"
delay_timer = "0.11"
parking_lot = "0.12"
auto-launch = "0.5.0"
percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
tauri = { git="https://github.com/tauri-apps/tauri",branch = "1.x", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"

View File

@ -88,8 +88,10 @@ pub fn toggle_system_proxy() {
pub fn toggle_tun_mode() {
let enable = Config::verge().data().enable_tun_mode;
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
if !enable {
if let Ok(res) = service::check_service().await {
if res.code == 0 {
match patch_verge(IVerge {
enable_tun_mode: Some(!enable),
..IVerge::default()
@ -99,6 +101,25 @@ pub fn toggle_tun_mode() {
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
return;
}
}
tauri::api::dialog::message(
None::<&tauri::Window>,
"Please install and enable service mode",
"Service mode is required for Tun mode",
);
} else {
match patch_verge(IVerge {
enable_tun_mode: Some(!enable),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
}
});
}
@ -403,3 +424,19 @@ pub async fn test_delay(url: String) -> Result<u32> {
}
}
}
pub fn check_permission() -> Result<()> {
#[cfg(target_os = "windows")]
{
let hklm = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE);
if let Ok(reg) = hklm.open_subkey_with_flags(
"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run",
winreg::enums::KEY_SET_VALUE,
) {
reg.delete_value("Clash Verge").unwrap_or_default();
return Ok(());
}
}
Err(anyhow::anyhow!("permission denied"))
}

View File

@ -19,6 +19,16 @@ fn main() -> std::io::Result<()> {
println!("app exists");
return Ok(());
}
// 权限检测
if feat::check_permission().is_ok() {
println!("please do not run with admin permission");
tauri::api::dialog::blocking::message(
None::<&tauri::Window>,
"Please do not run with admin permission",
"If you want to use Tun mode, please enable service mode instead",
);
return Ok(());
}
#[cfg(target_os = "linux")]
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");

View File

@ -1,12 +1,4 @@
import { useTranslation } from "react-i18next";
import { Button, ButtonGroup, Tooltip } from "@mui/material";
import { checkService } from "@/services/cmds";
import { useVerge } from "@/hooks/use-verge";
import getSystem from "@/utils/get-system";
import useSWR from "swr";
import { useEffect } from "react";
const isWIN = getSystem() === "windows";
import { Button, ButtonGroup } from "@mui/material";
interface Props {
value?: string;
@ -15,39 +7,12 @@ interface Props {
export const StackModeSwitch = (props: Props) => {
const { value, onChange } = props;
const { verge } = useVerge();
const { enable_service_mode } = verge ?? {};
// service mode
const { data: serviceStatus, mutate: mutateCheck } = useSWR(
isWIN ? "checkService" : null,
checkService,
{
revalidateIfStale: false,
shouldRetryOnError: false,
}
);
const { t } = useTranslation();
useEffect(() => {
mutateCheck();
}, []);
return (
<Tooltip
title={
isWIN && (serviceStatus !== "active" || !enable_service_mode)
? t("System and Mixed Can Only be Used in Service Mode")
: ""
}
>
<ButtonGroup size="small" sx={{ my: "4px" }}>
<Button
variant={value?.toLowerCase() === "system" ? "contained" : "outlined"}
onClick={() => onChange?.("system")}
disabled={
isWIN && (serviceStatus !== "active" || !enable_service_mode)
}
sx={{ textTransform: "capitalize" }}
>
System
@ -63,14 +28,10 @@ export const StackModeSwitch = (props: Props) => {
<Button
variant={value?.toLowerCase() === "mixed" ? "contained" : "outlined"}
onClick={() => onChange?.("mixed")}
disabled={
isWIN && (serviceStatus !== "active" || !enable_service_mode)
}
sx={{ textTransform: "capitalize" }}
>
Mixed
</Button>
</ButtonGroup>
</Tooltip>
);
};

View File

@ -29,10 +29,10 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
let validReg;
if (getSystem() === "windows") {
validReg =
/^(\*?\w+(\.\w+)*|\d{1,3}(\.\d{1,3}){0,2}\.\*|\d{1,3}(\.\d{1,3}){3})(;(\*?\w+(\.\w+)*|\d{1,3}(\.\d{1,3}){0,2}\.\*|\d{1,3}(\.\d{1,3}){3}))*$/;
/^((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>)(;((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>))*;?$/;
} else {
validReg =
/^((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?)(,((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?))*$/;
/^((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>)(;((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>))*;?$/;
}
const [open, setOpen] = useState(false);

View File

@ -22,11 +22,15 @@ const SettingSystem = ({ onError }: Props) => {
const { verge, mutateVerge, patchVerge } = useVerge();
// service mode
const { data: serviceStatus } = useSWR("checkService", checkService, {
const { data: serviceStatus, mutate: mutateCheck } = useSWR(
"checkService",
checkService,
{
revalidateIfStale: false,
shouldRetryOnError: false,
focusThrottleInterval: 36e5, // 1 hour
});
}
);
const serviceRef = useRef<DialogRef>(null);
const sysproxyRef = useRef<DialogRef>(null);
@ -84,7 +88,7 @@ const SettingSystem = ({ onError }: Props) => {
onChange={(e) => onChangeData({ enable_tun_mode: e })}
onGuard={(e) => patchVerge({ enable_tun_mode: e })}
>
<Switch edge="end" />
<Switch disabled={serviceStatus !== "active"} edge="end" />
</GuardState>
</SettingItem>
@ -109,7 +113,10 @@ const SettingSystem = ({ onError }: Props) => {
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ enable_service_mode: e })}
onGuard={(e) => patchVerge({ enable_service_mode: e })}
onGuard={(e) => {
setTimeout(() => mutateCheck(), 1000);
return patchVerge({ enable_service_mode: e });
}}
>
<Switch
edge="end"

View File

@ -24,11 +24,11 @@ const LogPage = () => {
const [match, setMatch] = useState(() => (_: string) => true);
const filterLogs = useMemo(() => {
return logData
.filter((data) =>
logState === "all" ? true : data.type.includes(logState)
)
.filter((data) => match(data.payload));
return logData.filter(
(data) =>
(logState === "all" ? true : data.type.includes(logState)) &&
match(data.payload)
);
}, [logData, logState, match]);
return (

View File

@ -134,25 +134,48 @@ export const getProxies = async () => {
const { GLOBAL: global, DIRECT: direct, REJECT: reject } = proxyRecord;
let groups = Object.values(proxyRecord)
.filter((each) => each.name !== "GLOBAL" && each.all)
.map((each) => ({
interface Group {
all: IProxyItem[];
name: string;
type: string;
udp: boolean;
xudp: boolean;
tfo: boolean;
history: {
time: string;
delay: number;
}[];
}
let groups: Group[] = Object.values(proxyRecord).reduce<Group[]>(
(acc, each) => {
if (each.name !== "GLOBAL" && each.all) {
acc.push({
...each,
all: each.all!.map((item) => generateItem(item)),
}));
});
}
return acc;
},
[]
);
if (global?.all) {
let globalGroups = global.all
.filter((name) => proxyRecord[name]?.all)
.map((name) => proxyRecord[name])
.map((each) => ({
...each,
all: each.all!.map((item) => generateItem(item)),
}));
let globalNames = globalGroups.map((each) => each.name);
let globalGroups: Group[] = global.all.reduce<Group[]>((acc, name) => {
if (proxyRecord[name]?.all) {
acc.push({
...proxyRecord[name],
all: proxyRecord[name].all!.map((item) => generateItem(item)),
});
}
return acc;
}, []);
let globalNames = new Set(globalGroups.map((each) => each.name));
groups = groups
.filter((group) => {
return !globalNames.includes(group.name);
return !globalNames.has(group.name);
})
.concat(globalGroups);
}

View File

@ -7,23 +7,22 @@ export async function getClashLogs() {
const newRegex = /(.+?)\s+(.+?)\s+(.+)/;
const logs = await invoke<string[]>("get_clash_logs");
return logs
.map((log) => {
return logs.reduce<ILogItem[]>((acc, log) => {
const result = log.match(regex);
if (result) {
const [_, _time, type, payload] = result;
const time = dayjs(_time).format("MM-DD HH:mm:ss");
return { time, type, payload };
acc.push({ time, type, payload });
return acc;
}
const result2 = log.match(newRegex);
if (result2) {
const [_, time, type, payload] = result2;
return { time, type, payload };
acc.push({ time, type, payload });
}
return null;
})
.filter(Boolean) as ILogItem[];
return acc;
}, []);
}
export async function getProfiles() {