mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-06 12:13:44 +08:00
feat: add External Controller toggle switch
This commit is contained in:
parent
18d24d5952
commit
4ce376f830
@ -1,32 +1,53 @@
|
||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
||||
import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { List, ListItem, ListItemText, TextField } from "@mui/material";
|
||||
import { List, ListItem, ListItemText, TextField, Typography, Box } from "@mui/material";
|
||||
import { useClashInfo } from "@/hooks/use-clash";
|
||||
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
|
||||
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { clashInfo, patchInfo } = useClashInfo();
|
||||
const { verge, patchVerge } = useVerge();
|
||||
|
||||
const [controller, setController] = useState(clashInfo?.server || "");
|
||||
const [secret, setSecret] = useState(clashInfo?.secret || "");
|
||||
|
||||
// 获取外部控制器开关状态
|
||||
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, () => ({
|
||||
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);
|
||||
}
|
||||
},
|
||||
close: () => setOpen(false),
|
||||
}));
|
||||
|
||||
const onSave = useLockFn(async () => {
|
||||
try {
|
||||
await patchInfo({ "external-controller": controller, secret });
|
||||
Notice.success(t("External Controller Address Modified"), 1000);
|
||||
// 只有在启用外部控制器时才更新配置
|
||||
if (enableController) {
|
||||
await patchInfo({ "external-controller": controller, secret });
|
||||
}
|
||||
Notice.success(t("External Controller Settings Saved"), 1000);
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString(), 4000);
|
||||
@ -44,9 +65,17 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant="body2" color={enableController ? "warning.main" : "text.secondary"}>
|
||||
{enableController
|
||||
? t("External controller is enabled info")
|
||||
: t("External controller is disabled info")}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<List>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("External Controller")} />
|
||||
<ListItemText primary={t("External Controller Address")} />
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
size="small"
|
||||
@ -54,6 +83,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
value={controller}
|
||||
placeholder="Required"
|
||||
onChange={(e) => setController(e.target.value)}
|
||||
disabled={!enableController}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@ -68,6 +98,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
onChange={(e) =>
|
||||
setSecret(e.target.value?.replace(/[^\x00-\x7F]/g, ""))
|
||||
}
|
||||
disabled={!enableController}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
@ -701,7 +701,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Prefer H3")}
|
||||
secondary={t("DNS DOH使用HTTP/3")}
|
||||
secondary={t("DNS DOH uses HTTP/3")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@ -713,7 +713,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Respect Rules")}
|
||||
secondary={t("DNS连接遵守路由规则")}
|
||||
secondary={t("DNS connections follow routing rules")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@ -749,7 +749,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Direct Nameserver Follow Policy")}
|
||||
secondary={t("是否遵循nameserver-policy")}
|
||||
secondary={t("Whether to follow nameserver policy")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
LanRounded,
|
||||
} from "@mui/icons-material";
|
||||
import { DialogRef, Notice, Switch } from "@/components/base";
|
||||
import { useClash } from "@/hooks/use-clash";
|
||||
import { useClash, useClashInfo } from "@/hooks/use-clash";
|
||||
import { GuardState } from "./mods/guard-state";
|
||||
import { WebUIViewer } from "./mods/web-ui-viewer";
|
||||
import { ClashPortViewer } from "./mods/clash-port-viewer";
|
||||
@ -36,6 +36,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
|
||||
const { clash, version, mutateClash, patchClash } = useClash();
|
||||
const { verge, mutateVerge, patchVerge } = useVerge();
|
||||
const { clashInfo, patchInfo } = useClashInfo();
|
||||
|
||||
const {
|
||||
ipv6,
|
||||
@ -58,6 +59,15 @@ 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);
|
||||
@ -108,6 +118,26 @@ 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());
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<SettingList title={t("Clash Setting")}>
|
||||
<WebUIViewer ref={webRef} />
|
||||
@ -248,11 +278,41 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
onClick={() => ctrlRef.current?.open()}
|
||||
label={t("External")}
|
||||
/>
|
||||
label={t("External Controller")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={SettingsRounded}
|
||||
onClick={() => ctrlRef.current?.open()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={enableController}
|
||||
onChange={(_, checked) => handleControllerToggle(checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem onClick={() => webRef.current?.open()} label={t("Web UI")} />
|
||||
<SettingItem
|
||||
onClick={enableController ? () => webRef.current?.open() : undefined}
|
||||
label={
|
||||
<Typography
|
||||
component="span"
|
||||
color={!enableController ? "text.disabled" : "text.primary"}
|
||||
sx={{ fontSize: "inherit" }}
|
||||
>
|
||||
{t("Web UI")}
|
||||
</Typography>
|
||||
}
|
||||
extra={
|
||||
!enableController && (
|
||||
<TooltipIcon
|
||||
title={t("Web UI info")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("Clash Core")}
|
||||
|
@ -257,8 +257,8 @@
|
||||
"Http Port": "منفذ HTTP(S)",
|
||||
"Redir Port": "منفذ إعادة التوجيه",
|
||||
"Tproxy Port": "منفذ Tproxy",
|
||||
"External": "خارجي",
|
||||
"External Controller": "وحدة التحكم الخارجية",
|
||||
"External Controller Address": "عنوان وحدة التحكم الخارجية",
|
||||
"Core Secret": "المفتاح السري للنواة",
|
||||
"Recommended": "موصى به",
|
||||
"Open URL": "فتح الرابط",
|
||||
|
@ -278,8 +278,11 @@
|
||||
"Http Port": "Http(s) Port",
|
||||
"Redir Port": "Redir Port",
|
||||
"Tproxy Port": "Tproxy Port",
|
||||
"External": "External",
|
||||
"External Controller": "External Controller",
|
||||
"External Controller Address": "External Controller Address",
|
||||
"External Controller Settings Saved": "External Controller Settings Saved",
|
||||
"External Controller is enabled info": "External Controller is enabled. Please set a secret password to avoid security risks.",
|
||||
"External controller is disabled info": "External controller is disabled. You only need to enable it if you want to control Clash core via web UI.",
|
||||
"Core Secret": "Core Secret",
|
||||
"Recommended": "Recommended",
|
||||
"Open URL": "Open URL",
|
||||
@ -434,6 +437,7 @@
|
||||
"Menu Icon": "Menu Icon",
|
||||
"PAC File": "PAC File",
|
||||
"Web UI": "Web UI",
|
||||
"Web UI info": "External controller is disabled, web UI cannot be used",
|
||||
"Hotkeys": "Hotkeys",
|
||||
"Verge Mixed Port": "Verge Mixed Port",
|
||||
"Verge Socks Port": "Verge Socks Port",
|
||||
@ -502,15 +506,15 @@
|
||||
"Fake IP Filter Mode": "Fake IP Filter Mode",
|
||||
"Enable IPv6 DNS resolution": "Enable IPv6 DNS resolution",
|
||||
"Prefer H3": "Prefer H3",
|
||||
"DNS DOH使用HTTP/3": "DNS DOH uses HTTP/3",
|
||||
"DNS DOH uses HTTP/3": "DNS DOH uses HTTP/3",
|
||||
"Respect Rules": "Respect Rules",
|
||||
"DNS连接遵守路由规则": "DNS connections follow routing rules",
|
||||
"DNS connections follow routing rules": "DNS connections follow routing rules",
|
||||
"Use Hosts": "Use Hosts",
|
||||
"Enable to resolve hosts through hosts file": "Enable to resolve hosts through hosts file",
|
||||
"Use System Hosts": "Use System Hosts",
|
||||
"Enable to resolve hosts through system hosts file": "Enable to resolve hosts through system hosts file",
|
||||
"Direct Nameserver Follow Policy": "Direct Nameserver Follow Policy",
|
||||
"是否遵循nameserver-policy": "Whether to follow nameserver policy",
|
||||
"Whether to follow nameserver policy": "Whether to follow nameserver policy",
|
||||
"Default Nameserver": "Default Nameserver",
|
||||
"Default DNS servers used to resolve DNS servers": "Default DNS servers used to resolve DNS servers",
|
||||
"Nameserver": "Nameserver",
|
||||
|
@ -259,8 +259,8 @@
|
||||
"Http Port": "پورت پروکسی Http(s)",
|
||||
"Redir Port": "پورت پروکسی شفاف Redir",
|
||||
"Tproxy Port": "پورت پروکسی شفاف Tproxy",
|
||||
"External": "خارجی",
|
||||
"External Controller": "کنترلکننده خارجی",
|
||||
"External Controller Address": "کنترلکننده خارجی",
|
||||
"Core Secret": "رمز اصلی",
|
||||
"Recommended": "توصیه شده",
|
||||
"Open URL": "باز کردن آدرس اینترنتی",
|
||||
|
@ -290,8 +290,8 @@
|
||||
"Http Port": "Port Http(s)",
|
||||
"Redir Port": "Port Redir",
|
||||
"Tproxy Port": "Port Tproxy",
|
||||
"External": "Eksternal",
|
||||
"External Controller": "Alamat Pengendali Eksternal",
|
||||
"External Controller": "Eksternal",
|
||||
"External Controller Address": "Alamat Pengendali Eksternal",
|
||||
"Core Secret": "Rahasia Inti",
|
||||
"Recommended": "Direkomendasikan",
|
||||
"Open URL": "Buka URL",
|
||||
|
@ -274,8 +274,8 @@
|
||||
"Http Port": "Порт Http(s)-прокси",
|
||||
"Redir Port": "Порт прозрачного прокси Redir",
|
||||
"Tproxy Port": "Порт прозрачного прокси Tproxy",
|
||||
"External": "Внешний контроллер",
|
||||
"External Controller": "Адрес прослушивания внешнего контроллера",
|
||||
"External Controller": "Внешний контроллер",
|
||||
"External Controller Address": "Адрес прослушивания внешнего контроллера",
|
||||
"Core Secret": "Секрет",
|
||||
"Recommended": "Рекомендуется",
|
||||
"Open URL": "Перейти по адресу",
|
||||
@ -496,15 +496,15 @@
|
||||
"Fake IP Range": "Диапазон FakeIP",
|
||||
"Fake IP Filter Mode": "FakeIP Filter Mode",
|
||||
"Prefer H3": "Предпочитать H3",
|
||||
"DNS DOH使用HTTP/3": "DNS DOH использует http/3",
|
||||
"DNS DOH uses HTTP/3": "DNS DOH использует http/3",
|
||||
"Respect Rules": "Приоритизировать правила",
|
||||
"DNS连接遵守路由规则": "Соединения DNS следуют правилам маршрутизации",
|
||||
"DNS connections follow routing rules": "Соединения DNS следуют правилам маршрутизации",
|
||||
"Use Hosts": "Использовать файл Hosts",
|
||||
"Enable to resolve hosts through hosts file": "Включить разрешение хостов через файл Hosts",
|
||||
"Use System Hosts": "Использовать системный файл Hosts",
|
||||
"Enable to resolve hosts through system hosts file": "Включить разрешение хостов через системный файл Hosts",
|
||||
"Direct Nameserver Follow Policy": "Прямой сервер имен следует политике",
|
||||
"是否遵循nameserver-policy": "Следовать ли политике DNS-серверов",
|
||||
"Whether to follow nameserver policy": "Следовать ли политике DNS-серверов",
|
||||
"Default Nameserver": "DNS-сервер по умолчанию",
|
||||
"Default DNS servers used to resolve DNS servers": "DNS-серверы по умолчанию, используемые для разрешения адресов серверов DNS",
|
||||
"Nameserver": "DNS-сервер",
|
||||
|
@ -258,8 +258,8 @@
|
||||
"Http Port": "HTTP(s) прокси порты",
|
||||
"Redir Port": "Redir — үтә күренмәле прокси порты",
|
||||
"Tproxy Port": "Tproxy — үтә күренмәле прокси порты",
|
||||
"External": "Тышкы",
|
||||
"External Controller": "Тышкы контроллер адресы",
|
||||
"External Controller": "Тышкы контроллер",
|
||||
"External Controller Address": "Тышкы контроллер адресы",
|
||||
"Core Secret": "Серсүз",
|
||||
"Recommended": "Тавсия ителә",
|
||||
"Open URL": "URL ачарга",
|
||||
|
@ -278,8 +278,11 @@
|
||||
"Http Port": "HTTP(S) 代理端口",
|
||||
"Redir Port": "Redir 透明代理端口",
|
||||
"TPROXY Port": "TPROXY 透明代理端口",
|
||||
"External": "外部控制",
|
||||
"External Controller": "外部控制器监听地址",
|
||||
"External Controller": "外部控制",
|
||||
"External Controller Address": "外部控制器监听地址",
|
||||
"External Controller Settings Saved": "外部控制器设置已保存",
|
||||
"External controller is enabled info": "外部控制器已启用。请设置一个密钥密码以避免安全风险。",
|
||||
"External controller is disabled info": "外部控制器已禁用。如果您想通过网页界面控制 Clash 核心才需启用。",
|
||||
"Core Secret": "API 访问密钥",
|
||||
"Recommended": "建议设置",
|
||||
"Open URL": "打开链接",
|
||||
@ -434,6 +437,7 @@
|
||||
"Menu Icon": "菜单图标",
|
||||
"PAC File": "PAC 文件",
|
||||
"Web UI": "网页界面",
|
||||
"Web UI info": "外部控制器已禁用,网页界面无法使用",
|
||||
"Hotkeys": "快捷键",
|
||||
"Verge Mixed Port": "Verge 混合端口",
|
||||
"Verge Socks Port": "Verge SOCKS 端口",
|
||||
@ -502,15 +506,15 @@
|
||||
"Fake IP Filter Mode": "Fake IP 过滤模式",
|
||||
"Enable IPv6 DNS resolution": "启用 IPv6 DNS 解析",
|
||||
"Prefer H3": "优先使用 HTTP/3",
|
||||
"DNS DOH使用HTTP/3": "DNS DOH 使用 HTTP/3 协议",
|
||||
"DNS DOH uses HTTP/3": "DNS DOH 使用 HTTP/3 协议",
|
||||
"Respect Rules": "遵循路由规则",
|
||||
"DNS连接遵守路由规则": "DNS 连接遵循路由规则",
|
||||
"DNS connections follow routing rules": "DNS 连接遵循路由规则",
|
||||
"Use Hosts": "使用 Hosts",
|
||||
"Enable to resolve hosts through hosts file": "启用通过 hosts 文件解析域名",
|
||||
"Use System Hosts": "使用系统 Hosts",
|
||||
"Enable to resolve hosts through system hosts file": "启用通过系统 hosts 文件解析域名",
|
||||
"Direct Nameserver Follow Policy": "直连域名服务器遵循策略",
|
||||
"是否遵循nameserver-policy": "是否遵循 nameserver-policy 设置",
|
||||
"Whether to follow nameserver policy": "是否遵循 nameserver-policy 设置",
|
||||
"Default Nameserver": "默认域名服务器",
|
||||
"Default DNS servers used to resolve DNS servers": "用于解析 DNS 服务器的默认 DNS 服务器",
|
||||
"Nameserver": "域名服务器",
|
||||
|
1
src/services/types.d.ts
vendored
1
src/services/types.d.ts
vendored
@ -747,6 +747,7 @@ interface IVergeConfig {
|
||||
enable_system_proxy?: boolean;
|
||||
enable_global_hotkey?: boolean;
|
||||
enable_dns_settings?: boolean;
|
||||
enable_external_controller?: boolean;
|
||||
proxy_auto_config?: boolean;
|
||||
pac_file_content?: string;
|
||||
enable_random_port?: boolean;
|
||||
|
Loading…
x
Reference in New Issue
Block a user