complete DNS (#6)

Co-authored-by: pompurin404 <pompurin404@mihomo.party>
This commit is contained in:
汐殇 2024-08-09 14:58:29 +08:00 committed by GitHub
parent 7b7a58212b
commit b80dbfbcfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 179 additions and 92 deletions

View File

@ -21,7 +21,8 @@ export function setControledMihomoConfig(patch: Partial<IMihomoConfig>): void {
} }
if (patch.dns) { if (patch.dns) {
const oldDns = controledMihomoConfig.dns || {} const oldDns = controledMihomoConfig.dns || {}
const newDns = Object.assign(oldDns, patch.dns) const newDns = { ...patch.dns }
newDns.enable = oldDns.enable
patch.dns = newDns patch.dns = newDns
} }
if (patch.sniffer) { if (patch.sniffer) {

View File

@ -4,11 +4,14 @@ import { MdDeleteForever } from 'react-icons/md'
import SettingCard from '@renderer/components/base/base-setting-card' import SettingCard from '@renderer/components/base/base-setting-card'
import SettingItem from '@renderer/components/base/base-setting-item' import SettingItem from '@renderer/components/base/base-setting-item'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import { restartCore } from '@renderer/utils/ipc' import { restartCore } from '@renderer/utils/ipc'
import React, { Key, useState } from 'react' import React, { Key, ReactNode, useState } from 'react'
const DNS: React.FC = () => { const DNS: React.FC = () => {
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { appConfig, patchAppConfig } = useAppConfig()
const { nameserverPolicy, useNameserverPolicy } = appConfig || {}
const { dns, hosts } = controledMihomoConfig || {} const { dns, hosts } = controledMihomoConfig || {}
const { const {
ipv6 = false, ipv6 = false,
@ -24,7 +27,9 @@ const DNS: React.FC = () => {
'enhanced-mode': enhancedMode = 'fake-ip', 'enhanced-mode': enhancedMode = 'fake-ip',
'use-hosts': useHosts = false, 'use-hosts': useHosts = false,
'use-system-hosts': useSystemHosts = false, 'use-system-hosts': useSystemHosts = false,
nameserver = ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'] 'respect-rules': respectRules = false,
nameserver = ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
'proxy-server-nameserver': proxyServerNameserver = []
} = dns || {} } = dns || {}
const [values, setValues] = useState({ const [values, setValues] = useState({
@ -34,45 +39,76 @@ const DNS: React.FC = () => {
fakeIPRange, fakeIPRange,
fakeIPFilter, fakeIPFilter,
useSystemHosts, useSystemHosts,
respectRules,
nameserver, nameserver,
proxyServerNameserver,
useNameserverPolicy,
nameserverPolicy: Object.entries(nameserverPolicy || {}).map(([domain, value]) => ({
domain,
value
})),
hosts: Object.entries(hosts || {}).map(([domain, value]) => ({ domain, value })) hosts: Object.entries(hosts || {}).map(([domain, value]) => ({ domain, value }))
}) })
const handleListChange = (type: string, value: string, index: number): void => { const handleListChange = (type: string, value: string, index: number): void => {
const newValues = [...values[type]] const list = [...values[type]]
if (index === newValues.length) { if (value.trim()) {
if (value.trim() !== '') { if (index < list.length) {
newValues.push(value) list[index] = value
} else if (list.length < 4) {
list.push(value)
} }
} else { } else {
if (value.trim() === '') { list.splice(index, 1)
newValues.splice(index, 1)
} else {
newValues[index] = value
} }
setValues({ ...values, [type]: list.slice(0, 4) })
} }
setValues({ ...values, [type]: newValues })
}
const handleHostsChange = (domain: string, value: string, index: number): void => {
const processValue = (val: string): string | string[] =>
val.includes(',') ? val.split(',').map((s) => s.trim()) : val.trim()
const isEmpty = (d: string, v: string | string[]): boolean =>
d === '' && (Array.isArray(v) ? v.every((item) => item === '') : v === '')
const newHosts = [...values.hosts] const renderListInputs = (type: string, placeholder: string): ReactNode => {
if (!isEmpty(domain.trim(), processValue(value))) { const currentItems = values[type].slice(0, 4)
if (index === newHosts.length) { const showNewLine = currentItems.length < 4 && currentItems.every((item) => item.trim() !== '')
newHosts.push({ domain: domain.trim(), value: processValue(value) })
} else { return [...currentItems, ...(showNewLine ? [''] : [])].slice(0, 4).map((item, index) => (
newHosts[index] = { domain: domain.trim(), value: processValue(value) } <div key={index} className="mt-2 flex">
<Input
fullWidth
size="sm"
placeholder={placeholder}
value={typeof item === 'string' ? item : item.domain}
onValueChange={(v) => handleListChange(type, v, index)}
/>
{index < values[type].length && (
<Button
className="ml-2"
size="sm"
variant="flat"
color="warning"
onClick={() => handleListChange(type, '', index)}
>
<MdDeleteForever className="text-lg" />
</Button>
)}
</div>
))
} }
} else if (index < newHosts.length) {
newHosts.splice(index, 1) const handleSubkeyChange = (type: string, domain: string, value: string, index: number): void => {
} const list = [...values[type]]
setValues({ ...values, hosts: newHosts }) const processedValue = value.includes(',')
? value.split(',').map((s: string) => s.trim())
: value.trim()
if (domain || processedValue) list[index] = { domain: domain.trim(), value: processedValue }
else list.splice(index, 1)
setValues({ ...values, [type]: list })
} }
const onSave = async (patch: Partial<IMihomoConfig>): Promise<void> => { const onSave = async (patch: Partial<IMihomoConfig>): Promise<void> => {
await patchAppConfig({
nameserverPolicy: Object.fromEntries(
values.nameserverPolicy.map(({ domain, value }) => [domain, value])
),
useNameserverPolicy: values.useNameserverPolicy
})
await patchControledMihomoConfig(patch) await patchControledMihomoConfig(patch)
await restartCore() await restartCore()
} }
@ -85,22 +121,29 @@ const DNS: React.FC = () => {
size="sm" size="sm"
color="primary" color="primary"
onPress={() => { onPress={() => {
const hostsObject = values.hosts.reduce((acc, { domain, value }) => { const hostsObject = Object.fromEntries(
if (domain) { values.hosts.map(({ domain, value }) => [domain, value])
acc[domain] = value )
} const dnsConfig = {
return acc
}, {})
onSave({
dns: {
ipv6: values.ipv6, ipv6: values.ipv6,
'fake-ip-range': values.fakeIPRange, 'fake-ip-range': values.fakeIPRange,
'fake-ip-filter': values.fakeIPFilter, 'fake-ip-filter': values.fakeIPFilter,
'enhanced-mode': values.enhancedMode, 'enhanced-mode': values.enhancedMode,
'use-hosts': values.useHosts, 'use-hosts': values.useHosts,
'use-system-hosts': values.useSystemHosts, 'use-system-hosts': values.useSystemHosts,
nameserver: values.nameserver 'respect-rules': values.respectRules,
}, nameserver: values.nameserver,
'proxy-server-nameserver': values.proxyServerNameserver,
fallback: [],
'fallback-filter': {}
}
if (values.useNameserverPolicy) {
dnsConfig['nameserver-policy'] = Object.fromEntries(
values.nameserverPolicy.map(({ domain, value }) => [domain, value])
)
}
onSave({
dns: dnsConfig,
hosts: hostsObject hosts: hostsObject
}) })
}} }}
@ -136,28 +179,7 @@ const DNS: React.FC = () => {
</SettingItem> </SettingItem>
<div className="flex flex-col items-stretch"> <div className="flex flex-col items-stretch">
<h3 className="select-none">IP回应</h3> <h3 className="select-none">IP回应</h3>
{[...values.fakeIPFilter, ''].map((ns, index) => ( {renderListInputs('fakeIPFilter', '例: +.lan')}
<div key={index} className="mt-2 flex">
<Input
fullWidth
size="sm"
placeholder="例: +.lan"
value={ns}
onValueChange={(v) => handleListChange('fakeIPFilter', v, index)}
/>
{index < values.fakeIPFilter.length && (
<Button
className="ml-2"
size="sm"
variant="flat"
color="warning"
onClick={() => handleListChange('fakeIPFilter', '', index)}
>
<MdDeleteForever className="text-lg" />
</Button>
)}
</div>
))}
</div> </div>
<Divider className="my-2" /> <Divider className="my-2" />
</> </>
@ -171,32 +193,87 @@ const DNS: React.FC = () => {
}} }}
/> />
</SettingItem> </SettingItem>
<SettingItem title="连接遵守规则" divider>
<Switch
size="sm"
isSelected={values.respectRules}
onValueChange={(v) => {
setValues({ ...values, respectRules: v })
}}
/>
</SettingItem>
<div className="flex flex-col items-stretch">
<h3 className="select-none"></h3>
{renderListInputs('proxyServerNameserver', '例: tls://223.5.5.5')}
</div>
<Divider className="my-2" />
<div className="flex flex-col items-stretch"> <div className="flex flex-col items-stretch">
<h3 className="select-none">DNS服务器</h3> <h3 className="select-none">DNS服务器</h3>
{[...values.nameserver, ''].map((ns, index) => ( {renderListInputs('nameserver', '例: tls://223.5.5.5')}
<div key={index} className="mt-2 flex"> </div>
<Input <Divider className="my-2" />
fullWidth <SettingItem title="覆盖DNS策略" divider>
<Switch
size="sm" size="sm"
placeholder="例: tls://223.5.5.5" isSelected={values.useNameserverPolicy}
value={ns} onValueChange={(v) => {
onValueChange={(v) => handleListChange('nameserver', v, index)} setValues({ ...values, useNameserverPolicy: v })
}}
/> />
{index < values.nameserver.length && ( </SettingItem>
<Button {values.useNameserverPolicy && (
className="ml-2" <div className="flex flex-col items-stretch">
<div className="flex flex-col items-stretch">
<h3 className="mb-2"></h3>
{[...values.nameserverPolicy, { domain: '', value: '' }].map(
({ domain, value }, index) => (
<div key={index} className="flex mb-2">
<div className="flex-[4]">
<Input
size="sm"
fullWidth
placeholder="域名"
value={domain}
onValueChange={(v) =>
handleSubkeyChange(
'nameserverPolicy',
v,
Array.isArray(value) ? value.join(',') : value,
index
)
}
/>
</div>
<span className="select-none mx-2">:</span>
<div className="flex-[6] flex">
<Input
size="sm"
fullWidth
placeholder="DNS服务器"
value={Array.isArray(value) ? value.join(',') : value}
onValueChange={(v) =>
handleSubkeyChange('nameserverPolicy', domain, v, index)
}
/>
{index < values.nameserverPolicy.length && (
<Button
size="sm" size="sm"
variant="flat"
color="warning" color="warning"
onClick={() => handleListChange('nameserver', '', index)} variant="flat"
className="ml-2"
onClick={() => handleSubkeyChange('nameserverPolicy', '', '', index)}
> >
<MdDeleteForever className="text-lg" /> <MdDeleteForever className="text-lg" />
</Button> </Button>
)} )}
</div> </div>
))}
</div> </div>
<Divider className="my-2" /> )
)}
</div>
</div>
)}
<SettingItem title="使用系统hosts" divider> <SettingItem title="使用系统hosts" divider>
<Switch <Switch
size="sm" size="sm"
@ -227,7 +304,12 @@ const DNS: React.FC = () => {
placeholder="域名" placeholder="域名"
value={domain} value={domain}
onValueChange={(v) => onValueChange={(v) =>
handleHostsChange(v, Array.isArray(value) ? value.join(',') : value, index) handleSubkeyChange(
'hosts',
v,
Array.isArray(value) ? value.join(',') : value,
index
)
} }
/> />
</div> </div>
@ -238,7 +320,7 @@ const DNS: React.FC = () => {
fullWidth fullWidth
placeholder="域名或IP" placeholder="域名或IP"
value={Array.isArray(value) ? value.join(',') : value} value={Array.isArray(value) ? value.join(',') : value}
onValueChange={(v) => handleHostsChange(domain, v, index)} onValueChange={(v) => handleSubkeyChange('hosts', domain, v, index)}
/> />
{index < values.hosts.length && ( {index < values.hosts.length && (
<Button <Button
@ -246,7 +328,7 @@ const DNS: React.FC = () => {
color="warning" color="warning"
variant="flat" variant="flat"
className="ml-2" className="ml-2"
onClick={() => handleHostsChange('', '', index)} onClick={() => handleSubkeyChange('hosts', '', '', index)}
> >
<MdDeleteForever className="text-lg" /> <MdDeleteForever className="text-lg" />
</Button> </Button>

View File

@ -198,6 +198,8 @@ interface IAppConfig {
delayTestUrl?: string delayTestUrl?: string
delayTestTimeout?: number delayTestTimeout?: number
encryptedPassword?: Buffer encryptedPassword?: Buffer
useNameserverPolicy: boolean
nameserverPolicy: { [key: string]: string | string[] }
} }
interface IMihomoTunConfig { interface IMihomoTunConfig {
@ -240,6 +242,8 @@ interface IMihomoDNSConfig {
'use-system-hosts'?: boolean 'use-system-hosts'?: boolean
'respect-rules'?: boolean 'respect-rules'?: boolean
nameserver?: string[] nameserver?: string[]
fallback?: string[]
'fallback-filter'?: { [key: string]: boolean | string | string[] }
'proxy-server-nameserver'?: string[] 'proxy-server-nameserver'?: string[]
'nameserver-policy'?: { [key: string]: string | string[] } 'nameserver-policy'?: { [key: string]: string | string[] }
} }