mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:03:45 +08:00
feat: add dns settings
This commit is contained in:
parent
69cb9769c1
commit
f80591242e
@ -92,7 +92,7 @@ export const useCustomTheme = () => {
|
||||
// css
|
||||
const backgroundColor = mode === "light" ? "#ECECEC" : "#2e303d";
|
||||
const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
|
||||
const scrollColor = mode === "light" ? "#90939980" : "#54545480";
|
||||
const scrollColor = mode === "light" ? "#90939980" : "#3E3E3Eee";
|
||||
const dividerColor =
|
||||
mode === "light" ? "rgba(0, 0, 0, 0.06)" : "rgba(255, 255, 255, 0.06)";
|
||||
|
||||
|
854
src/components/setting/mods/dns-viewer.tsx
Normal file
854
src/components/setting/mods/dns-viewer.tsx
Normal file
@ -0,0 +1,854 @@
|
||||
import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Select,
|
||||
styled,
|
||||
Switch,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { RestartAltRounded } from "@mui/icons-material";
|
||||
import { useClash } from "@/hooks/use-clash";
|
||||
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||
import yaml from "js-yaml";
|
||||
import MonacoEditor from "react-monaco-editor";
|
||||
import { useThemeMode } from "@/services/states";
|
||||
import getSystem from "@/utils/get-system";
|
||||
|
||||
const Item = styled(ListItem)(({ theme }) => ({
|
||||
padding: "8px 0",
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
"& textarea": {
|
||||
lineHeight: 1.5,
|
||||
fontSize: 14,
|
||||
resize: "vertical",
|
||||
},
|
||||
}));
|
||||
|
||||
// 默认DNS配置
|
||||
const DEFAULT_DNS_CONFIG = {
|
||||
enable: true,
|
||||
listen: ":53",
|
||||
"enhanced-mode": "fake-ip" as "fake-ip" | "redir-host",
|
||||
"fake-ip-range": "198.18.0.1/16",
|
||||
"fake-ip-filter-mode": "blacklist" as "blacklist" | "whitelist",
|
||||
"prefer-h3": false,
|
||||
"respect-rules": false,
|
||||
"use-hosts": false,
|
||||
"use-system-hosts": false,
|
||||
"fake-ip-filter": [
|
||||
"*.lan",
|
||||
"*.local",
|
||||
"*.arpa",
|
||||
"time.*.com",
|
||||
"ntp.*.com",
|
||||
"time.*.com",
|
||||
"+.market.xiaomi.com",
|
||||
"localhost.ptlogin2.qq.com",
|
||||
"*.msftncsi.com",
|
||||
"www.msftconnecttest.com",
|
||||
],
|
||||
"default-nameserver": ["223.6.6.6", "8.8.8.8"],
|
||||
nameserver: [
|
||||
"8.8.8.8",
|
||||
"https://doh.pub/dns-query",
|
||||
"https://dns.alidns.com/dns-query",
|
||||
],
|
||||
fallback: [
|
||||
"https://dns.alidns.com/dns-query",
|
||||
"https://dns.google/dns-query",
|
||||
"https://cloudflare-dns.com/dns-query",
|
||||
],
|
||||
"nameserver-policy": {},
|
||||
"proxy-server-nameserver": [
|
||||
"https://doh.pub/dns-query",
|
||||
"https://dns.alidns.com/dns-query",
|
||||
],
|
||||
"direct-nameserver": [],
|
||||
"direct-nameserver-follow-policy": false,
|
||||
"fallback-filter": {
|
||||
geoip: true,
|
||||
"geoip-code": "CN",
|
||||
ipcidr: ["240.0.0.0/4", "0.0.0.0/32"],
|
||||
domain: ["+.google.com", "+.facebook.com", "+.youtube.com"],
|
||||
},
|
||||
};
|
||||
|
||||
export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { clash, mutateClash, patchClash } = useClash();
|
||||
const themeMode = useThemeMode();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [visualization, setVisualization] = useState(true);
|
||||
const [values, setValues] = useState<{
|
||||
enable: boolean;
|
||||
listen: string;
|
||||
enhancedMode: "fake-ip" | "redir-host";
|
||||
fakeIpRange: string;
|
||||
fakeIpFilterMode: "blacklist" | "whitelist";
|
||||
preferH3: boolean;
|
||||
respectRules: boolean;
|
||||
fakeIpFilter: string;
|
||||
nameserver: string;
|
||||
fallback: string;
|
||||
defaultNameserver: string;
|
||||
useHosts: boolean;
|
||||
useSystemHosts: boolean;
|
||||
proxyServerNameserver: string;
|
||||
directNameserver: string;
|
||||
directNameserverFollowPolicy: boolean;
|
||||
fallbackGeoip: boolean;
|
||||
fallbackGeoipCode: string;
|
||||
fallbackIpcidr: string;
|
||||
fallbackDomain: string;
|
||||
nameserverPolicy: string;
|
||||
}>({
|
||||
enable: DEFAULT_DNS_CONFIG.enable,
|
||||
listen: DEFAULT_DNS_CONFIG.listen,
|
||||
enhancedMode: DEFAULT_DNS_CONFIG["enhanced-mode"],
|
||||
fakeIpRange: DEFAULT_DNS_CONFIG["fake-ip-range"],
|
||||
fakeIpFilterMode: DEFAULT_DNS_CONFIG["fake-ip-filter-mode"],
|
||||
preferH3: DEFAULT_DNS_CONFIG["prefer-h3"],
|
||||
respectRules: DEFAULT_DNS_CONFIG["respect-rules"],
|
||||
fakeIpFilter: DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
|
||||
defaultNameserver: DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
|
||||
nameserver: DEFAULT_DNS_CONFIG.nameserver.join(", "),
|
||||
fallback: DEFAULT_DNS_CONFIG.fallback.join(", "),
|
||||
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
|
||||
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
|
||||
proxyServerNameserver:
|
||||
DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || "",
|
||||
directNameserver: DEFAULT_DNS_CONFIG["direct-nameserver"]?.join(", ") || "",
|
||||
directNameserverFollowPolicy:
|
||||
DEFAULT_DNS_CONFIG["direct-nameserver-follow-policy"] || false,
|
||||
fallbackGeoip: DEFAULT_DNS_CONFIG["fallback-filter"].geoip,
|
||||
fallbackGeoipCode: DEFAULT_DNS_CONFIG["fallback-filter"]["geoip-code"],
|
||||
fallbackIpcidr:
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].ipcidr?.join(", ") || "",
|
||||
fallbackDomain:
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].domain?.join(", ") || "",
|
||||
nameserverPolicy: "",
|
||||
});
|
||||
|
||||
// 用于YAML编辑模式
|
||||
const [yamlContent, setYamlContent] = useState("");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: () => {
|
||||
setOpen(true);
|
||||
resetToDefaults();
|
||||
},
|
||||
close: () => setOpen(false),
|
||||
}));
|
||||
|
||||
// 重置为默认值
|
||||
const resetToDefaults = () => {
|
||||
setValues({
|
||||
enable: DEFAULT_DNS_CONFIG.enable,
|
||||
listen: DEFAULT_DNS_CONFIG.listen,
|
||||
enhancedMode: DEFAULT_DNS_CONFIG["enhanced-mode"],
|
||||
fakeIpRange: DEFAULT_DNS_CONFIG["fake-ip-range"],
|
||||
fakeIpFilterMode: DEFAULT_DNS_CONFIG["fake-ip-filter-mode"],
|
||||
preferH3: DEFAULT_DNS_CONFIG["prefer-h3"],
|
||||
respectRules: DEFAULT_DNS_CONFIG["respect-rules"],
|
||||
fakeIpFilter: DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
|
||||
defaultNameserver: DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
|
||||
nameserver: DEFAULT_DNS_CONFIG.nameserver.join(", "),
|
||||
fallback: DEFAULT_DNS_CONFIG.fallback.join(", "),
|
||||
useHosts: DEFAULT_DNS_CONFIG["use-hosts"],
|
||||
useSystemHosts: DEFAULT_DNS_CONFIG["use-system-hosts"],
|
||||
proxyServerNameserver:
|
||||
DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || "",
|
||||
directNameserver:
|
||||
DEFAULT_DNS_CONFIG["direct-nameserver"]?.join(", ") || "",
|
||||
directNameserverFollowPolicy:
|
||||
DEFAULT_DNS_CONFIG["direct-nameserver-follow-policy"] || false,
|
||||
fallbackGeoip: DEFAULT_DNS_CONFIG["fallback-filter"].geoip,
|
||||
fallbackGeoipCode: DEFAULT_DNS_CONFIG["fallback-filter"]["geoip-code"],
|
||||
fallbackIpcidr:
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].ipcidr?.join(", ") || "",
|
||||
fallbackDomain:
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].domain?.join(", ") || "",
|
||||
nameserverPolicy: "",
|
||||
});
|
||||
|
||||
// 更新YAML编辑器内容
|
||||
updateYamlFromValues(DEFAULT_DNS_CONFIG);
|
||||
};
|
||||
|
||||
// 从表单值更新YAML内容
|
||||
const updateYamlFromValues = (dnsConfig: any = null) => {
|
||||
// 如果提供了dnsConfig,直接使用它
|
||||
if (dnsConfig) {
|
||||
setYamlContent(yaml.dump(dnsConfig, { forceQuotes: true }));
|
||||
return;
|
||||
}
|
||||
|
||||
// 否则从当前表单值生成
|
||||
const config = generateDnsConfig();
|
||||
setYamlContent(yaml.dump(config, { forceQuotes: true }));
|
||||
};
|
||||
|
||||
// 从YAML更新表单值
|
||||
const updateValuesFromYaml = () => {
|
||||
try {
|
||||
const dnsConfig = yaml.load(yamlContent) as any;
|
||||
if (!dnsConfig) return;
|
||||
|
||||
const enhancedMode =
|
||||
dnsConfig["enhanced-mode"] || DEFAULT_DNS_CONFIG["enhanced-mode"];
|
||||
// 确保enhancedMode只能是"fake-ip"或"redir-host"
|
||||
const validEnhancedMode =
|
||||
enhancedMode === "fake-ip" || enhancedMode === "redir-host"
|
||||
? enhancedMode
|
||||
: DEFAULT_DNS_CONFIG["enhanced-mode"];
|
||||
|
||||
const fakeIpFilterMode =
|
||||
dnsConfig["fake-ip-filter-mode"] ||
|
||||
DEFAULT_DNS_CONFIG["fake-ip-filter-mode"];
|
||||
// 确保fakeIpFilterMode只能是"blacklist"或"whitelist"
|
||||
const validFakeIpFilterMode =
|
||||
fakeIpFilterMode === "blacklist" || fakeIpFilterMode === "whitelist"
|
||||
? fakeIpFilterMode
|
||||
: DEFAULT_DNS_CONFIG["fake-ip-filter-mode"];
|
||||
|
||||
setValues({
|
||||
enable: dnsConfig.enable ?? DEFAULT_DNS_CONFIG.enable,
|
||||
listen: dnsConfig.listen ?? DEFAULT_DNS_CONFIG.listen,
|
||||
enhancedMode: validEnhancedMode,
|
||||
fakeIpRange:
|
||||
dnsConfig["fake-ip-range"] ?? DEFAULT_DNS_CONFIG["fake-ip-range"],
|
||||
fakeIpFilterMode: validFakeIpFilterMode,
|
||||
preferH3: dnsConfig["prefer-h3"] ?? DEFAULT_DNS_CONFIG["prefer-h3"],
|
||||
respectRules:
|
||||
dnsConfig["respect-rules"] ?? DEFAULT_DNS_CONFIG["respect-rules"],
|
||||
fakeIpFilter:
|
||||
dnsConfig["fake-ip-filter"]?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG["fake-ip-filter"].join(", "),
|
||||
defaultNameserver:
|
||||
dnsConfig["default-nameserver"]?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
|
||||
nameserver:
|
||||
dnsConfig.nameserver?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG.nameserver.join(", "),
|
||||
fallback:
|
||||
dnsConfig.fallback?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG.fallback.join(", "),
|
||||
useHosts: dnsConfig["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
|
||||
useSystemHosts:
|
||||
dnsConfig["use-system-hosts"] ??
|
||||
DEFAULT_DNS_CONFIG["use-system-hosts"],
|
||||
proxyServerNameserver:
|
||||
dnsConfig["proxy-server-nameserver"]?.join(", ") ??
|
||||
(DEFAULT_DNS_CONFIG["proxy-server-nameserver"]?.join(", ") || ""),
|
||||
directNameserver:
|
||||
dnsConfig["direct-nameserver"]?.join(", ") ??
|
||||
(DEFAULT_DNS_CONFIG["direct-nameserver"]?.join(", ") || ""),
|
||||
directNameserverFollowPolicy:
|
||||
dnsConfig["direct-nameserver-follow-policy"] ??
|
||||
DEFAULT_DNS_CONFIG["direct-nameserver-follow-policy"],
|
||||
fallbackGeoip:
|
||||
dnsConfig["fallback-filter"]?.geoip ??
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].geoip,
|
||||
fallbackGeoipCode:
|
||||
dnsConfig["fallback-filter"]?.["geoip-code"] ??
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"]["geoip-code"],
|
||||
fallbackIpcidr:
|
||||
dnsConfig["fallback-filter"]?.ipcidr?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].ipcidr.join(", "),
|
||||
fallbackDomain:
|
||||
dnsConfig["fallback-filter"]?.domain?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG["fallback-filter"].domain.join(", "),
|
||||
nameserverPolicy:
|
||||
formatNameserverPolicy(dnsConfig["nameserver-policy"]) || "",
|
||||
});
|
||||
} catch (err: any) {
|
||||
Notice.error(t("Invalid YAML format"));
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化nameserver-policy为字符串
|
||||
const formatNameserverPolicy = (policy: any): string => {
|
||||
if (!policy) return "";
|
||||
|
||||
let result: string[] = [];
|
||||
|
||||
Object.entries(policy).forEach(([domain, servers]) => {
|
||||
if (Array.isArray(servers)) {
|
||||
// 处理数组格式的服务器
|
||||
const serversStr = servers.join(";");
|
||||
result.push(`${domain}=${serversStr}`);
|
||||
} else {
|
||||
// 处理单个服务器
|
||||
result.push(`${domain}=${servers}`);
|
||||
}
|
||||
});
|
||||
|
||||
return result.join(", ");
|
||||
};
|
||||
|
||||
// 解析nameserver-policy为对象
|
||||
const parseNameserverPolicy = (str: string): Record<string, any> => {
|
||||
const result: Record<string, any> = {};
|
||||
if (!str) return result;
|
||||
|
||||
str.split(",").forEach((item) => {
|
||||
const parts = item.trim().split("=");
|
||||
if (parts.length < 2) return;
|
||||
|
||||
const domain = parts[0].trim();
|
||||
const serversStr = parts.slice(1).join("=").trim();
|
||||
|
||||
// 检查是否包含多个分号分隔的服务器
|
||||
if (serversStr.includes(";")) {
|
||||
// 多个服务器,作为数组处理
|
||||
result[domain] = serversStr
|
||||
.split(";")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
} else {
|
||||
// 单个服务器
|
||||
result[domain] = serversStr;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// 初始化时设置默认YAML
|
||||
useEffect(() => {
|
||||
updateYamlFromValues(DEFAULT_DNS_CONFIG);
|
||||
}, []);
|
||||
|
||||
// 切换编辑模式时的处理
|
||||
useEffect(() => {
|
||||
if (visualization) {
|
||||
// 从YAML更新表单值
|
||||
updateValuesFromYaml();
|
||||
} else {
|
||||
// 从表单值更新YAML
|
||||
updateYamlFromValues();
|
||||
}
|
||||
}, [visualization]);
|
||||
|
||||
// 解析列表字符串为数组
|
||||
const parseList = (str: string): string[] => {
|
||||
if (!str) return [];
|
||||
return str
|
||||
.split(",")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
// 生成DNS配置对象
|
||||
const generateDnsConfig = () => {
|
||||
const dnsConfig: any = {
|
||||
enable: values.enable,
|
||||
listen: values.listen,
|
||||
"enhanced-mode": values.enhancedMode,
|
||||
"fake-ip-range": values.fakeIpRange,
|
||||
"fake-ip-filter-mode": values.fakeIpFilterMode,
|
||||
"prefer-h3": values.preferH3,
|
||||
"respect-rules": values.respectRules,
|
||||
"fake-ip-filter": parseList(values.fakeIpFilter),
|
||||
"default-nameserver": parseList(values.defaultNameserver),
|
||||
nameserver: parseList(values.nameserver),
|
||||
fallback: parseList(values.fallback),
|
||||
"use-hosts": values.useHosts,
|
||||
"use-system-hosts": values.useSystemHosts,
|
||||
"fallback-filter": {
|
||||
geoip: values.fallbackGeoip,
|
||||
"geoip-code": values.fallbackGeoipCode,
|
||||
ipcidr: parseList(values.fallbackIpcidr),
|
||||
domain: parseList(values.fallbackDomain),
|
||||
},
|
||||
};
|
||||
|
||||
// 只在有nameserverPolicy时添加
|
||||
const policy = parseNameserverPolicy(values.nameserverPolicy);
|
||||
if (Object.keys(policy).length > 0) {
|
||||
dnsConfig["nameserver-policy"] = policy;
|
||||
}
|
||||
|
||||
// 只在有值时添加其他可选字段
|
||||
if (values.proxyServerNameserver) {
|
||||
dnsConfig["proxy-server-nameserver"] = parseList(
|
||||
values.proxyServerNameserver,
|
||||
);
|
||||
}
|
||||
|
||||
if (values.directNameserver) {
|
||||
dnsConfig["direct-nameserver"] = parseList(values.directNameserver);
|
||||
}
|
||||
|
||||
dnsConfig["direct-nameserver-follow-policy"] =
|
||||
values.directNameserverFollowPolicy;
|
||||
|
||||
return dnsConfig;
|
||||
};
|
||||
|
||||
const onSave = useLockFn(async () => {
|
||||
try {
|
||||
let dnsConfig;
|
||||
|
||||
if (visualization) {
|
||||
// 使用表单值
|
||||
dnsConfig = generateDnsConfig();
|
||||
} else {
|
||||
// 使用YAML编辑器的值
|
||||
const parsedConfig = yaml.load(yamlContent);
|
||||
if (typeof parsedConfig !== "object" || parsedConfig === null) {
|
||||
throw new Error(t("Invalid DNS configuration"));
|
||||
}
|
||||
dnsConfig = parsedConfig;
|
||||
}
|
||||
|
||||
await patchClash({ dns: dnsConfig });
|
||||
mutateClash();
|
||||
setOpen(false);
|
||||
Notice.success(t("DNS settings saved"));
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
// YAML编辑器内容变更处理
|
||||
const handleYamlChange = (value: string) => {
|
||||
setYamlContent(value || "");
|
||||
|
||||
// 允许YAML编辑后立即分析和更新表单值
|
||||
try {
|
||||
const dnsConfig = yaml.load(value) as any;
|
||||
if (dnsConfig && typeof dnsConfig === "object") {
|
||||
// 稍微延迟更新,以避免性能问题
|
||||
setTimeout(() => {
|
||||
updateValuesFromYaml();
|
||||
}, 300);
|
||||
}
|
||||
} catch (err) {
|
||||
// 忽略解析错误,只有当YAML有效时才更新表单
|
||||
console.log("YAML解析错误,忽略自动更新", err);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理表单值变化
|
||||
const handleChange = (field: string) => (event: any) => {
|
||||
const value =
|
||||
event.target.type === "checkbox"
|
||||
? event.target.checked
|
||||
: event.target.value;
|
||||
|
||||
setValues((prev) => {
|
||||
const newValues = {
|
||||
...prev,
|
||||
[field]: value,
|
||||
};
|
||||
|
||||
// 当可视化编辑模式下的值变化时,自动更新YAML
|
||||
if (visualization) {
|
||||
setTimeout(() => {
|
||||
updateYamlFromValues(null);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return newValues;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
{t("DNS Settings")}
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="warning"
|
||||
startIcon={<RestartAltRounded />}
|
||||
onClick={resetToDefaults}
|
||||
>
|
||||
{t("Reset to Default")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setVisualization((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
{visualization ? t("Advanced") : t("Visualization")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
contentSx={{
|
||||
width: 550,
|
||||
overflow: "auto",
|
||||
...(visualization
|
||||
? {}
|
||||
: { padding: 0, display: "flex", flexDirection: "column" }),
|
||||
}}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
{/* Warning message */}
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="warning.main"
|
||||
sx={{ mb: 2, mt: 0, fontStyle: "italic" }}
|
||||
>
|
||||
{t("DNS Settings Warning")}
|
||||
</Typography>
|
||||
|
||||
{visualization ? (
|
||||
<List>
|
||||
<Item>
|
||||
<ListItemText primary={t("Enable DNS")} />
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.enable}
|
||||
onChange={handleChange("enable")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("DNS Listen")} />
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
value={values.listen}
|
||||
onChange={handleChange("listen")}
|
||||
placeholder=":53"
|
||||
sx={{ width: 150 }}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Enhanced Mode")} />
|
||||
<FormControl size="small" sx={{ width: 150 }}>
|
||||
<Select
|
||||
value={values.enhancedMode}
|
||||
onChange={handleChange("enhancedMode")}
|
||||
>
|
||||
<MenuItem value="fake-ip">fake-ip</MenuItem>
|
||||
<MenuItem value="redir-host">redir-host</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Fake IP Range")} />
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
value={values.fakeIpRange}
|
||||
onChange={handleChange("fakeIpRange")}
|
||||
placeholder="198.18.0.1/16"
|
||||
sx={{ width: 150 }}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Fake IP Filter Mode")} />
|
||||
<FormControl size="small" sx={{ width: 150 }}>
|
||||
<Select
|
||||
value={values.fakeIpFilterMode}
|
||||
onChange={handleChange("fakeIpFilterMode")}
|
||||
>
|
||||
<MenuItem value="blacklist">blacklist</MenuItem>
|
||||
<MenuItem value="whitelist">whitelist</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Prefer H3")}
|
||||
secondary={t("DNS DOH使用HTTP/3")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.preferH3}
|
||||
onChange={handleChange("preferH3")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Respect Rules")}
|
||||
secondary={t("DNS连接遵守路由规则")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.respectRules}
|
||||
onChange={handleChange("respectRules")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Use Hosts")}
|
||||
secondary={t("Enable to resolve hosts through hosts file")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.useHosts}
|
||||
onChange={handleChange("useHosts")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Use System Hosts")}
|
||||
secondary={t("Enable to resolve hosts through system hosts file")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.useSystemHosts}
|
||||
onChange={handleChange("useSystemHosts")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Direct Nameserver Follow Policy")}
|
||||
secondary={t("是否遵循nameserver-policy")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.directNameserverFollowPolicy}
|
||||
onChange={handleChange("directNameserverFollowPolicy")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Default Nameserver")}
|
||||
secondary={t("Default DNS servers used to resolve DNS servers")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={3}
|
||||
size="small"
|
||||
value={values.defaultNameserver}
|
||||
onChange={handleChange("defaultNameserver")}
|
||||
placeholder="223.6.6.6, 8.8.8.8"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Nameserver")}
|
||||
secondary={t("List of DNS servers")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
size="small"
|
||||
value={values.nameserver}
|
||||
onChange={handleChange("nameserver")}
|
||||
placeholder="8.8.8.8, https://doh.pub/dns-query, https://dns.alidns.com/dns-query"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback")}
|
||||
secondary={t("List of fallback DNS servers")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
size="small"
|
||||
value={values.fallback}
|
||||
onChange={handleChange("fallback")}
|
||||
placeholder="https://dns.alidns.com/dns-query, https://dns.google/dns-query, https://cloudflare-dns.com/dns-query"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Proxy Server Nameserver")}
|
||||
secondary={t("Proxy Node Nameserver")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={3}
|
||||
size="small"
|
||||
value={values.proxyServerNameserver}
|
||||
onChange={handleChange("proxyServerNameserver")}
|
||||
placeholder="https://doh.pub/dns-query, https://dns.alidns.com/dns-query"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Direct Nameserver")}
|
||||
secondary={t("Direct outbound Nameserver")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={3}
|
||||
size="small"
|
||||
value={values.directNameserver}
|
||||
onChange={handleChange("directNameserver")}
|
||||
placeholder="system, 223.6.6.6"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fake IP Filter")}
|
||||
secondary={t("Domains that skip fake IP resolution")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
size="small"
|
||||
value={values.fakeIpFilter}
|
||||
onChange={handleChange("fakeIpFilter")}
|
||||
placeholder="*.lan, *.local, localhost.ptlogin2.qq.com"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Nameserver Policy")}
|
||||
secondary={t("Domain-specific DNS server")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={4}
|
||||
size="small"
|
||||
value={values.nameserverPolicy}
|
||||
onChange={handleChange("nameserverPolicy")}
|
||||
placeholder="+.arpa=10.0.0.1, rule-set:cn=https://doh.pub/dns-query;https://dns.alidns.com/dns-query"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
sx={{ mt: 2, mb: 1, fontWeight: "bold" }}
|
||||
>
|
||||
{t("Fallback Filter Settings")}
|
||||
</Typography>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("GeoIP Filtering")}
|
||||
secondary={t("Enable GeoIP filtering for fallback")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.fallbackGeoip}
|
||||
onChange={handleChange("fallbackGeoip")}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("GeoIP Code")} />
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
value={values.fallbackGeoipCode}
|
||||
onChange={handleChange("fallbackGeoipCode")}
|
||||
placeholder="CN"
|
||||
sx={{ width: 100 }}
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback IP CIDR")}
|
||||
secondary={t("IP CIDRs not using fallback servers")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={3}
|
||||
size="small"
|
||||
value={values.fallbackIpcidr}
|
||||
onChange={handleChange("fallbackIpcidr")}
|
||||
placeholder="240.0.0.0/4, 127.0.0.1/8"
|
||||
/>
|
||||
</Item>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback Domain")}
|
||||
secondary={t("Domains using fallback servers")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={3}
|
||||
size="small"
|
||||
value={values.fallbackDomain}
|
||||
onChange={handleChange("fallbackDomain")}
|
||||
placeholder="+.google.com, +.facebook.com, +.youtube.com"
|
||||
/>
|
||||
</Item>
|
||||
</List>
|
||||
) : (
|
||||
<MonacoEditor
|
||||
height="100vh"
|
||||
language="yaml"
|
||||
value={yamlContent}
|
||||
theme={themeMode === "light" ? "vs" : "vs-dark"}
|
||||
className="flex-grow"
|
||||
options={{
|
||||
tabSize: 2,
|
||||
minimap: {
|
||||
enabled: document.documentElement.clientWidth >= 1500,
|
||||
},
|
||||
mouseWheelZoom: true,
|
||||
quickSuggestions: {
|
||||
strings: true,
|
||||
comments: true,
|
||||
other: true,
|
||||
},
|
||||
padding: {
|
||||
top: 33,
|
||||
},
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${
|
||||
getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontLigatures: true,
|
||||
smoothScrolling: true,
|
||||
}}
|
||||
onChange={handleYamlChange}
|
||||
/>
|
||||
)}
|
||||
</BaseDialog>
|
||||
);
|
||||
});
|
@ -47,7 +47,7 @@ export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
contentSx={{ width: 450, maxHeight: 330 }}
|
||||
contentSx={{ width: 450 }}
|
||||
disableOk
|
||||
cancelBtn={t("Close")}
|
||||
onCancel={() => setOpen(false)}
|
||||
@ -66,7 +66,7 @@ export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
label={t("Ip Address")}
|
||||
content={address.V4.ip}
|
||||
/>
|
||||
)
|
||||
),
|
||||
)}
|
||||
<AddressDisplay
|
||||
label={t("Mac Address")}
|
||||
@ -84,7 +84,7 @@ export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
label={t("Ip Address")}
|
||||
content={address.V6.ip}
|
||||
/>
|
||||
)
|
||||
),
|
||||
)}
|
||||
<AddressDisplay
|
||||
label={t("Mac Address")}
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
SettingsRounded,
|
||||
ShuffleRounded,
|
||||
LanRounded,
|
||||
DnsRounded,
|
||||
} from "@mui/icons-material";
|
||||
import { DialogRef, Notice, Switch } from "@/components/base";
|
||||
import { useClash } from "@/hooks/use-clash";
|
||||
@ -20,6 +21,7 @@ import { useVerge } from "@/hooks/use-verge";
|
||||
import { updateGeoData } from "@/services/api";
|
||||
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
|
||||
import { NetworkInterfaceViewer } from "./mods/network-interface-viewer";
|
||||
import { DnsViewer } from "./mods/dns-viewer";
|
||||
|
||||
const isWIN = getSystem() === "windows";
|
||||
|
||||
@ -38,6 +40,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
"allow-lan": allowLan,
|
||||
"log-level": logLevel,
|
||||
"unified-delay": unifiedDelay,
|
||||
dns,
|
||||
} = clash ?? {};
|
||||
|
||||
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
|
||||
@ -47,6 +50,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
const ctrlRef = useRef<DialogRef>(null);
|
||||
const coreRef = useRef<DialogRef>(null);
|
||||
const networkRef = useRef<DialogRef>(null);
|
||||
const dnsRef = useRef<DialogRef>(null);
|
||||
|
||||
const onSwitchFormat = (_e: any, value: boolean) => value;
|
||||
const onChangeData = (patch: Partial<IConfigData>) => {
|
||||
@ -71,6 +75,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
<ControllerViewer ref={ctrlRef} />
|
||||
<ClashCoreViewer ref={coreRef} />
|
||||
<NetworkInterfaceViewer ref={networkRef} />
|
||||
<DnsViewer ref={dnsRef} />
|
||||
|
||||
<SettingItem
|
||||
label={t("Allow Lan")}
|
||||
@ -97,6 +102,27 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("DNS Settings")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={SettingsRounded}
|
||||
onClick={() => dnsRef.current?.open()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<GuardState
|
||||
value={dns?.enable ?? false}
|
||||
valueProps="checked"
|
||||
onCatch={onError}
|
||||
onFormat={onSwitchFormat}
|
||||
onChange={(e) => onChangeData({ dns: { ...dns, enable: e } })}
|
||||
onGuard={(e) => patchClash({ dns: { enable: e } })}
|
||||
>
|
||||
<Switch edge="end" />
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("IPv6")}>
|
||||
<GuardState
|
||||
value={ipv6 ?? false}
|
||||
|
@ -471,5 +471,44 @@
|
||||
"Validate Merge File": "Validate Merge File",
|
||||
"Validation Success": "Validation Success",
|
||||
"Validation Failed": "Validation Failed",
|
||||
"Service Administrator Prompt": "Clash Verge requires administrator privileges to reinstall the system service"
|
||||
"Service Administrator Prompt": "Clash Verge requires administrator privileges to reinstall the system service",
|
||||
"DNS Settings": "DNS Settings",
|
||||
"DNS Settings Warning": "If you are not familiar with these settings, please do not modify them and keep DNS Settings enabled",
|
||||
"Enable DNS": "Enable DNS",
|
||||
"DNS Listen": "DNS Listen",
|
||||
"Enhanced Mode": "Enhanced Mode",
|
||||
"Fake IP Range": "Fake IP Range",
|
||||
"Fake IP Filter Mode": "Fake IP Filter Mode",
|
||||
"Prefer H3": "Prefer H3",
|
||||
"DNS DOH使用HTTP/3": "DNS DOH uses HTTP/3",
|
||||
"Respect Rules": "Respect Rules",
|
||||
"DNS连接遵守路由规则": "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",
|
||||
"Default Nameserver": "Default Nameserver",
|
||||
"Default DNS servers used to resolve DNS servers": "Default DNS servers used to resolve DNS servers",
|
||||
"Nameserver": "Nameserver",
|
||||
"List of DNS servers": "List of DNS servers, comma separated",
|
||||
"Fallback": "Fallback",
|
||||
"List of fallback DNS servers": "List of fallback DNS servers, comma separated",
|
||||
"Proxy Server Nameserver": "Proxy Server Nameserver",
|
||||
"Proxy Node Nameserver": "DNS servers for proxy node domain resolution",
|
||||
"Direct Nameserver": "Direct Nameserver",
|
||||
"Direct outbound Nameserver": "DNS servers for direct exit domain resolution, supports 'system' keyword, comma separated",
|
||||
"Fake IP Filter": "Fake IP Filter",
|
||||
"Domains that skip fake IP resolution": "Domains that skip fake IP resolution, comma separated",
|
||||
"Nameserver Policy": "Nameserver Policy",
|
||||
"Domain-specific DNS server": "Domain-specific DNS server, multiple servers separated by semicolons, format: domain=server1;server2",
|
||||
"Fallback Filter Settings": "Fallback Filter Settings",
|
||||
"GeoIP Filtering": "GeoIP Filtering",
|
||||
"Enable GeoIP filtering for fallback": "Enable GeoIP filtering for fallback",
|
||||
"GeoIP Code": "GeoIP Code",
|
||||
"Fallback IP CIDR": "Fallback IP CIDR",
|
||||
"IP CIDRs not using fallback servers": "IP CIDRs not using fallback servers, comma separated",
|
||||
"Fallback Domain": "Fallback Domain",
|
||||
"Domains using fallback servers": "Domains using fallback servers, comma separated"
|
||||
}
|
||||
|
@ -463,5 +463,45 @@
|
||||
"Validation Failed": "验证失败",
|
||||
"Verge Basic Setting": "Verge 基础设置",
|
||||
"Verge Advanced Setting": "Verge 高级设置",
|
||||
"Service Administrator Prompt": "Clash Verge 需要使用管理员权限来重新安装系统服务"
|
||||
"Service Administrator Prompt": "Clash Verge 需要使用管理员权限来重新安装系统服务",
|
||||
"DNS Settings": "DNS 设置",
|
||||
"DNS Settings Warning": "如果你不清楚这里的设置请不要修改,并保持dns设置开启",
|
||||
"Enable DNS": "启用 DNS",
|
||||
"DNS Listen": "DNS 监听地址",
|
||||
"Enhanced Mode": "增强模式",
|
||||
"Fake IP Range": "Fake IP 范围",
|
||||
"Fake IP Filter Mode": "Fake IP 过滤模式",
|
||||
"Prefer H3": "优先使用 HTTP/3",
|
||||
"DNS DOH使用HTTP/3": "DNS DOH 使用 HTTP/3 协议",
|
||||
"Respect Rules": "遵循路由规则",
|
||||
"DNS连接遵守路由规则": "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 设置",
|
||||
"Default Nameserver": "默认域名服务器",
|
||||
"Default DNS servers used to resolve DNS servers": "用于解析 DNS 服务器的默认 DNS 服务器",
|
||||
"Nameserver": "域名服务器",
|
||||
"List of DNS servers": "DNS 服务器列表,用逗号分隔",
|
||||
"Fallback": "回退服务器",
|
||||
"List of fallback DNS servers": "回退 DNS 服务器列表,用逗号分隔",
|
||||
"Proxy Server Nameserver": "代理节点DNS",
|
||||
"Proxy Node Nameserver": "代理节点域名解析服务器,仅用于解析代理节点的域名,用逗号分隔",
|
||||
"Direct Nameserver": "直连域名服务器",
|
||||
"Direct outbound Nameserver": "直连出口域名解析服务器,支持 system 关键字,用逗号分隔",
|
||||
"Fake IP Filter": "Fake IP 过滤",
|
||||
"Domains that skip fake IP resolution": "跳过 Fake IP 解析的域名,用逗号分隔",
|
||||
"Nameserver Policy": "域名服务器策略",
|
||||
"Domain-specific DNS server": "特定域名的 DNS 服务器,多个服务器使用分号分隔,格式: domain=server1;server2",
|
||||
"Fallback Filter Settings": "回退过滤设置",
|
||||
"GeoIP Filtering": "GeoIP 过滤",
|
||||
"Enable GeoIP filtering for fallback": "启用 GeoIP 回退过滤",
|
||||
"GeoIP Code": "GeoIP 国家代码",
|
||||
"Fallback IP CIDR": "回退 IP CIDR",
|
||||
"IP CIDRs not using fallback servers": "不使用回退服务器的 IP CIDR,用逗号分隔",
|
||||
"Fallback Domain": "回退域名",
|
||||
"Domains using fallback servers": "使用回退服务器的域名,用逗号分隔",
|
||||
"Fallback Geosite": "回退 Geosite"
|
||||
}
|
||||
|
25
src/services/types.d.ts
vendored
25
src/services/types.d.ts
vendored
@ -42,6 +42,31 @@ interface IConfigData {
|
||||
"strict-route": boolean;
|
||||
mtu: number;
|
||||
};
|
||||
dns?: {
|
||||
enable?: boolean;
|
||||
listen?: string;
|
||||
"enhanced-mode"?: "fake-ip" | "redir-host";
|
||||
"fake-ip-range"?: string;
|
||||
"fake-ip-filter"?: string[];
|
||||
"fake-ip-filter-mode"?: "blacklist" | "whitelist";
|
||||
"prefer-h3"?: boolean;
|
||||
"respect-rules"?: boolean;
|
||||
nameserver?: string[];
|
||||
fallback?: string[];
|
||||
"default-nameserver"?: string[];
|
||||
"proxy-server-nameserver"?: string[];
|
||||
"direct-nameserver"?: string[];
|
||||
"direct-nameserver-follow-policy"?: boolean;
|
||||
"nameserver-policy"?: Record<string, any>;
|
||||
"use-hosts"?: boolean;
|
||||
"use-system-hosts"?: boolean;
|
||||
"fallback-filter"?: {
|
||||
geoip?: boolean;
|
||||
"geoip-code"?: string;
|
||||
ipcidr?: string[];
|
||||
domain?: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface IRuleItem {
|
||||
|
Loading…
x
Reference in New Issue
Block a user