mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 00:03:44 +08:00
refactor: external toggle control logic & disable external-controller by default
This commit is contained in:
parent
d4a42f4ede
commit
d84b5456ff
@ -50,7 +50,6 @@ impl IClashTemp {
|
|||||||
map.insert("allow-lan".into(), false.into());
|
map.insert("allow-lan".into(), false.into());
|
||||||
map.insert("ipv6".into(), true.into());
|
map.insert("ipv6".into(), true.into());
|
||||||
map.insert("mode".into(), "rule".into());
|
map.insert("mode".into(), "rule".into());
|
||||||
map.insert("external-controller".into(), "127.0.0.1:9097".into());
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
map.insert("external-controller-unix".into(), "mihomo.sock".into());
|
map.insert("external-controller-unix".into(), "mihomo.sock".into());
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@ -215,6 +214,10 @@ impl IClashTemp {
|
|||||||
.and_then(|value| match value.as_str() {
|
.and_then(|value| match value.as_str() {
|
||||||
Some(val_str) => {
|
Some(val_str) => {
|
||||||
let val_str = val_str.trim();
|
let val_str = val_str.trim();
|
||||||
|
|
||||||
|
if val_str.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let val = match val_str.starts_with(':') {
|
let val = match val_str.starts_with(':') {
|
||||||
true => format!("127.0.0.1{val_str}"),
|
true => format!("127.0.0.1{val_str}"),
|
||||||
@ -227,11 +230,15 @@ impl IClashTemp {
|
|||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
})
|
})
|
||||||
.unwrap_or("127.0.0.1:9097".into())
|
.unwrap_or_else(|| String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn guard_client_ctrl(config: &Mapping) -> String {
|
pub fn guard_client_ctrl(config: &Mapping) -> String {
|
||||||
let value = Self::guard_server_ctrl(config);
|
let value = Self::guard_server_ctrl(config);
|
||||||
|
if value.is_empty() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
match SocketAddr::from_str(value.as_str()) {
|
match SocketAddr::from_str(value.as_str()) {
|
||||||
Ok(mut socket) => {
|
Ok(mut socket) => {
|
||||||
if socket.ip().is_unspecified() {
|
if socket.ip().is_unspecified() {
|
||||||
@ -239,7 +246,7 @@ impl IClashTemp {
|
|||||||
}
|
}
|
||||||
socket.to_string()
|
socket.to_string()
|
||||||
}
|
}
|
||||||
Err(_) => "127.0.0.1:9097".into(),
|
Err(_) => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,12 +285,12 @@ fn test_clash_info() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
IClashTemp(IClashTemp::guard(Mapping::new())).get_client_info(),
|
IClashTemp(IClashTemp::guard(Mapping::new())).get_client_info(),
|
||||||
get_result(7897, "127.0.0.1:9097")
|
get_result(7897, "")
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(get_case("", ""), get_result(7897, "127.0.0.1:9097"));
|
assert_eq!(get_case("", ""), get_result(7897, ""));
|
||||||
|
|
||||||
assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9097"));
|
assert_eq!(get_case(65537, ""), get_result(1, ""));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_case(8888, "127.0.0.1:8888"),
|
get_case(8888, "127.0.0.1:8888"),
|
||||||
@ -292,7 +299,7 @@ fn test_clash_info() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_case(8888, " :98888 "),
|
get_case(8888, " :98888 "),
|
||||||
get_result(8888, "127.0.0.1:9097")
|
get_result(8888, "")
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -317,7 +324,7 @@ fn test_clash_info() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_case(8888, "192.168.1.1:80800"),
|
get_case(8888, "192.168.1.1:80800"),
|
||||||
get_result(8888, "127.0.0.1:9097")
|
get_result(8888, "")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@ import { useLockFn } from "ahooks";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { List, ListItem, ListItemText, TextField, Typography, Box } from "@mui/material";
|
import { List, ListItem, ListItemText, TextField, Typography, Box } from "@mui/material";
|
||||||
import { useClashInfo } from "@/hooks/use-clash";
|
import { useClashInfo } from "@/hooks/use-clash";
|
||||||
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
|
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
|
import { useClash } from "@/hooks/use-clash";
|
||||||
|
|
||||||
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -12,43 +13,39 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
|
|
||||||
const { clashInfo, patchInfo } = useClashInfo();
|
const { clashInfo, patchInfo } = useClashInfo();
|
||||||
const { verge, patchVerge } = useVerge();
|
const { verge, patchVerge } = useVerge();
|
||||||
|
const { clash } = useClash();
|
||||||
|
|
||||||
const [controller, setController] = useState(clashInfo?.server || "");
|
const [controller, setController] = useState("");
|
||||||
const [secret, setSecret] = useState(clashInfo?.secret || "");
|
const [secret, setSecret] = useState("");
|
||||||
|
|
||||||
// 获取外部控制器开关状态
|
const enableController = Boolean(clash?.["external-controller"] && clash?.["external-controller"] !== "");
|
||||||
const [enableController, setEnableController] = useState(() => {
|
|
||||||
const savedState = localStorage.getItem("enable_external_controller");
|
|
||||||
if (savedState !== null) {
|
|
||||||
return savedState === "true";
|
|
||||||
}
|
|
||||||
return verge?.enable_external_controller ?? true;
|
|
||||||
});
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
open: () => {
|
open: () => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setController(clashInfo?.server || "");
|
setController(clash?.["external-controller"] || "");
|
||||||
setSecret(clashInfo?.secret || "");
|
setSecret(clash?.secret || "");
|
||||||
// 从localStorage更新开关状态
|
|
||||||
const savedState = localStorage.getItem("enable_external_controller");
|
|
||||||
if (savedState !== null) {
|
|
||||||
setEnableController(savedState === "true");
|
|
||||||
} else {
|
|
||||||
setEnableController(verge?.enable_external_controller ?? true);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
close: () => setOpen(false),
|
close: () => setOpen(false),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const onSave = useLockFn(async () => {
|
const onSave = useLockFn(async () => {
|
||||||
try {
|
try {
|
||||||
// 只有在启用外部控制器时才更新配置
|
|
||||||
if (enableController) {
|
|
||||||
await patchInfo({ "external-controller": controller, secret });
|
|
||||||
}
|
|
||||||
Notice.success(t("External Controller Settings Saved"), 1000);
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
const promises = [];
|
||||||
|
promises.push(
|
||||||
|
patchInfo({
|
||||||
|
"external-controller": controller || "127.0.0.1:9097",
|
||||||
|
secret
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 同步verge配置
|
||||||
|
if (controller && controller !== "") {
|
||||||
|
promises.push(patchVerge({ enable_external_controller: true }));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
Notice.success(t("External Controller Settings Saved"), 1000);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Notice.error(err.message || err.toString(), 4000);
|
Notice.error(err.message || err.toString(), 4000);
|
||||||
}
|
}
|
||||||
@ -61,6 +58,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
contentSx={{ width: 400 }}
|
contentSx={{ width: 400 }}
|
||||||
okBtn={t("Save")}
|
okBtn={t("Save")}
|
||||||
cancelBtn={t("Cancel")}
|
cancelBtn={t("Cancel")}
|
||||||
|
disableOk={!enableController}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
onCancel={() => setOpen(false)}
|
onCancel={() => setOpen(false)}
|
||||||
onOk={onSave}
|
onOk={onSave}
|
||||||
@ -81,7 +79,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
size="small"
|
size="small"
|
||||||
sx={{ width: 175 }}
|
sx={{ width: 175 }}
|
||||||
value={controller}
|
value={controller}
|
||||||
placeholder="Required"
|
placeholder="127.0.0.1:9097"
|
||||||
onChange={(e) => setController(e.target.value)}
|
onChange={(e) => setController(e.target.value)}
|
||||||
disabled={!enableController}
|
disabled={!enableController}
|
||||||
/>
|
/>
|
||||||
|
@ -44,6 +44,7 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
"log-level": logLevel,
|
"log-level": logLevel,
|
||||||
"unified-delay": unifiedDelay,
|
"unified-delay": unifiedDelay,
|
||||||
dns,
|
dns,
|
||||||
|
"external-controller": externalController
|
||||||
} = clash ?? {};
|
} = clash ?? {};
|
||||||
|
|
||||||
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
|
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
|
||||||
@ -59,15 +60,6 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
return verge?.enable_dns_settings ?? false;
|
return verge?.enable_dns_settings ?? false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加外部控制器开关状态
|
|
||||||
const [enableController, setEnableController] = useState(() => {
|
|
||||||
const savedState = localStorage.getItem("enable_external_controller");
|
|
||||||
if (savedState !== null) {
|
|
||||||
return savedState === "true";
|
|
||||||
}
|
|
||||||
return verge?.enable_external_controller ?? true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const { addListener } = useListen();
|
const { addListener } = useListen();
|
||||||
|
|
||||||
const webRef = useRef<DialogRef>(null);
|
const webRef = useRef<DialogRef>(null);
|
||||||
@ -118,25 +110,14 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理外部控制器开关状态变化
|
// 同步外部控制器配置和开关状态
|
||||||
const handleControllerToggle = useLockFn(async (enable: boolean) => {
|
useEffect(() => {
|
||||||
try {
|
const hasController = Boolean(externalController && externalController !== "");
|
||||||
setEnableController(enable);
|
const isEnabled = Boolean(verge?.enable_external_controller);
|
||||||
localStorage.setItem("enable_external_controller", String(enable));
|
if (hasController !== isEnabled) {
|
||||||
await patchVerge({ enable_external_controller: enable });
|
patchVerge({ enable_external_controller: hasController });
|
||||||
if (!enable) {
|
|
||||||
await patchInfo({ "external-controller": "", secret: "" });
|
|
||||||
} else {
|
|
||||||
// 如果开启,恢复默认值或之前的值
|
|
||||||
const server = clashInfo?.server || "127.0.0.1:9097";
|
|
||||||
await patchInfo({ "external-controller": server, secret: clashInfo?.secret || "" });
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setEnableController(!enable);
|
|
||||||
localStorage.setItem("enable_external_controller", String(!enable));
|
|
||||||
Notice.error(err.message || err.toString());
|
|
||||||
}
|
}
|
||||||
});
|
}, [externalController, verge?.enable_external_controller]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingList title={t("Clash Setting")}>
|
<SettingList title={t("Clash Setting")}>
|
||||||
@ -286,26 +267,49 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Switch
|
<GuardState
|
||||||
edge="end"
|
// 依据配置文件中是否有值来决定开关状态
|
||||||
checked={enableController}
|
value={Boolean(externalController && externalController !== "")}
|
||||||
onChange={(_, checked) => handleControllerToggle(checked)}
|
valueProps="checked"
|
||||||
/>
|
onCatch={onError}
|
||||||
|
onFormat={onSwitchFormat}
|
||||||
|
onChange={(e) => {
|
||||||
|
onChangeVerge({ enable_external_controller: e });
|
||||||
|
onChangeData({
|
||||||
|
"external-controller": e ? (externalController || "127.0.0.1:9097") : ""
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onGuard={async (e) => {
|
||||||
|
const promises = [
|
||||||
|
patchVerge({ enable_external_controller: e })
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
// 如果禁用,清空配置
|
||||||
|
promises.push(patchClash({ "external-controller": "" }));
|
||||||
|
} else if (!externalController || externalController === "") {
|
||||||
|
promises.push(patchClash({ "external-controller": "127.0.0.1:9097" }));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Switch edge="end" />
|
||||||
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
onClick={enableController ? () => webRef.current?.open() : undefined}
|
onClick={(externalController && externalController !== "") ? () => webRef.current?.open() : undefined}
|
||||||
label={
|
label={
|
||||||
<Typography
|
<Typography
|
||||||
component="span"
|
component="span"
|
||||||
color={!enableController ? "text.disabled" : "text.primary"}
|
color={(!externalController || externalController === "") ? "text.disabled" : "text.primary"}
|
||||||
sx={{ fontSize: "inherit" }}
|
sx={{ fontSize: "inherit" }}
|
||||||
>
|
>
|
||||||
{t("Web UI")}
|
{t("Web UI")}
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
extra={
|
extra={
|
||||||
!enableController && (
|
(!externalController || externalController === "") && (
|
||||||
<TooltipIcon
|
<TooltipIcon
|
||||||
title={t("Web UI info")}
|
title={t("Web UI info")}
|
||||||
sx={{ opacity: "0.7" }}
|
sx={{ opacity: "0.7" }}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user