refactor: external toggle control logic & disable external-controller by default

This commit is contained in:
wonfen 2025-04-16 20:29:06 +08:00
parent d4a42f4ede
commit d84b5456ff
3 changed files with 78 additions and 69 deletions

View File

@ -50,7 +50,6 @@ impl IClashTemp {
map.insert("allow-lan".into(), false.into());
map.insert("ipv6".into(), true.into());
map.insert("mode".into(), "rule".into());
map.insert("external-controller".into(), "127.0.0.1:9097".into());
#[cfg(not(target_os = "windows"))]
map.insert("external-controller-unix".into(), "mihomo.sock".into());
#[cfg(target_os = "windows")]
@ -215,6 +214,10 @@ impl IClashTemp {
.and_then(|value| match value.as_str() {
Some(val_str) => {
let val_str = val_str.trim();
if val_str.is_empty() {
return None;
}
let val = match val_str.starts_with(':') {
true => format!("127.0.0.1{val_str}"),
@ -227,11 +230,15 @@ impl IClashTemp {
}
None => None,
})
.unwrap_or("127.0.0.1:9097".into())
.unwrap_or_else(|| String::new())
}
pub fn guard_client_ctrl(config: &Mapping) -> String {
let value = Self::guard_server_ctrl(config);
if value.is_empty() {
return value;
}
match SocketAddr::from_str(value.as_str()) {
Ok(mut socket) => {
if socket.ip().is_unspecified() {
@ -239,7 +246,7 @@ impl IClashTemp {
}
socket.to_string()
}
Err(_) => "127.0.0.1:9097".into(),
Err(_) => String::new(),
}
}
}
@ -278,12 +285,12 @@ fn test_clash_info() {
assert_eq!(
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!(
get_case(8888, "127.0.0.1:8888"),
@ -292,7 +299,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, " :98888 "),
get_result(8888, "127.0.0.1:9097")
get_result(8888, "")
);
assert_eq!(
@ -317,7 +324,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, "192.168.1.1:80800"),
get_result(8888, "127.0.0.1:9097")
get_result(8888, "")
);
}

View File

@ -3,8 +3,9 @@ import { useLockFn } from "ahooks";
import { useTranslation } from "react-i18next";
import { List, ListItem, ListItemText, TextField, Typography, Box } from "@mui/material";
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 { useClash } from "@/hooks/use-clash";
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation();
@ -12,43 +13,39 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
const { clashInfo, patchInfo } = useClashInfo();
const { verge, patchVerge } = useVerge();
const { clash } = useClash();
const [controller, setController] = useState(clashInfo?.server || "");
const [secret, setSecret] = useState(clashInfo?.secret || "");
const [controller, setController] = useState("");
const [secret, setSecret] = useState("");
// 获取外部控制器开关状态
const [enableController, setEnableController] = useState(() => {
const savedState = localStorage.getItem("enable_external_controller");
if (savedState !== null) {
return savedState === "true";
}
return verge?.enable_external_controller ?? true;
});
const enableController = Boolean(clash?.["external-controller"] && clash?.["external-controller"] !== "");
useImperativeHandle(ref, () => ({
open: () => {
setOpen(true);
setController(clashInfo?.server || "");
setSecret(clashInfo?.secret || "");
// 从localStorage更新开关状态
const savedState = localStorage.getItem("enable_external_controller");
if (savedState !== null) {
setEnableController(savedState === "true");
} else {
setEnableController(verge?.enable_external_controller ?? true);
}
setController(clash?.["external-controller"] || "");
setSecret(clash?.secret || "");
},
close: () => setOpen(false),
}));
const onSave = useLockFn(async () => {
try {
// 只有在启用外部控制器时才更新配置
if (enableController) {
await patchInfo({ "external-controller": controller, secret });
}
Notice.success(t("External Controller Settings Saved"), 1000);
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) {
Notice.error(err.message || err.toString(), 4000);
}
@ -61,6 +58,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
contentSx={{ width: 400 }}
okBtn={t("Save")}
cancelBtn={t("Cancel")}
disableOk={!enableController}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
onOk={onSave}
@ -81,7 +79,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
size="small"
sx={{ width: 175 }}
value={controller}
placeholder="Required"
placeholder="127.0.0.1:9097"
onChange={(e) => setController(e.target.value)}
disabled={!enableController}
/>

View File

@ -44,6 +44,7 @@ const SettingClash = ({ onError }: Props) => {
"log-level": logLevel,
"unified-delay": unifiedDelay,
dns,
"external-controller": externalController
} = clash ?? {};
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
@ -59,15 +60,6 @@ const SettingClash = ({ onError }: Props) => {
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 webRef = useRef<DialogRef>(null);
@ -118,25 +110,14 @@ const SettingClash = ({ onError }: Props) => {
}
});
// 处理外部控制器开关状态变化
const handleControllerToggle = useLockFn(async (enable: boolean) => {
try {
setEnableController(enable);
localStorage.setItem("enable_external_controller", String(enable));
await patchVerge({ enable_external_controller: enable });
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());
// 同步外部控制器配置和开关状态
useEffect(() => {
const hasController = Boolean(externalController && externalController !== "");
const isEnabled = Boolean(verge?.enable_external_controller);
if (hasController !== isEnabled) {
patchVerge({ enable_external_controller: hasController });
}
});
}, [externalController, verge?.enable_external_controller]);
return (
<SettingList title={t("Clash Setting")}>
@ -286,26 +267,49 @@ const SettingClash = ({ onError }: Props) => {
/>
}
>
<Switch
edge="end"
checked={enableController}
onChange={(_, checked) => handleControllerToggle(checked)}
/>
<GuardState
// 依据配置文件中是否有值来决定开关状态
value={Boolean(externalController && externalController !== "")}
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
onClick={enableController ? () => webRef.current?.open() : undefined}
onClick={(externalController && externalController !== "") ? () => webRef.current?.open() : undefined}
label={
<Typography
component="span"
color={!enableController ? "text.disabled" : "text.primary"}
color={(!externalController || externalController === "") ? "text.disabled" : "text.primary"}
sx={{ fontSize: "inherit" }}
>
{t("Web UI")}
</Typography>
}
extra={
!enableController && (
(!externalController || externalController === "") && (
<TooltipIcon
title={t("Web UI info")}
sx={{ opacity: "0.7" }}