import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base"; import { BaseFieldset } from "@/components/base/base-fieldset"; import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { EditorViewer } from "@/components/profile/editor-viewer"; import { useVerge } from "@/hooks/use-verge"; import { getAutotemProxy, getSystemProxy } from "@/services/cmds"; import getSystem from "@/utils/get-system"; import { EditRounded } from "@mui/icons-material"; import { Button, InputAdornment, List, ListItem, ListItemText, styled, TextField, Typography, } from "@mui/material"; import { useLockFn } from "ahooks"; import { forwardRef, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; const DEFAULT_PAC = `function FindProxyForURL(url, host) { return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;"; }`; /** NO_PROXY validation */ // *., cdn*., *, etc. const domain_subdomain_part = String.raw`(?:[a-z0-9\-\*]+\.|\*)*`; // .*, .cn, .moe, .co*, * const domain_tld_part = String.raw`(?:\w{2,64}\*?|\*)`; // *epicgames*, *skk.moe, *.skk.moe, skk.*, sponsor.cdn.skk.moe, *.*, etc. // also matches 192.168.*, 10.*, 127.0.0.*, etc. (partial ipv4) const rDomainSimple = domain_subdomain_part + domain_tld_part; const ipv4_part = String.raw`\d{1,3}`; const ipv6_part = "(?:[a-fA-F0-9:])+"; const rLocal = `localhost||localdomain`; const getValidReg = (isWindows: boolean) => { // 127.0.0.1 (full ipv4) const rIPv4Unix = String.raw`(?:${ipv4_part}\.){3}${ipv4_part}(?:\/\d{1,2})?`; const rIPv4Windows = String.raw`(?:${ipv4_part}\.){3}${ipv4_part}`; const rIPv6Unix = String.raw`(?:${ipv6_part}:+)+${ipv6_part}(?:\/\d{1,3})?`; const rIPv6Windows = String.raw`(?:${ipv6_part}:+)+${ipv6_part}`; const rValidPart = `${rDomainSimple}|${ isWindows ? rIPv4Windows : rIPv4Unix }|${isWindows ? rIPv6Windows : rIPv6Unix}|${rLocal}`; const separator = isWindows ? ";" : ","; const rValid = String.raw`^(${rValidPart})(?:${separator}\s?(${rValidPart}))*${separator}?$`; return new RegExp(rValid); }; export const SysproxyViewer = forwardRef((props, ref) => { const { t } = useTranslation(); const isWindows = getSystem() === "windows"; const validReg = useMemo(() => getValidReg(isWindows), [isWindows]); const [open, setOpen] = useState(false); const [editorOpen, setEditorOpen] = useState(false); const { verge, patchVerge } = useVerge(); type SysProxy = Awaited>; const [sysproxy, setSysproxy] = useState(); type AutoProxy = Awaited>; const [autoproxy, setAutoproxy] = useState(); const { enable_system_proxy: enabled, proxy_auto_config, pac_file_content, enable_proxy_guard, enable_bypass_check, use_default_bypass, system_proxy_bypass, proxy_guard_duration, } = verge ?? {}; const [value, setValue] = useState({ guard: enable_proxy_guard, bypass: system_proxy_bypass, bypass_check: enable_bypass_check ?? true, duration: proxy_guard_duration ?? 10, use_default: use_default_bypass ?? true, pac: proxy_auto_config, pac_content: pac_file_content ?? DEFAULT_PAC, }); useImperativeHandle(ref, () => ({ open: () => { setOpen(true); setValue({ guard: enable_proxy_guard, bypass: system_proxy_bypass, bypass_check: enable_bypass_check ?? true, duration: proxy_guard_duration ?? 10, use_default: use_default_bypass ?? true, pac: proxy_auto_config, pac_content: pac_file_content ?? DEFAULT_PAC, }); getSystemProxy().then((p) => setSysproxy(p)); getAutotemProxy().then((p) => setAutoproxy(p)); }, close: () => setOpen(false), })); const onSave = useLockFn(async () => { if (value.duration < 1) { Notice.error(t("Proxy Daemon Duration Cannot be Less than 1 Second")); return; } const patch: Partial = {}; if (value.guard !== enable_proxy_guard) { patch.enable_proxy_guard = value.guard; } if (value.duration !== proxy_guard_duration) { patch.proxy_guard_duration = value.duration; } if (value.bypass !== system_proxy_bypass) { patch.system_proxy_bypass = value.bypass; } if (value.bypass_check !== enable_bypass_check) { patch.enable_bypass_check = value.bypass_check; } if (value.pac !== proxy_auto_config) { patch.proxy_auto_config = value.pac; } if (value.use_default !== use_default_bypass) { patch.use_default_bypass = value.use_default; } if (value.pac_content !== pac_file_content) { patch.pac_file_content = value.pac_content; } if (value.bypass && value.bypass_check && !validReg.test(value.bypass)) { Notice.error(t("Invalid Bypass Format")); return; } try { await patchVerge(patch); setOpen(false); } catch (err: any) { Notice.error(err.message || err.toString()); } }); return ( setOpen(false)} onCancel={() => setOpen(false)} onOk={onSave} > {t("Enable status")} {value.pac ? autoproxy?.enable ? t("Enabled") : t("Disabled") : sysproxy?.enable ? t("Enabled") : t("Disabled")} {!value.pac && ( <> {t("Server Addr")} {sysproxy?.server ? sysproxy.server : t("Not available")} )} {value.pac && ( {t("PAC URL")} {autoproxy?.url || "-"} )} setValue((v) => ({ ...v, pac: e }))} /> setValue((v) => ({ ...v, guard: e }))} sx={{ marginLeft: "auto" }} /> s, }} onChange={(e) => { setValue((v) => ({ ...v, duration: +e.target.value.replace(/\D/, ""), })); }} /> {!value.pac && ( setValue((v) => ({ ...v, use_default: e }))} /> )} {!value.pac && ( setValue((v) => ({ ...v, bypass_check: e }))} /> )} {!value.pac && ( <> { setValue((v) => ({ ...v, bypass: e.target.value })); }} /> )} {value.pac && ( <> {editorOpen && ( { let pac = DEFAULT_PAC; if (curr && curr.trim().length > 0) { pac = curr; } setValue((v) => ({ ...v, pac_content: pac })); }} onClose={() => setEditorOpen(false)} /> )} )} ); }); const FlexBox = styled("div")` display: flex; margin-top: 4px; .label { flex: none; //width: 85px; } `;