support interfaces info

This commit is contained in:
pompurin404 2024-08-19 10:10:22 +08:00
parent 7b4d60e280
commit 4aeac0ebb3
No known key found for this signature in database
7 changed files with 340 additions and 237 deletions

View File

@ -1,3 +1,4 @@
### New Features ### New Features
- 添加覆写脚本执行日志功能 - 添加覆写脚本执行日志功能
- 支持查看局域网网络信息

View File

@ -0,0 +1,5 @@
import os from 'os'
export function getInterfaces(): NodeJS.Dict<NetworkInterfaceInfo[]> {
return os.networkInterfaces()
}

View File

@ -51,6 +51,7 @@ import { getFilePath, openUWPTool, readTextFile, setupFirewall } from '../sys/mi
import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory' import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory'
import { isPortable, setPortable } from './dirs' import { isPortable, setPortable } from './dirs'
import { listWebdavBackups, webdavBackup, webdavRestore } from '../resolve/backup' import { listWebdavBackups, webdavBackup, webdavRestore } from '../resolve/backup'
import { getInterfaces } from '../sys/interface'
function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -139,6 +140,7 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('platform', () => process.platform) ipcMain.handle('platform', () => process.platform)
ipcMain.handle('openUWPTool', ipcErrorWrapper(openUWPTool)) ipcMain.handle('openUWPTool', ipcErrorWrapper(openUWPTool))
ipcMain.handle('setupFirewall', ipcErrorWrapper(setupFirewall)) ipcMain.handle('setupFirewall', ipcErrorWrapper(setupFirewall))
ipcMain.handle('getInterfaces', getInterfaces)
ipcMain.handle('setPortable', (_e, portable) => ipcErrorWrapper(setPortable)(portable)) ipcMain.handle('setPortable', (_e, portable) => ipcErrorWrapper(setPortable)(portable))
ipcMain.handle('isPortable', isPortable) ipcMain.handle('isPortable', isPortable)
ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup)) ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup))

View File

@ -0,0 +1,67 @@
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
Snippet
} from '@nextui-org/react'
import React, { useEffect, useState } from 'react'
import { getInterfaces } from '@renderer/utils/ipc'
interface Props {
onClose: () => void
}
const InterfaceModal: React.FC<Props> = (props) => {
const { onClose } = props
const [info, setInfo] = useState<Record<string, NetworkInterfaceInfo[]>>({})
const getInfo = async (): Promise<void> => {
setInfo(await getInterfaces())
}
useEffect(() => {
getInfo()
}, [])
return (
<Modal
backdrop="blur"
hideCloseButton
isOpen={true}
onOpenChange={onClose}
scrollBehavior="inside"
>
<ModalContent>
<ModalHeader className="flex"></ModalHeader>
<ModalBody>
{Object.entries(info).map(([key, value]) => {
return (
<div key={key}>
<h4 className="font-bold">{key}</h4>
{value.map((v) => {
return (
<div key={v.address}>
<div className="mt-2 flex justify-between">
{v.family}
<Snippet symbol="" size="sm">
{v.address}
</Snippet>
</div>
</div>
)
})}
</div>
)
})}
</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onClose}>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}
export default InterfaceModal

View File

