From 7551b45da28b71ab68c8a2ecb558c47f6f36ac8f Mon Sep 17 00:00:00 2001 From: MystiPanda Date: Tue, 20 Feb 2024 23:27:03 +0800 Subject: [PATCH] feat: Support Tun Config (#416) --- src-tauri/src/config/clash.rs | 9 + src-tauri/src/config/runtime.rs | 20 ++- src-tauri/src/enhance/mod.rs | 15 +- src-tauri/src/enhance/tun.rs | 6 - src/components/setting/mods/tun-viewer.tsx | 192 +++++++++++++++++++++ src/components/setting/setting-clash.tsx | 1 - src/components/setting/setting-system.tsx | 31 +++- src/services/types.d.ts | 9 + 8 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 src/components/setting/mods/tun-viewer.tsx diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 5566c95c..42014dc2 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -23,6 +23,14 @@ impl IClashTemp { pub fn template() -> Self { let mut map = Mapping::new(); + let mut tun = Mapping::new(); + tun.insert("stack".into(), "gVisor".into()); + tun.insert("device".into(), "Meta".into()); + tun.insert("auto-route".into(), true.into()); + tun.insert("strict-route".into(), true.into()); + tun.insert("auto-detect-interface".into(), true.into()); + tun.insert("dns-hijack".into(), vec!["any:53", "tcp://any:53"].into()); + tun.insert("mtu".into(), 9000.into()); map.insert("mixed-port".into(), 7897.into()); map.insert("socks-port".into(), 7898.into()); @@ -32,6 +40,7 @@ impl IClashTemp { map.insert("mode".into(), "rule".into()); map.insert("external-controller".into(), "127.0.0.1:9097".into()); map.insert("secret".into(), "".into()); + map.insert("tun".into(), tun.into()); Self(map) } diff --git a/src-tauri/src/config/runtime.rs b/src-tauri/src/config/runtime.rs index a763e4c3..892e6be7 100644 --- a/src-tauri/src/config/runtime.rs +++ b/src-tauri/src/config/runtime.rs @@ -1,7 +1,7 @@ +use crate::enhance::field::use_keys; use serde::{Deserialize, Serialize}; -use serde_yaml::Mapping; +use serde_yaml::{Mapping, Value}; use std::collections::HashMap; - #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct IRuntime { pub config: Option, @@ -16,7 +16,7 @@ impl IRuntime { Self::default() } - // 这里只更改 allow-lan | ipv6 | log-level + // 这里只更改 allow-lan | ipv6 | log-level | tun pub fn patch_config(&mut self, patch: Mapping) { if let Some(config) = self.config.as_mut() { ["allow-lan", "ipv6", "log-level"] @@ -26,6 +26,20 @@ impl IRuntime { config.insert(key.into(), value.clone()); } }); + let tun = config.get("tun"); + let mut tun = tun.map_or(Mapping::new(), |val| { + val.as_mapping().cloned().unwrap_or(Mapping::new()) + }); + let patch_tun = patch.get("tun"); + let patch_tun = patch_tun.map_or(Mapping::new(), |val| { + val.as_mapping().cloned().unwrap_or(Mapping::new()) + }); + use_keys(&patch_tun).into_iter().for_each(|key| { + if let Some(value) = patch_tun.get(&key).to_owned() { + tun.insert(key.into(), value.clone()); + } + }); + config.insert("tun".into(), Value::from(tun)); } } } diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index fd4dbafc..c2c90d67 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -1,5 +1,5 @@ mod chain; -mod field; +pub mod field; mod merge; mod script; mod tun; @@ -78,7 +78,18 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { // 合并默认的config for (key, value) in clash_config.into_iter() { - config.insert(key, value); + if key.as_str() == Some("tun") { + let mut tun = config.get_mut("tun").map_or(Mapping::new(), |val| { + val.as_mapping().cloned().unwrap_or(Mapping::new()) + }); + let patch_tun = value.as_mapping().cloned().unwrap_or(Mapping::new()); + for (key, value) in patch_tun.into_iter() { + tun.insert(key, value); + } + config.insert("tun".into(), tun.into()); + } else { + config.insert(key, value); + } } // 内建脚本最后跑 diff --git a/src-tauri/src/enhance/tun.rs b/src-tauri/src/enhance/tun.rs index b72823ac..59870887 100644 --- a/src-tauri/src/enhance/tun.rs +++ b/src-tauri/src/enhance/tun.rs @@ -30,12 +30,6 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping { }); revise!(tun_val, "enable", enable); - if enable { - append!(tun_val, "stack", "gvisor"); - append!(tun_val, "dns-hijack", vec!["any:53"]); - append!(tun_val, "auto-route", true); - append!(tun_val, "auto-detect-interface", true); - } revise!(config, "tun", tun_val); diff --git a/src/components/setting/mods/tun-viewer.tsx b/src/components/setting/mods/tun-viewer.tsx new file mode 100644 index 00000000..603fd6d1 --- /dev/null +++ b/src/components/setting/mods/tun-viewer.tsx @@ -0,0 +1,192 @@ +import { forwardRef, useImperativeHandle, useState } from "react"; +import { useLockFn } from "ahooks"; +import { useTranslation } from "react-i18next"; +import { + List, + ListItem, + ListItemText, + MenuItem, + Select, + Switch, + TextField, +} from "@mui/material"; +import { useClash } from "@/hooks/use-clash"; +import { BaseDialog, DialogRef, Notice } from "@/components/base"; + +export const TunViewer = forwardRef((props, ref) => { + const { t } = useTranslation(); + + const { clash, mutateClash, patchClash } = useClash(); + + const [open, setOpen] = useState(false); + const [values, setValues] = useState({ + stack: "gVisor", + device: "Mihomo", + autoRoute: true, + autoDetectInterface: true, + dnsHijack: ["any:53", "tcp://any:53"], + strictRoute: true, + mtu: 9000, + }); + + useImperativeHandle(ref, () => ({ + open: () => { + setOpen(true); + setValues({ + stack: clash?.tun.stack ?? "gVisor", + device: clash?.tun.device ?? "Mihomo", + autoRoute: clash?.tun["auto-route"] ?? true, + autoDetectInterface: clash?.tun["auto-detect-interface"] ?? true, + dnsHijack: clash?.tun["dns-hijack"] ?? ["any:53", "tcp://any:53"], + strictRoute: clash?.tun["strict-route"] ?? true, + mtu: clash?.tun.mtu ?? 9000, + }); + }, + close: () => setOpen(false), + })); + + const onSave = useLockFn(async () => { + try { + let tun = { + stack: values.stack, + device: values.device, + "auto-route": values.autoRoute, + "auto-detect-interface": values.autoDetectInterface, + "dns-hijack": values.dnsHijack, + "strict-route": values.strictRoute, + mtu: values.mtu, + }; + await patchClash({ tun }); + await mutateClash( + (old) => ({ + ...(old! || {}), + tun, + }), + false + ); + setOpen(false); + } catch (err: any) { + Notice.error(err.message || err.toString()); + } + }); + + return ( + setOpen(false)} + onCancel={() => setOpen(false)} + onOk={onSave} + > + + + + + + + + + + setValues((v) => ({ ...v, device: e.target.value })) + } + /> + + + + + setValues((v) => ({ ...v, autoRoute: c }))} + /> + + + + + setValues((v) => ({ ...v, strictRoute: c }))} + /> + + + + + + setValues((v) => ({ ...v, autoDetectInterface: c })) + } + /> + + + + + + setValues((v) => ({ ...v, dnsHijack: e.target.value.split(",") })) + } + /> + + + + + + setValues((v) => ({ + ...v, + mtu: parseInt(e.target.value), + })) + } + /> + + + + ); +}); diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index 2b00a1ed..bd91b216 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -41,7 +41,6 @@ const SettingClash = ({ onError }: Props) => { const { enable_random_port = false, verge_mixed_port } = verge ?? {}; const webRef = useRef(null); - const fieldRef = useRef(null); const portRef = useRef(null); const ctrlRef = useRef(null); const coreRef = useRef(null); diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx index 5cee2cab..10da5a02 100644 --- a/src/components/setting/setting-system.tsx +++ b/src/components/setting/setting-system.tsx @@ -10,6 +10,7 @@ import { SettingList, SettingItem } from "./mods/setting-comp"; import { GuardState } from "./mods/guard-state"; import { ServiceViewer } from "./mods/service-viewer"; import { SysproxyViewer } from "./mods/sysproxy-viewer"; +import { TunViewer } from "./mods/tun-viewer"; import getSystem from "@/utils/get-system"; interface Props { @@ -36,6 +37,7 @@ const SettingSystem = ({ onError }: Props) => { const serviceRef = useRef(null); const sysproxyRef = useRef(null); + const tunRef = useRef(null); const { enable_tun_mode, @@ -53,6 +55,7 @@ const SettingSystem = ({ onError }: Props) => { return ( + {isWIN && ( )} @@ -60,17 +63,31 @@ const SettingSystem = ({ onError }: Props) => { - - + + + + + + tunRef.current?.open()} + > + - + } >