diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 0632cbec..5566c95c 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -25,6 +25,8 @@ impl IClashTemp { let mut map = Mapping::new(); map.insert("mixed-port".into(), 7897.into()); + map.insert("socks-port".into(), 7898.into()); + map.insert("port".into(), 7899.into()); map.insert("log-level".into(), "info".into()); map.insert("allow-lan".into(), false.into()); map.insert("mode".into(), "rule".into()); @@ -35,10 +37,14 @@ impl IClashTemp { } fn guard(mut config: Mapping) -> Mapping { - let port = Self::guard_mixed_port(&config); + let mixed_port = Self::guard_mixed_port(&config); + let socks_port = Self::guard_socks_port(&config); + let port = Self::guard_port(&config); let ctrl = Self::guard_server_ctrl(&config); - config.insert("mixed-port".into(), port.into()); + config.insert("mixed-port".into(), mixed_port.into()); + config.insert("socks-port".into(), socks_port.into()); + config.insert("port".into(), port.into()); config.insert("external-controller".into(), ctrl.into()); config } @@ -61,11 +67,23 @@ impl IClashTemp { Self::guard_mixed_port(&self.0) } + #[allow(unused)] + pub fn get_socks_port(&self) -> u16 { + Self::guard_socks_port(&self.0) + } + + #[allow(unused)] + pub fn get_port(&self) -> u16 { + Self::guard_port(&self.0) + } + pub fn get_client_info(&self) -> ClashInfo { let config = &self.0; ClashInfo { - port: Self::guard_mixed_port(config), + mixed_port: Self::guard_mixed_port(config), + socks_port: Self::guard_socks_port(config), + port: Self::guard_port(config), server: Self::guard_client_ctrl(config), secret: config.get("secret").and_then(|value| match value { Value::String(val_str) => Some(val_str.clone()), @@ -91,6 +109,36 @@ impl IClashTemp { port } + pub fn guard_socks_port(config: &Mapping) -> u16 { + let mut port = config + .get("socks-port") + .and_then(|value| match value { + Value::String(val_str) => val_str.parse().ok(), + Value::Number(val_num) => val_num.as_u64().map(|u| u as u16), + _ => None, + }) + .unwrap_or(7898); + if port == 0 { + port = 7898; + } + port + } + + pub fn guard_port(config: &Mapping) -> u16 { + let mut port = config + .get("port") + .and_then(|value| match value { + Value::String(val_str) => val_str.parse().ok(), + Value::Number(val_num) => val_num.as_u64().map(|u| u as u16), + _ => None, + }) + .unwrap_or(7899); + if port == 0 { + port = 7899; + } + port + } + pub fn guard_server_ctrl(config: &Mapping) -> String { config .get("external-controller") @@ -129,6 +177,8 @@ impl IClashTemp { #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] pub struct ClashInfo { /// clash core port + pub mixed_port: u16, + pub socks_port: u16, pub port: u16, /// same as `external-controller` pub server: String, @@ -148,7 +198,9 @@ fn test_clash_info() { fn get_result>(port: u16, server: S) -> ClashInfo { ClashInfo { - port, + mixed_port: port, + socks_port: 7898, + port: 7899, server: server.into(), secret: None, } diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index cf661516..b2b914d4 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -14,9 +14,6 @@ pub struct IProfiles { /// same as PrfConfig.chain pub chain: Option>, - /// record valid fields for clash - pub valid: Option>, - /// profile list pub items: Option>, } @@ -55,13 +52,6 @@ impl IProfiles { pub fn template() -> Self { Self { - valid: Some(vec![ - "dns".into(), - "sub-rules".into(), - "unified-delay".into(), - "tcp-concurrent".into(), - "global-client-fingerprint".into(), - ]), items: Some(vec![]), ..Self::default() } @@ -94,10 +84,6 @@ impl IProfiles { self.chain = Some(chain); } - if let Some(valid) = patch.valid { - self.valid = Some(valid); - } - Ok(()) } diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index f67a35af..6f123854 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -81,9 +81,6 @@ pub struct IVerge { /// 默认的延迟测试连接 pub default_latency_test: Option, - /// 支持关闭字段过滤,避免meta的新字段都被过滤掉,默认为关闭 - pub enable_clash_fields: Option, - /// 是否使用内部的脚本支持,默认为真 pub enable_builtin_enhanced: Option, @@ -106,6 +103,10 @@ pub struct IVerge { /// verge mixed port 用于覆盖 clash 的 mixed port pub verge_mixed_port: Option, + + pub verge_socks_port: Option, + + pub verge_port: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -160,11 +161,12 @@ impl IVerge { enable_system_proxy: Some(false), enable_random_port: Some(false), verge_mixed_port: Some(7897), + verge_socks_port: Some(7898), + verge_port: Some(7899), enable_proxy_guard: Some(false), proxy_guard_duration: Some(30), auto_close_connection: Some(true), enable_builtin_enhanced: Some(true), - enable_clash_fields: Some(true), auto_log_clean: Some(3), ..Self::default() } @@ -202,6 +204,8 @@ impl IVerge { patch!(enable_silent_start); patch!(enable_random_port); patch!(verge_mixed_port); + patch!(verge_socks_port); + patch!(verge_port); patch!(enable_system_proxy); patch!(enable_proxy_guard); patch!(system_proxy_bypass); @@ -217,7 +221,6 @@ impl IVerge { patch!(enable_builtin_enhanced); patch!(proxy_layout_column); patch!(test_list); - patch!(enable_clash_fields); patch!(auto_log_clean); patch!(window_size_position); } diff --git a/src-tauri/src/enhance/field.rs b/src-tauri/src/enhance/field.rs index c9ce0435..3a4b7fcf 100644 --- a/src-tauri/src/enhance/field.rs +++ b/src-tauri/src/enhance/field.rs @@ -3,9 +3,9 @@ use std::collections::HashSet; pub const HANDLE_FIELDS: [&str; 9] = [ "mode", - "port", - "socks-port", "mixed-port", + "socks-port", + "port", "allow-lan", "log-level", "ipv6", @@ -15,71 +15,13 @@ pub const HANDLE_FIELDS: [&str; 9] = [ pub const DEFAULT_FIELDS: [&str; 5] = [ "proxies", - "proxy-groups", "proxy-providers", - "rules", + "proxy-groups", "rule-providers", + "rules", ]; -pub const OTHERS_FIELDS: [&str; 31] = [ - "dns", - "tun", - "ebpf", - "hosts", - "script", - "profile", - "payload", - "tunnels", - "auto-redir", - "experimental", - "interface-name", - "routing-mark", - "redir-port", - "tproxy-port", - "iptables", - "external-ui", - "bind-address", - "authentication", - "tls", // meta - "sniffer", // meta - "geox-url", // meta - "listeners", // meta - "sub-rules", // meta - "geodata-mode", // meta - "unified-delay", // meta - "tcp-concurrent", // meta - "enable-process", // meta - "find-process-mode", // meta - "skip-auth-prefixes", // meta - "external-controller-tls", // meta - "global-client-fingerprint", // meta -]; - -pub fn use_clash_fields() -> Vec { - DEFAULT_FIELDS - .into_iter() - .chain(HANDLE_FIELDS) - .chain(OTHERS_FIELDS) - .map(|s| s.to_string()) - .collect() -} - -pub fn use_valid_fields(mut valid: Vec) -> Vec { - let others = Vec::from(OTHERS_FIELDS); - - valid.iter_mut().for_each(|s| s.make_ascii_lowercase()); - valid - .into_iter() - .filter(|s| others.contains(&s.as_str())) - .chain(DEFAULT_FIELDS.iter().map(|s| s.to_string())) - .collect() -} - -pub fn use_filter(config: Mapping, filter: &Vec, enable: bool) -> Mapping { - if !enable { - return config; - } - +pub fn use_filter(config: Mapping, filter: &Vec) -> Mapping { let mut ret = Mapping::new(); for (key, value) in config.into_iter() { @@ -105,40 +47,35 @@ pub fn use_lowercase(config: Mapping) -> Mapping { ret } -pub fn use_sort(config: Mapping, enable_filter: bool) -> Mapping { +pub fn use_sort(config: Mapping) -> Mapping { let mut ret = Mapping::new(); + HANDLE_FIELDS.into_iter().for_each(|key| { + let key = Value::from(key); + if let Some(value) = config.get(&key) { + ret.insert(key, value.clone()); + } + }); - HANDLE_FIELDS + let supported_keys: HashSet<&str> = HANDLE_FIELDS.into_iter().chain(DEFAULT_FIELDS).collect(); + + let config_keys: HashSet<&str> = config + .keys() + .filter_map(|e| e.as_str()) .into_iter() - .chain(OTHERS_FIELDS) - .chain(DEFAULT_FIELDS) - .for_each(|key| { - let key = Value::from(key); - if let Some(value) = config.get(&key) { - ret.insert(key, value.clone()); - } - }); + .collect(); - if !enable_filter { - let supported_keys: HashSet<&str> = HANDLE_FIELDS - .into_iter() - .chain(OTHERS_FIELDS) - .chain(DEFAULT_FIELDS) - .collect(); - - let config_keys: HashSet<&str> = config - .keys() - .filter_map(|e| e.as_str()) - .into_iter() - .collect(); - - config_keys.difference(&supported_keys).for_each(|&key| { - let key = Value::from(key); - if let Some(value) = config.get(&key) { - ret.insert(key, value.clone()); - } - }); - } + config_keys.difference(&supported_keys).for_each(|&key| { + let key = Value::from(key); + if let Some(value) = config.get(&key) { + ret.insert(key, value.clone()); + } + }); + DEFAULT_FIELDS.into_iter().for_each(|key| { + let key = Value::from(key); + if let Some(value) = config.get(&key) { + ret.insert(key, value.clone()); + } + }); ret } diff --git a/src-tauri/src/enhance/merge.rs b/src-tauri/src/enhance/merge.rs index 20342c98..2f372da5 100644 --- a/src-tauri/src/enhance/merge.rs +++ b/src-tauri/src/enhance/merge.rs @@ -19,7 +19,7 @@ pub fn use_merge(merge: Mapping, mut config: Mapping) -> Mapping { }); let merge_list = MERGE_FIELDS.iter().map(|s| s.to_string()); - let merge = use_filter(merge, &merge_list.collect(), true); + let merge = use_filter(merge, &merge_list.collect()); ["rules", "proxies", "proxy-groups"] .iter() diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 9a163547..53714248 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -22,19 +22,18 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { // config.yaml 的订阅 let clash_config = { Config::clash().latest().0.clone() }; - let (clash_core, enable_tun, enable_builtin, enable_filter) = { + let (clash_core, enable_tun, enable_builtin) = { let verge = Config::verge(); let verge = verge.latest(); ( verge.clash_core.clone(), verge.enable_tun_mode.unwrap_or(false), verge.enable_builtin_enhanced.unwrap_or(true), - verge.enable_clash_fields.unwrap_or(true), ) }; // 从profiles里拿东西 - let (mut config, chain, valid) = { + let (mut config, chain) = { let profiles = Config::profiles(); let profiles = profiles.latest(); @@ -49,23 +48,17 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { None => vec![], }; - let valid = profiles.valid.clone().unwrap_or_default(); - - (current, chain, valid) + (current, chain) }; let mut result_map = HashMap::new(); // 保存脚本日志 let mut exists_keys = use_keys(&config); // 保存出现过的keys - let valid = use_valid_fields(valid); - config = use_filter(config, &valid, enable_filter); - // 处理用户的profile chain.into_iter().for_each(|item| match item.data { ChainType::Merge(merge) => { exists_keys.extend(use_keys(&merge)); config = use_merge(merge, config.to_owned()); - config = use_filter(config.to_owned(), &valid, enable_filter); } ChainType::Script(script) => { let mut logs = vec![]; @@ -73,7 +66,6 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { match use_script(script, config.to_owned()) { Ok((res_config, res_logs)) => { exists_keys.extend(use_keys(&res_config)); - config = use_filter(res_config, &valid, enable_filter); logs.extend(res_logs); } Err(err) => logs.push(("exception".into(), err.to_string())), @@ -88,8 +80,6 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { config.insert(key, value); } - let clash_fields = use_clash_fields(); - // 内建脚本最后跑 if enable_builtin { ChainItem::builtin() @@ -102,7 +92,7 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { match item.data { ChainType::Script(script) => match use_script(script, config.to_owned()) { Ok((res_config, _)) => { - config = use_filter(res_config, &clash_fields, enable_filter); + config = res_config; } Err(err) => { log::error!(target: "app", "builtin script error `{err}`"); @@ -113,12 +103,11 @@ pub fn enhance() -> (Mapping, Vec, HashMap) { }); } - config = use_filter(config, &clash_fields, enable_filter); config = use_tun(config, enable_tun); - config = use_sort(config, enable_filter); + config = use_sort(config); let mut exists_set = HashSet::new(); - exists_set.extend(exists_keys.into_iter().filter(|s| clash_fields.contains(s))); + exists_set.extend(exists_keys.into_iter()); exists_keys = exists_set.into_iter().collect(); (config, exists_keys, result_map) diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 774ff41e..f85dae41 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -162,6 +162,8 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { match { let mixed_port = patch.get("mixed-port"); + let socks_port = patch.get("socks-port"); + let port = patch.get("port"); let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false); if mixed_port.is_some() && !enable_random_port { let changed = mixed_port.unwrap() @@ -182,6 +184,8 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { // 激活订阅 if mixed_port.is_some() + || socks_port.is_some() + || port.is_some() || patch.get("secret").is_some() || patch.get("external-controller").is_some() { diff --git a/src/components/setting/mods/clash-field-viewer.tsx b/src/components/setting/mods/clash-field-viewer.tsx deleted file mode 100644 index 8185cbaf..00000000 --- a/src/components/setting/mods/clash-field-viewer.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import useSWR from "swr"; -import { forwardRef, useImperativeHandle, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { Checkbox, Divider, Stack, Tooltip, Typography } from "@mui/material"; -import { InfoRounded } from "@mui/icons-material"; -import { getRuntimeExists } from "@/services/cmds"; -import { - HANDLE_FIELDS, - DEFAULT_FIELDS, - OTHERS_FIELDS, -} from "@/utils/clash-fields"; -import { BaseDialog, DialogRef } from "@/components/base"; -import { useProfiles } from "@/hooks/use-profiles"; -import { Notice } from "@/components/base"; - -const otherFields = [...OTHERS_FIELDS]; -const handleFields = [...HANDLE_FIELDS, ...DEFAULT_FIELDS]; - -export const ClashFieldViewer = forwardRef((props, ref) => { - const { t } = useTranslation(); - - const { profiles = {}, patchProfiles } = useProfiles(); - const { data: existsKeys = [], mutate: mutateExists } = useSWR( - "getRuntimeExists", - getRuntimeExists - ); - - const [open, setOpen] = useState(false); - const [selected, setSelected] = useState([]); - - useImperativeHandle(ref, () => ({ - open: () => { - mutateExists(); - setSelected(profiles.valid || []); - setOpen(true); - }, - close: () => setOpen(false), - })); - - const handleChange = (item: string) => { - if (!item) return; - - setSelected((old) => - old.includes(item) ? old.filter((e) => e !== item) : [...old, item] - ); - }; - - const handleSave = async () => { - setOpen(false); - - const oldSet = new Set(profiles.valid || []); - const curSet = new Set(selected); - const joinSet = new Set(selected.concat([...oldSet])); - - if (curSet.size === oldSet.size && curSet.size === joinSet.size) return; - - try { - await patchProfiles({ valid: [...curSet] }); - // Notice.success("Refresh clash config", 1000); - } catch (err: any) { - Notice.error(err?.message || err.toString()); - } - }; - - return ( - setOpen(false)} - onCancel={() => setOpen(false)} - onOk={handleSave} - > - {otherFields.map((item) => { - const inSelect = selected.includes(item); - const inConfig = existsKeys.includes(item); - - return ( - - handleChange(item)} - /> - {item} - - {!inSelect && inConfig && } - - ); - })} - - - - Clash Verge Control Fields - - - - {handleFields.map((item) => ( - - - {item} - - ))} - - ); -}); - -function WarnIcon() { - return ( - - - - ); -} diff --git a/src/components/setting/mods/clash-port-viewer.tsx b/src/components/setting/mods/clash-port-viewer.tsx index 9c5b73c3..c3841aeb 100644 --- a/src/components/setting/mods/clash-port-viewer.tsx +++ b/src/components/setting/mods/clash-port-viewer.tsx @@ -13,26 +13,42 @@ export const ClashPortViewer = forwardRef((props, ref) => { const { verge, patchVerge } = useVerge(); const [open, setOpen] = useState(false); + const [mixedPort, setMixedPort] = useState( + verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897 + ); + const [socksPort, setSocksPort] = useState( + verge?.verge_socks_port ?? clashInfo?.socks_port ?? 7898 + ); const [port, setPort] = useState( - verge?.verge_mixed_port ?? clashInfo?.port ?? 7897 + verge?.verge_port ?? clashInfo?.port ?? 7899 ); useImperativeHandle(ref, () => ({ open: () => { - if (verge?.verge_mixed_port) setPort(verge?.verge_mixed_port); + if (verge?.verge_mixed_port) setMixedPort(verge?.verge_mixed_port); + if (verge?.verge_socks_port) setSocksPort(verge?.verge_socks_port); + if (verge?.verge_port) setPort(verge?.verge_port); setOpen(true); }, close: () => setOpen(false), })); const onSave = useLockFn(async () => { - if (port === verge?.verge_mixed_port) { + if ( + mixedPort === verge?.verge_mixed_port && + socksPort === verge?.verge_socks_port && + port === verge?.verge_port + ) { setOpen(false); return; } try { - await patchInfo({ "mixed-port": port }); - await patchVerge({ verge_mixed_port: port }); + await patchInfo({ "mixed-port": mixedPort }); + await patchInfo({ "socks-port": socksPort }); + await patchInfo({ port }); + await patchVerge({ verge_mixed_port: mixedPort }); + await patchVerge({ verge_socks_port: socksPort }); + await patchVerge({ verge_port: port }); setOpen(false); Notice.success("Change Clash port successfully!", 1000); } catch (err: any) { @@ -54,6 +70,30 @@ export const ClashPortViewer = forwardRef((props, ref) => { + + setMixedPort(+e.target.value?.replace(/\D+/, "").slice(0, 5)) + } + /> + + + + + setSocksPort(+e.target.value?.replace(/\D+/, "").slice(0, 5)) + } + /> + + + ((props, ref) => { const [values, setValues] = useState({ appLogLevel: "info", autoCloseConnection: true, - enableClashFields: true, enableBuiltinEnhanced: true, proxyLayoutColumn: 6, defaultLatencyTest: "", @@ -34,7 +33,6 @@ export const MiscViewer = forwardRef((props, ref) => { setValues({ appLogLevel: verge?.app_log_level ?? "info", autoCloseConnection: verge?.auto_close_connection ?? true, - enableClashFields: verge?.enable_clash_fields ?? true, enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true, proxyLayoutColumn: verge?.proxy_layout_column || 6, defaultLatencyTest: verge?.default_latency_test || "", @@ -49,7 +47,6 @@ export const MiscViewer = forwardRef((props, ref) => { await patchVerge({ app_log_level: values.appLogLevel, auto_close_connection: values.autoCloseConnection, - enable_clash_fields: values.enableClashFields, enable_builtin_enhanced: values.enableBuiltinEnhanced, proxy_layout_column: values.proxyLayoutColumn, default_latency_test: values.defaultLatencyTest, @@ -105,17 +102,6 @@ export const MiscViewer = forwardRef((props, ref) => { /> - - - - setValues((v) => ({ ...v, enableClashFields: c })) - } - /> - - { const { ipv6, "allow-lan": allowLan, "log-level": logLevel } = clash ?? {}; - const { - enable_random_port = false, - verge_mixed_port, - enable_clash_fields = true, - } = verge ?? {}; + const { enable_random_port = false, verge_mixed_port } = verge ?? {}; const webRef = useRef(null); const fieldRef = useRef(null); @@ -70,7 +65,6 @@ const SettingClash = ({ onError }: Props) => { return ( - @@ -176,19 +170,6 @@ const SettingClash = ({ onError }: Props) => { - {enable_clash_fields && ( - - fieldRef.current?.open()} - > - - - - )} - { const patchInfo = async ( patch: Partial< - Pick + Pick< + IConfigData, + "port" | "socks-port" | "mixed-port" | "external-controller" | "secret" + > > ) => { const hasInfo = patch["mixed-port"] != null || + patch["socks-port"] != null || + patch["port"] != null || patch["external-controller"] != null || patch.secret != null; @@ -68,6 +73,26 @@ export const useClashInfo = () => { } } + if (patch["socks-port"]) { + const port = patch["socks-port"]; + if (port < 1000) { + throw new Error("The port should not < 1000"); + } + if (port > 65536) { + throw new Error("The port should not > 65536"); + } + } + + if (patch["port"]) { + const port = patch["port"]; + if (port < 1000) { + throw new Error("The port should not < 1000"); + } + if (port > 65536) { + throw new Error("The port should not > 65536"); + } + } + await patchClashConfig(patch); mutateInfo(); mutate("getClashConfig"); diff --git a/src/services/types.d.ts b/src/services/types.d.ts index abcb818b..626461b7 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -131,7 +131,9 @@ interface IConnections { interface IClashInfo { // status: string; - port?: number; // clash mixed port + mixed_port?: number; // clash mixed port + socks_port?: number; // clash socks port + port?: number; // clash http port server?: string; // external-controller secret?: string; } @@ -196,6 +198,8 @@ interface IVergeConfig { enable_system_proxy?: boolean; enable_random_port?: boolean; verge_mixed_port?: number; + verge_socks_port?: number; + verge_port?: number; enable_proxy_guard?: boolean; proxy_guard_duration?: number; system_proxy_bypass?: string; @@ -215,7 +219,6 @@ interface IVergeConfig { }; auto_close_connection?: boolean; default_latency_test?: string; - enable_clash_fields?: boolean; enable_builtin_enhanced?: boolean; auto_log_clean?: 0 | 1 | 2 | 3; proxy_layout_column?: number; diff --git a/src/utils/clash-fields.ts b/src/utils/clash-fields.ts deleted file mode 100644 index 7a9b369b..00000000 --- a/src/utils/clash-fields.ts +++ /dev/null @@ -1,53 +0,0 @@ -export const HANDLE_FIELDS = [ - "mode", - "port", - "socks-port", - "mixed-port", - "allow-lan", - "log-level", - "ipv6", - "secret", - "external-controller", -]; - -export const DEFAULT_FIELDS = [ - "proxies", - "proxy-groups", - "proxy-providers", - "rules", - "rule-providers", -] as const; - -export const OTHERS_FIELDS = [ - "dns", - "tun", - "ebpf", - "hosts", - "script", - "profile", - "payload", - "tunnels", - "auto-redir", - "experimental", - "interface-name", - "routing-mark", - "redir-port", - "tproxy-port", - "iptables", - "external-ui", - "bind-address", - "authentication", - "tls", // meta - "sniffer", // meta - "geox-url", // meta - "listeners", // meta - "sub-rules", // meta - "geodata-mode", // meta - "unified-delay", // meta - "tcp-concurrent", // meta - "enable-process", // meta - "find-process-mode", // meta - "skip-auth-prefixes", // meta - "external-controller-tls", // meta - "global-client-fingerprint", // meta -] as const;