@ -5,8 +5,10 @@ import SettingItem from '@renderer/components/base/base-setting-item'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { platform } from '@renderer/utils/init' import { platform } from '@renderer/utils/init'
import { FaNetworkWired } from 'react-icons/fa'
import { restartCore } from '@renderer/utils/ipc' import { restartCore } from '@renderer/utils/ipc'
import React, { useState } from 'react' import React, { useState } from 'react'
import InterfaceModal from '@renderer/components/mihomo/interface-modal'
const CoreMap = { const CoreMap = {
mihomo: '稳定版', mihomo: '稳定版',
@ -43,128 +45,47 @@ const Mihomo: React.FC = () => {
const [externalControllerInput, setExternalControllerInput] = useState(externalController) const [externalControllerInput, setExternalControllerInput] = useState(externalController)
const [secretInput, setSecretInput] = useState(secret) const [secretInput, setSecretInput] = useState(secret)
const [lanOpen, setLanOpen] = useState(false)
const onChangeNeedRestart = async (patch: Partial<IMihomoConfig>): Promise<void> => { const onChangeNeedRestart = async (patch: Partial<IMihomoConfig>): Promise<void> => {
await patchControledMihomoConfig(patch) await patchControledMihomoConfig(patch)
await restartCore() await restartCore()
} }
return ( return (
<BasePage title="内核设置"> <>
<SettingCard> {lanOpen && <InterfaceModal onClose={() => setLanOpen(false)} />}
<SettingItem title="内核版本" divider> <BasePage title="内核设置">
<Select <SettingCard>
className="w-[100px]" <SettingItem title="内核版本" divider>
size="sm" <Select
selectedKeys={new Set([core])}
onSelectionChange={async (v) => {
try {
await patchAppConfig({ core: v.currentKey as 'mihomo' | 'mihomo-alpha' })
await restartCore()
} catch (e) {
alert(e)
} finally {
PubSub.publish('mihomo-core-changed')
}
}}
>
<SelectItem key="mihomo">{CoreMap['mihomo']}</SelectItem>
<SelectItem key="mihomo-alpha">{CoreMap['mihomo-alpha']}</SelectItem>
</Select>
</SettingItem>
<SettingItem title="混合端口" divider>
<div className="flex">
{mixedPortInput !== mixedPort && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ 'mixed-port': mixedPortInput })
}}
>
</Button>
)}
<Input
size="sm"
type="number"
className="w-[100px]" className="w-[100px]"
value={mixedPortInput.toString()}
max={65535}
min={0}
onValueChange={(v) => {
setMixedPortInput(parseInt(v))
}}
/>
</div>
</SettingItem>
<SettingItem title="Socks端口" divider>
<div className="flex">
{socksPortInput !== socksPort && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ 'socks-port': socksPortInput })
}}
>
</Button>
)}
<Input
size="sm" size="sm"
type="number" selectedKeys={new Set([core])}
className="w-[100px]" onSelectionChange={async (v) => {
value={socksPortInput.toString()} try {
max={65535} await patchAppConfig({ core: v.currentKey as 'mihomo' | 'mihomo-alpha' })
min={0} await restartCore()
onValueChange={(v) => { } catch (e) {
setSocksPortInput(parseInt(v)) alert(e)
} finally {
PubSub.publish('mihomo-core-changed')
}
}} }}
/> >
</div> <SelectItem key="mihomo">{CoreMap['mihomo']}</SelectItem>
</SettingItem> <SelectItem key="mihomo-alpha">{CoreMap['mihomo-alpha']}</SelectItem>
<SettingItem title="Http端口" divider> </Select>
<div className="flex"> </SettingItem>
{httpPortInput !== httpPort && ( <SettingItem title="混合端口" divider>
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ port: httpPortInput })
}}
>
</Button>
)}
<Input
size="sm"
type="number"
className="w-[100px]"
value={httpPortInput.toString()}
max={65535}
min={0}
onValueChange={(v) => {
setHttpPortInput(parseInt(v))
}}
/>
</div>
</SettingItem>
{platform !== 'win32' && (
<SettingItem title="Redir端口" divider>
<div className="flex"> <div className="flex">
{redirPortInput !== redirPort && ( {mixedPortInput !== mixedPort && (
<Button <Button
size="sm" size="sm"
color="primary" color="primary"
className="mr-2" className="mr-2"
onPress={() => { onPress={() => {
onChangeNeedRestart({ 'redir-port': redirPortInput }) onChangeNeedRestart({ 'mixed-port': mixedPortInput })
}} }}
> >
@ -175,26 +96,24 @@ const Mihomo: React.FC = () => {
size="sm" size="sm"
type="number" type="number"
className="w-[100px]" className="w-[100px]"
value={redirPortInput.toString()} value={mixedPortInput.toString()}
max={65535} max={65535}
min={0} min={0}
onValueChange={(v) => { onValueChange={(v) => {
setRedirPortInput(parseInt(v)) setMixedPortInput(parseInt(v))
}} }}
/> />
</div> </div>
</SettingItem> </SettingItem>
)} <SettingItem title="Socks端口" divider>
{platform === 'linux' && (
<SettingItem title="TProxy端口" divider>
<div className="flex"> <div className="flex">
{tproxyPortInput !== tproxyPort && ( {socksPortInput !== socksPort && (
<Button <Button
size="sm" size="sm"
color="primary" color="primary"
className="mr-2" className="mr-2"
onPress={() => { onPress={() => {
onChangeNeedRestart({ 'tproxy-port': tproxyPortInput }) onChangeNeedRestart({ 'socks-port': socksPortInput })
}} }}
> >
@ -205,151 +124,255 @@ const Mihomo: React.FC = () => {
size="sm" size="sm"
type="number" type="number"
className="w-[100px]" className="w-[100px]"
value={tproxyPortInput.toString()} value={socksPortInput.toString()}
max={65535} max={65535}
min={0} min={0}
onValueChange={(v) => { onValueChange={(v) => {
setTproxyPortInput(parseInt(v)) setSocksPortInput(parseInt(v))
}} }}
/> />
</div> </div>
</SettingItem> </SettingItem>
)} <SettingItem title="Http端口" divider>
<SettingItem title="外部控制" divider> <div className="flex">
<div className="flex"> {httpPortInput !== httpPort && (
{externalControllerInput !== externalController && ( <Button
<Button size="sm"
size="sm" color="primary"
color="primary" className="mr-2"
className="mr-2" onPress={() => {
onPress={() => { onChangeNeedRestart({ port: httpPortInput })
onChangeNeedRestart({ 'external-controller': externalControllerInput }) }}
}} >
>
</Button>
</Button> )}
)}
<Input <Input
size="sm"
type="number"
className="w-[100px]"
value={httpPortInput.toString()}
max={65535}
min={0}
onValueChange={(v) => {
setHttpPortInput(parseInt(v))
}}
/>
</div>
</SettingItem>
{platform !== 'win32' && (
<SettingItem title="Redir端口" divider>
<div className="flex">
{redirPortInput !== redirPort && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ 'redir-port': redirPortInput })
}}
>
</Button>
)}
<Input
size="sm"
type="number"
className="w-[100px]"
value={redirPortInput.toString()}
max={65535}
min={0}
onValueChange={(v) => {
setRedirPortInput(parseInt(v))
}}
/>
</div>
</SettingItem>
)}
{platform === 'linux' && (
<SettingItem title="TProxy端口" divider>
<div className="flex">
{tproxyPortInput !== tproxyPort && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ 'tproxy-port': tproxyPortInput })
}}
>
</Button>
)}
<Input
size="sm"
type="number"
className="w-[100px]"
value={tproxyPortInput.toString()}
max={65535}
min={0}
onValueChange={(v) => {
setTproxyPortInput(parseInt(v))
}}
/>
</div>
</SettingItem>
)}
<SettingItem title="外部控制" divider>
<div className="flex">
{externalControllerInput !== externalController && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ 'external-controller': externalControllerInput })
}}
>
</Button>
)}
<Input
size="sm"
value={externalControllerInput}
onValueChange={(v) => {
setExternalControllerInput(v)
}}
/>
</div>
</SettingItem>
<SettingItem title="外部控制访问密钥" divider>
<div className="flex">
{secretInput !== secret && (
<Button
size="sm"
color="primary"
className="mr-2"
onPress={() => {
onChangeNeedRestart({ secret: secretInput })
}}
>
</Button>
)}
<Input
size="sm"
type="password"
value={secretInput}
onValueChange={(v) => {
setSecretInput(v)
}}
/>
</div>
</SettingItem>
<SettingItem title="IPv6" divider>
<Switch
size="sm" size="sm"
value={externalControllerInput} isSelected={ipv6}
onValueChange={(v) => { onValueChange={(v) => {
setExternalControllerInput(v) onChangeNeedRestart({ ipv6: v })
}} }}
/> />
</div> </SettingItem>
</SettingItem> <SettingItem
<SettingItem title="外部控制访问密钥" divider> title="允许局域网连接"
<div className="flex"> actions={
{secretInput !== secret && (
<Button <Button
size="sm" size="sm"
color="primary" isIconOnly
className="mr-2" variant="light"
className="ml-2"
onPress={() => { onPress={() => {
onChangeNeedRestart({ secret: secretInput }) setLanOpen(true)
}} }}
> >
<FaNetworkWired className="text-lg" />
</Button> </Button>
)} }
divider
<Input >
<Switch
size="sm" size="sm"
type="password" isSelected={allowLan}
value={secretInput}
onValueChange={(v) => { onValueChange={(v) => {
setSecretInput(v) onChangeNeedRestart({ 'allow-lan': v })
}} }}
/> />
</div> </SettingItem>
</SettingItem> <SettingItem title="使用RTT延迟测试" divider>
<SettingItem title="IPv6" divider> <Switch
<Switch size="sm"
size="sm" isSelected={unifiedDelay}
isSelected={ipv6} onValueChange={(v) => {
onValueChange={(v) => { onChangeNeedRestart({ 'unified-delay': v })
onChangeNeedRestart({ ipv6: v }) }}
}} />
/> </SettingItem>
</SettingItem> <SettingItem title="TCP并发" divider>
<SettingItem title="允许局域网连接" divider> <Switch
<Switch size="sm"
size="sm" isSelected={tcpConcurrent}
isSelected={allowLan} onValueChange={(v) => {
onValueChange={(v) => { onChangeNeedRestart({ 'tcp-concurrent': v })
onChangeNeedRestart({ 'allow-lan': v }) }}
}} />
/> </SettingItem>
</SettingItem> <SettingItem title="存储选择节点" divider>
<SettingItem title="使用RTT延迟测试" divider> <Switch
<Switch size="sm"
size="sm" isSelected={storeSelected}
isSelected={unifiedDelay} onValueChange={(v) => {
onValueChange={(v) => { onChangeNeedRestart({ profile: { 'store-selected': v } })
onChangeNeedRestart({ 'unified-delay': v }) }}
}} />
/> </SettingItem>
</SettingItem> <SettingItem title="存储FakeIP" divider>
<SettingItem title="TCP并发" divider> <Switch
<Switch size="sm"
size="sm" isSelected={storeFakeIp}
isSelected={tcpConcurrent} onValueChange={(v) => {
onValueChange={(v) => { onChangeNeedRestart({ profile: { 'store-fake-ip': v } })
onChangeNeedRestart({ 'tcp-concurrent': v }) }}
}} />
/> </SettingItem>
</SettingItem> <SettingItem title="日志等级" divider>
<SettingItem title="存储选择节点" divider> <Select
<Switch className="w-[100px]"
size="sm" size="sm"
isSelected={storeSelected} selectedKeys={new Set([logLevel])}
onValueChange={(v) => { onSelectionChange={(v) => {
onChangeNeedRestart({ profile: { 'store-selected': v } }) onChangeNeedRestart({ 'log-level': v.currentKey as LogLevel })
}} }}
/> >
</SettingItem> <SelectItem key="silent"></SelectItem>
<SettingItem title="存储FakeIP" divider> <SelectItem key="error"></SelectItem>
<Switch <SelectItem key="warning"></SelectItem>
size="sm" <SelectItem key="info"></SelectItem>
isSelected={storeFakeIp} <SelectItem key="debug"></SelectItem>
onValueChange={(v) => { </Select>
onChangeNeedRestart({ profile: { 'store-fake-ip': v } }) </SettingItem>
}} <SettingItem title="查找进程">
/> <Select
</SettingItem> className="w-[100px]"
<SettingItem title="日志等级" divider> size="sm"
<Select selectedKeys={new Set([findProcessMode])}
className="w-[100px]" onSelectionChange={(v) => {
size="sm" onChangeNeedRestart({ 'find-process-mode': v.currentKey as FindProcessMode })
selectedKeys={new Set([logLevel])} }}
onSelectionChange={(v) => { >
onChangeNeedRestart({ 'log-level': v.currentKey as LogLevel }) <SelectItem key="strict"></SelectItem>
}} <SelectItem key="off"></SelectItem>
> <SelectItem key="always"></SelectItem>
<SelectItem key="silent"></SelectItem> </Select>
<SelectItem key="error"></SelectItem> </SettingItem>
<SelectItem key="warning"></SelectItem> </SettingCard>
<SelectItem key="info"></SelectItem> </BasePage>
<SelectItem key="debug"></SelectItem> </>
</Select>
</SettingItem>
<SettingItem title="查找进程">
<Select
className="w-[100px]"
size="sm"
selectedKeys={new Set([findProcessMode])}
onSelectionChange={(v) => {
onChangeNeedRestart({ 'find-process-mode': v.currentKey as FindProcessMode })
}}
>
<SelectItem key="strict"></SelectItem>
<SelectItem key="off"></SelectItem>
<SelectItem key="always"></SelectItem>
</Select>
</SettingItem>
</SettingCard>
</BasePage>
) )
} }

View File

@ -251,6 +251,10 @@ export async function setupFirewall(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setupFirewall')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setupFirewall'))
} }
export async function getInterfaces(): Promise<Record<string, NetworkInterfaceInfo[]>> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getInterfaces'))
}
export async function setPortable(portable: boolean): Promise<void> { export async function setPortable(portable: boolean): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setPortable', portable)) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setPortable', portable))
} }

View File

@ -36,6 +36,7 @@ type MihomoProxyType =
type TunStack = 'gvisor' | 'mixed' | 'system' type TunStack = 'gvisor' | 'mixed' | 'system'
type FindProcessMode = 'off' | 'strict' | 'always' type FindProcessMode = 'off' | 'strict' | 'always'
type DnsMode = 'normal' | 'fake-ip' | 'redir-host' type DnsMode = 'normal' | 'fake-ip' | 'redir-host'
type NetworkInterfaceInfo = os.NetworkInterfaceInfo
interface IAppVersion { interface IAppVersion {
version: string version: string