mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 11:42:19 +08:00
proxy delay
This commit is contained in:
parent
ba9c9ba84d
commit
0fc43b339d
|
@ -27,6 +27,7 @@
|
|||
"electron-updater": "^6.2.1",
|
||||
"framer-motion": "^11.3.19",
|
||||
"next-themes": "^0.3.0",
|
||||
"pubsub-js": "^1.9.4",
|
||||
"react-icons": "^5.2.1",
|
||||
"react-monaco-editor": "^0.55.0",
|
||||
"react-router-dom": "^6.25.1",
|
||||
|
@ -40,6 +41,7 @@
|
|||
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/pubsub-js": "^1.8.6",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
|
|
|
@ -32,6 +32,9 @@ importers:
|
|||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
pubsub-js:
|
||||
specifier: ^1.9.4
|
||||
version: 1.9.4
|
||||
react-icons:
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1(react@18.3.1)
|
||||
|
@ -66,6 +69,9 @@ importers:
|
|||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.0.0
|
||||
'@types/pubsub-js':
|
||||
specifier: ^1.8.6
|
||||
version: 1.8.6
|
||||
'@types/react':
|
||||
specifier: ^18.3.3
|
||||
version: 18.3.3
|
||||
|
@ -1795,6 +1801,9 @@ packages:
|
|||
'@types/prop-types@15.7.12':
|
||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||
|
||||
'@types/pubsub-js@1.8.6':
|
||||
resolution: {integrity: sha512-Kwug5cwV0paUDm/NfwDx1sp9xI0bGIvmWJjJWCU8NngkCCMt3EIC7oPDvb6fV7BR8kPpFyyBu4D11bda/2MdPA==}
|
||||
|
||||
'@types/react-dom@18.3.0':
|
||||
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
||||
|
||||
|
@ -3585,6 +3594,9 @@ packages:
|
|||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pubsub-js@1.9.4:
|
||||
resolution: {integrity: sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==}
|
||||
|
||||
pump@3.0.0:
|
||||
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
|
||||
|
||||
|
@ -6848,6 +6860,8 @@ snapshots:
|
|||
|
||||
'@types/prop-types@15.7.12': {}
|
||||
|
||||
'@types/pubsub-js@1.8.6': {}
|
||||
|
||||
'@types/react-dom@18.3.0':
|
||||
dependencies:
|
||||
'@types/react': 18.3.3
|
||||
|
@ -8929,6 +8943,8 @@ snapshots:
|
|||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pubsub-js@1.9.4: {}
|
||||
|
||||
pump@3.0.0:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.4
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import axios, { AxiosInstance } from 'axios'
|
||||
import { getControledMihomoConfig } from '../config'
|
||||
import { getAppConfig, getControledMihomoConfig } from '../config'
|
||||
import WebSocket from 'ws'
|
||||
import { window } from '..'
|
||||
|
||||
|
@ -59,6 +59,19 @@ export const mihomoChangeProxy = async (group: string, proxy: string): Promise<I
|
|||
return instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy })
|
||||
}
|
||||
|
||||
export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
|
||||
const appConfig = getAppConfig()
|
||||
const { delayTestUrl, delayTestTimeout } = appConfig
|
||||
const instance = await getAxios()
|
||||
return instance.get(`/proxies/${encodeURIComponent(proxy)}/delay`, {
|
||||
params: {
|
||||
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
|
||||
timeout: delayTestTimeout || 5000
|
||||
},
|
||||
timeout: delayTestTimeout || 5000
|
||||
})
|
||||
}
|
||||
|
||||
export const startMihomoTraffic = (): void => {
|
||||
mihomoTraffic()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
mihomoConfig,
|
||||
mihomoConnections,
|
||||
mihomoProxies,
|
||||
mihomoProxyDelay,
|
||||
mihomoRules,
|
||||
mihomoVersion,
|
||||
patchMihomoConfig,
|
||||
|
@ -33,6 +34,7 @@ export function registerIpcMainHandlers(): void {
|
|||
ipcMain.handle('mihomoRules', mihomoRules)
|
||||
ipcMain.handle('mihomoProxies', () => mihomoProxies())
|
||||
ipcMain.handle('mihomoChangeProxy', (_e, group, proxy) => mihomoChangeProxy(group, proxy))
|
||||
ipcMain.handle('mihomoProxyDelay', (_e, proxy, url) => mihomoProxyDelay(proxy, url))
|
||||
ipcMain.handle('startMihomoLogs', startMihomoLogs)
|
||||
ipcMain.handle('stopMihomoLogs', () => stopMihomoLogs())
|
||||
ipcMain.handle('patchMihomoConfig', async (_e, patch) => await patchMihomoConfig(patch))
|
||||
|
|
|
@ -1,14 +1,65 @@
|
|||
import { Card, CardBody, Divider } from '@nextui-org/react'
|
||||
import React from 'react'
|
||||
import { Button, Card, CardBody, Divider } from '@nextui-org/react'
|
||||
import { useAppConfig } from '@renderer/hooks/use-config'
|
||||
import PubSub from 'pubsub-js'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
interface Props {
|
||||
onProxyDelay: (proxy: string) => Promise<IMihomoDelay>
|
||||
proxyDisplayMode: 'simple' | 'full'
|
||||
proxy: IMihomoProxy | IMihomoGroup
|
||||
group: string
|
||||
onSelect: (proxy: string) => void
|
||||
selected: boolean
|
||||
}
|
||||
|
||||
const ProxyItem: React.FC<Props> = (props) => {
|
||||
const { proxy, selected, onSelect } = props
|
||||
const { proxyDisplayMode, group, proxy, selected, onSelect, onProxyDelay } = props
|
||||
const { appConfig } = useAppConfig()
|
||||
const { delayTestTimeout = 5000 } = appConfig || {}
|
||||
const [delay, setDelay] = useState(() => {
|
||||
if (proxy.history.length > 0) {
|
||||
return proxy.history[0].delay
|
||||
}
|
||||
return 0
|
||||
})
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger' {
|
||||
if (delay < 0) return 'danger'
|
||||
if (delay === 0) return 'primary'
|
||||
if (delay < 500) return 'success'
|
||||
if (delay < delayTestTimeout) return 'warning'
|
||||
return 'danger'
|
||||
}
|
||||
|
||||
function delayText(delay: number): string {
|
||||
if (delay < 0) return 'Error'
|
||||
if (delay === 0) return 'Delay'
|
||||
if (delay < delayTestTimeout) return delay.toString()
|
||||
return 'Timeout'
|
||||
}
|
||||
|
||||
const onDelay = (): void => {
|
||||
setLoading(true)
|
||||
onProxyDelay(proxy.name).then(
|
||||
(delay) => {
|
||||
setDelay(delay.delay || delayTestTimeout + 1)
|
||||
setLoading(false)
|
||||
},
|
||||
() => {
|
||||
setDelay(-1)
|
||||
setLoading(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const token = PubSub.subscribe(`${group}-delay`, onDelay)
|
||||
|
||||
return (): void => {
|
||||
PubSub.unsubscribe(token)
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
|
@ -16,13 +67,26 @@ const ProxyItem: React.FC<Props> = (props) => {
|
|||
onPress={() => onSelect(proxy.name)}
|
||||
isPressable
|
||||
fullWidth
|
||||
className={`my-1 ${selected ? 'bg-primary' : ''}`}
|
||||
className={`my-1 ${selected ? 'bg-primary/30' : ''}`}
|
||||
radius="sm"
|
||||
>
|
||||
<CardBody className="p-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>{proxy.name}</div>
|
||||
<div className="mx-2 text-sm">{proxy.history.length > 0 && proxy.history[0].delay}</div>
|
||||
<div>
|
||||
<div className="inline">{proxy.name}</div>
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<div className="inline ml-2 text-default-500">{proxy.type}</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
isLoading={loading}
|
||||
color={delayColor(delay)}
|
||||
onPress={onDelay}
|
||||
variant="light"
|
||||
className="h-full min-w-[50px] p-0 mx-2 text-sm hover:bg-content"
|
||||
>
|
||||
{delayText(delay)}
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
|
|
@ -3,13 +3,16 @@ import { Virtuoso } from 'react-virtuoso'
|
|||
import ProxyItem from './proxy-item'
|
||||
|
||||
interface Props {
|
||||
onProxyDelay: (proxy: string) => Promise<IMihomoDelay>
|
||||
onChangeProxy: (proxy: string) => void
|
||||
proxyDisplayMode: 'simple' | 'full'
|
||||
proxies: (IMihomoProxy | IMihomoGroup)[]
|
||||
group: string
|
||||
now: string
|
||||
}
|
||||
|
||||
const ProxyList: React.FC<Props> = (props) => {
|
||||
const { onChangeProxy, proxies, now } = props
|
||||
const { proxyDisplayMode, onProxyDelay, onChangeProxy, proxies, group, now } = props
|
||||
|
||||
return (
|
||||
<Virtuoso
|
||||
|
@ -18,8 +21,11 @@ const ProxyList: React.FC<Props> = (props) => {
|
|||
increaseViewportBy={100}
|
||||
itemContent={(index) => (
|
||||
<ProxyItem
|
||||
onProxyDelay={onProxyDelay}
|
||||
onSelect={onChangeProxy}
|
||||
proxy={proxies[index]}
|
||||
group={group}
|
||||
proxyDisplayMode={proxyDisplayMode}
|
||||
selected={proxies[index].name === now}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react'
|
||||
import { SiSpeedtest } from 'react-icons/si'
|
||||
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react'
|
||||
import { mihomoProxies } from '@renderer/utils/ipc'
|
||||
import { SiNginxproxymanager } from 'react-icons/si'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import useSWR from 'swr'
|
||||
|
||||
const ProxyCard: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/proxies')
|
||||
const { data: proxies = { proxies: {} } } = useSWR('mihomoProxies', mihomoProxies)
|
||||
return (
|
||||
<Card
|
||||
fullWidth
|
||||
|
@ -13,16 +16,38 @@ const ProxyCard: React.FC = () => {
|
|||
isPressable
|
||||
onPress={() => navigate('/proxies')}
|
||||
>
|
||||
<CardBody>
|
||||
<div className="flex justify-between h-[32px]">
|
||||
<h3 className="select-none text-md font-bold leading-[32px]">节点名称</h3>
|
||||
<Button isIconOnly size="sm" variant="light" color="default">
|
||||
<SiSpeedtest color="default" className="text-[20px]" />
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<SiNginxproxymanager color="default" className="text-[20px]" />
|
||||
</Button>
|
||||
<Chip
|
||||
classNames={
|
||||
match
|
||||
? {
|
||||
base: 'border-foreground',
|
||||
content: 'text-foreground'
|
||||
}
|
||||
: {
|
||||
base: 'border-primary',
|
||||
content: 'text-primary'
|
||||
}
|
||||
}
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className="mr-3 mt-2"
|
||||
>
|
||||
{Object.keys(proxies.proxies).length ?? 0}
|
||||
</Chip>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<small>二级节点</small>
|
||||
<h3 className="select-none text-md font-bold">代理组</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import { Accordion, AccordionItem, Avatar } from '@nextui-org/react'
|
||||
import { Accordion, AccordionItem, Avatar, Button } from '@nextui-org/react'
|
||||
import BasePage from '@renderer/components/base/base-page'
|
||||
import ProxyList from '@renderer/components/proxies/proxy-list'
|
||||
import { mihomoChangeProxy, mihomoProxies } from '@renderer/utils/ipc'
|
||||
import { useAppConfig } from '@renderer/hooks/use-config'
|
||||
import { MdOutlineSpeed } from 'react-icons/md'
|
||||
import { mihomoChangeProxy, mihomoProxies, mihomoProxyDelay } from '@renderer/utils/ipc'
|
||||
import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import PubSub from 'pubsub-js'
|
||||
import useSWR from 'swr'
|
||||
|
||||
const Proxies: React.FC = () => {
|
||||
const { data: proxies, mutate } = useSWR('mihomoProxies', mihomoProxies)
|
||||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const { proxyDisplayMode = 'simple' } = appConfig || {}
|
||||
|
||||
const groups = useMemo(() => {
|
||||
const groups: IMihomoGroup[] = []
|
||||
|
@ -44,16 +50,60 @@ const Proxies: React.FC = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const onProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
|
||||
return await mihomoProxyDelay(proxy, url)
|
||||
}
|
||||
|
||||
useEffect(() => {}, [])
|
||||
return (
|
||||
<BasePage title="代理组">
|
||||
<BasePage
|
||||
title="代理组"
|
||||
header={
|
||||
<Button
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
patchAppConfig({ proxyDisplayMode: proxyDisplayMode === 'simple' ? 'full' : 'simple' })
|
||||
}}
|
||||
>
|
||||
{proxyDisplayMode === 'simple' ? (
|
||||
<CgDetailsMore size={20} />
|
||||
) : (
|
||||
<CgDetailsLess size={20} />
|
||||
)}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Accordion variant="splitted" className="p-2">
|
||||
{groups.map((group) => {
|
||||
return (
|
||||
<AccordionItem
|
||||
key={group.name}
|
||||
title={group.name}
|
||||
classNames={{ content: 'p-0' }}
|
||||
title={
|
||||
<div className="flex justify-between">
|
||||
<div>{group.name}</div>
|
||||
<Button
|
||||
variant="light"
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
PubSub.publish(`${group.name}-delay`)
|
||||
}}
|
||||
>
|
||||
<MdOutlineSpeed className="text-lg text-default-500" />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
subtitle={
|
||||
proxyDisplayMode === 'full' && (
|
||||
<div>
|
||||
{group.type}
|
||||
|
||||
{group.now}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
classNames={{ title: 'select-none', base: 'px-2', content: 'pt-2', trigger: 'py-2' }}
|
||||
startContent={
|
||||
group.icon.length > 0 ? (
|
||||
<Avatar className="bg-transparent" size="sm" radius="sm" src={group.icon} />
|
||||
|
@ -61,8 +111,11 @@ const Proxies: React.FC = () => {
|
|||
}
|
||||
>
|
||||
<ProxyList
|
||||
onProxyDelay={(proxy) => onProxyDelay(proxy, group.testUrl)}
|
||||
onChangeProxy={(proxy) => onChangeProxy(group.name, proxy)}
|
||||
proxyDisplayMode={proxyDisplayMode}
|
||||
proxies={groupProxies[group.name]}
|
||||
group={group.name}
|
||||
now={group.now}
|
||||
/>
|
||||
</AccordionItem>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Switch } from '@nextui-org/react'
|
||||
import { Button, Input, Switch } from '@nextui-org/react'
|
||||
import BasePage from '@renderer/components/base/base-page'
|
||||
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||
|
@ -15,7 +15,7 @@ const Settings: React.FC = () => {
|
|||
})
|
||||
|
||||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const { silentStart = false } = appConfig || {}
|
||||
const { silentStart = false, delayTestUrl, delayTestTimeout } = appConfig || {}
|
||||
|
||||
return (
|
||||
<BasePage
|
||||
|
@ -57,6 +57,32 @@ const Settings: React.FC = () => {
|
|||
/>
|
||||
</SettingItem>
|
||||
</SettingCard>
|
||||
<SettingCard>
|
||||
<SettingItem title="延迟测试地址" divider>
|
||||
<Input
|
||||
size="sm"
|
||||
className="w-[60%]"
|
||||
spellCheck={false}
|
||||
value={delayTestUrl}
|
||||
placeholder="默认https://www.gstatic.com/generate_204"
|
||||
onValueChange={(v) => {
|
||||
patchAppConfig({ delayTestUrl: v })
|
||||
}}
|
||||
></Input>
|
||||
</SettingItem>
|
||||
<SettingItem title="延迟测试超时时间">
|
||||
<Input
|
||||
type="number"
|
||||
size="sm"
|
||||
className="w-[60%]"
|
||||
value={delayTestTimeout?.toString()}
|
||||
placeholder="默认5000"
|
||||
onValueChange={(v) => {
|
||||
patchAppConfig({ delayTestTimeout: parseInt(v) })
|
||||
}}
|
||||
></Input>
|
||||
</SettingItem>
|
||||
</SettingCard>
|
||||
</BasePage>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ const Sysproxy: React.FC = () => {
|
|||
size="sm"
|
||||
className="w-[50%]"
|
||||
value={values.host}
|
||||
spellCheck={false}
|
||||
placeholder="默认127.0.0.1若无特殊需求请勿修改"
|
||||
onValueChange={(v) => {
|
||||
setValues({ ...values, host: v })
|
||||
|
|
|
@ -22,6 +22,10 @@ export async function mihomoChangeProxy(group: string, proxy: string): Promise<I
|
|||
return await window.electron.ipcRenderer.invoke('mihomoChangeProxy', group, proxy)
|
||||
}
|
||||
|
||||
export async function mihomoProxyDelay(proxy: string, url?: string): Promise<IMihomoDelay> {
|
||||
return await window.electron.ipcRenderer.invoke('mihomoProxyDelay', proxy, url)
|
||||
}
|
||||
|
||||
export async function startMihomoLogs(): Promise<void> {
|
||||
return await window.electron.ipcRenderer.invoke('startMihomoLogs')
|
||||
}
|
||||
|
|
9
src/shared/types.d.ts
vendored
9
src/shared/types.d.ts
vendored
|
@ -77,6 +77,11 @@ interface IMihomoHistory {
|
|||
delay: number
|
||||
}
|
||||
|
||||
interface IMihomoDelay {
|
||||
delay?: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface IMihomoProxy {
|
||||
alive: boolean
|
||||
extra: Record<string, { alive: boolean; history: IMihomoHistory[] }>
|
||||
|
@ -93,6 +98,7 @@ interface IMihomoGroup {
|
|||
alive: boolean
|
||||
all: string[]
|
||||
extra: Record<string, { alive: boolean; history: IMihomoHistory[] }>
|
||||
testUrl?: string
|
||||
hidden: boolean
|
||||
history: IMihomoHistory[]
|
||||
icon: string
|
||||
|
@ -118,9 +124,12 @@ interface ISysProxyConfig {
|
|||
|
||||
interface IAppConfig {
|
||||
core: 'mihomo' | 'mihomo-alpha'
|
||||
proxyDisplayMode: 'simple' | 'full'
|
||||
silentStart: boolean
|
||||
sysProxy: ISysProxyConfig
|
||||
userAgent?: string
|
||||
delayTestUrl?: string
|
||||
delayTestTimeout?: number
|
||||
}
|
||||
|
||||
interface IMihomoTunConfig {
|
||||
|
|
Loading…
Reference in New Issue
Block a user