mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-12-26 19:24:12 +08:00
fix: latency not correct when set custom url in config (#1189)
To thoroughly fix this issue, all latency-related logic needs to be strongly bound to testurl
This commit is contained in:
parent
f610333717
commit
af3081371d
@ -1,17 +1,23 @@
|
||||
import { JSX, ParentComponent } from 'solid-js'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { getLatencyClassName } from '~/helpers'
|
||||
import { useProxies } from '~/signals'
|
||||
import { urlForLatencyTest, useProxies } from '~/signals'
|
||||
|
||||
interface Props extends JSX.HTMLAttributes<HTMLSpanElement> {
|
||||
proxyName: string
|
||||
testUrl: string | null
|
||||
}
|
||||
|
||||
export const Latency: ParentComponent<Props> = (props) => {
|
||||
const [local, others] = splitProps(props, ['class'])
|
||||
const { getLatencyByName } = useProxies()
|
||||
const [textClassName, setTextClassName] = createSignal('')
|
||||
const latency = createMemo(() => getLatencyByName(others.proxyName || ''))
|
||||
const latency = createMemo(() =>
|
||||
getLatencyByName(
|
||||
others.proxyName || '',
|
||||
others.testUrl || urlForLatencyTest(),
|
||||
),
|
||||
)
|
||||
|
||||
createEffect(() => {
|
||||
setTextClassName(getLatencyClassName(latency()))
|
||||
@ -19,7 +25,11 @@ export const Latency: ParentComponent<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<span
|
||||
class={twMerge('badge w-11 whitespace-nowrap', textClassName(), local.class)}
|
||||
class={twMerge(
|
||||
'badge w-11 whitespace-nowrap',
|
||||
textClassName(),
|
||||
local.class,
|
||||
)}
|
||||
{...others}
|
||||
>
|
||||
{latency() || '---'}
|
||||
|
@ -8,10 +8,12 @@ import {
|
||||
formatProxyType,
|
||||
getLatencyClassName,
|
||||
} from '~/helpers'
|
||||
import { rootElement, useProxies } from '~/signals'
|
||||
import { rootElement, urlForLatencyTest, useProxies } from '~/signals'
|
||||
|
||||
export const ProxyNodeCard = (props: {
|
||||
proxyName: string
|
||||
testUrl: string | null
|
||||
timeout: number | null
|
||||
isSelected?: boolean
|
||||
onClick?: () => void
|
||||
}) => {
|
||||
@ -36,6 +38,9 @@ export const ProxyNodeCard = (props: {
|
||||
[proxyName, specialTypes()].filter(Boolean).join(' - '),
|
||||
)
|
||||
|
||||
const latencyTestHistory =
|
||||
proxyNode().latencyTestHistory[props.testUrl || urlForLatencyTest()] || []
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
placement="top"
|
||||
@ -67,13 +72,19 @@ export const ProxyNodeCard = (props: {
|
||||
|
||||
<Latency
|
||||
proxyName={props.proxyName}
|
||||
testUrl={props.testUrl || null}
|
||||
class={twMerge(
|
||||
proxyLatencyTestingMap()[proxyName] && 'animate-pulse',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
void proxyLatencyTest(proxyName, proxyNode().provider)
|
||||
void proxyLatencyTest(
|
||||
proxyName,
|
||||
proxyNode().provider,
|
||||
props.testUrl,
|
||||
props.timeout,
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -87,12 +98,10 @@ export const ProxyNodeCard = (props: {
|
||||
<div class="flex flex-col items-center gap-2 rounded-box bg-neutral bg-gradient-to-br from-primary to-secondary p-2.5 text-primary-content shadow-lg">
|
||||
<h2 class="text-lg font-bold">{proxyName}</h2>
|
||||
|
||||
<div class="w-full text-xs uppercase">
|
||||
{specialTypes()}
|
||||
</div>
|
||||
<div class="w-full text-xs uppercase">{specialTypes()}</div>
|
||||
|
||||
<ul class="timeline timeline-vertical timeline-compact timeline-snap-icon">
|
||||
<For each={proxyNode().latencyTestHistory}>
|
||||
<For each={latencyTestHistory}>
|
||||
{(latencyTestResult, index) => (
|
||||
<li>
|
||||
<Show when={index() > 0}>
|
||||
@ -120,11 +129,7 @@ export const ProxyNodeCard = (props: {
|
||||
<IconCircleCheckFilled class="size-4" />
|
||||
</div>
|
||||
|
||||
<Show
|
||||
when={
|
||||
index() !== proxyNode().latencyTestHistory.length - 1
|
||||
}
|
||||
>
|
||||
<Show when={index() !== latencyTestHistory.length - 1}>
|
||||
<hr />
|
||||
</Show>
|
||||
</li>
|
||||
|
@ -4,6 +4,7 @@ import { proxiesPreviewType } from '~/signals'
|
||||
|
||||
export const ProxyNodePreview = (props: {
|
||||
proxyNameList: string[]
|
||||
testUrl: string | null
|
||||
now?: string
|
||||
}) => {
|
||||
const off = () => proxiesPreviewType() === PROXIES_PREVIEW_TYPE.OFF
|
||||
@ -34,6 +35,7 @@ export const ProxyNodePreview = (props: {
|
||||
<Match when={isShowBar()}>
|
||||
<ProxyPreviewBar
|
||||
proxyNameList={props.proxyNameList}
|
||||
testUrl={props.testUrl}
|
||||
now={props.now}
|
||||
/>
|
||||
</Match>
|
||||
@ -41,6 +43,7 @@ export const ProxyNodePreview = (props: {
|
||||
<Match when={isShowDots()}>
|
||||
<ProxyPreviewDots
|
||||
proxyNameList={props.proxyNameList}
|
||||
testUrl={props.testUrl}
|
||||
now={props.now}
|
||||
/>
|
||||
</Match>
|
||||
|
@ -3,11 +3,12 @@ import { latencyQualityMap, useProxies } from '~/signals'
|
||||
|
||||
export const ProxyPreviewBar = (props: {
|
||||
proxyNameList: string[]
|
||||
testUrl: string | null
|
||||
now?: string
|
||||
}) => {
|
||||
const { getLatencyByName } = useProxies()
|
||||
const latencyList = createMemo(() =>
|
||||
props.proxyNameList.map((name) => getLatencyByName(name)),
|
||||
props.proxyNameList.map((name) => getLatencyByName(name, props.testUrl)),
|
||||
)
|
||||
|
||||
const all = createMemo(() => latencyList().length)
|
||||
@ -69,7 +70,7 @@ export const ProxyPreviewBar = (props: {
|
||||
</div>
|
||||
|
||||
<Show when={props.now}>
|
||||
<Latency proxyName={props.now!} />
|
||||
<Latency proxyName={props.now!} testUrl={props.testUrl} />
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
|
@ -38,6 +38,7 @@ const LatencyDot = (props: {
|
||||
|
||||
export const ProxyPreviewDots = (props: {
|
||||
proxyNameList: string[]
|
||||
testUrl: string | null
|
||||
now?: string
|
||||
}) => {
|
||||
const { getLatencyByName } = useProxies()
|
||||
@ -48,7 +49,7 @@ export const ProxyPreviewDots = (props: {
|
||||
<For
|
||||
each={props.proxyNameList.map((name): [string, number] => [
|
||||
name,
|
||||
getLatencyByName(name),
|
||||
getLatencyByName(name, props.testUrl),
|
||||
])}
|
||||
>
|
||||
{([name, latency]) => (
|
||||
@ -62,7 +63,7 @@ export const ProxyPreviewDots = (props: {
|
||||
</div>
|
||||
|
||||
<Show when={props.now}>
|
||||
<Latency proxyName={props.now!} />
|
||||
<Latency proxyName={props.now!} testUrl={props.testUrl} />
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { LATENCY_QUALITY_MAP_HTTP, PROXIES_ORDERING_TYPE } from '~/constants'
|
||||
import { useI18n } from '~/i18n'
|
||||
import { latencyQualityMap, useProxies } from '~/signals'
|
||||
import { latencyQualityMap, urlForLatencyTest, useProxies } from '~/signals'
|
||||
|
||||
export const formatProxyType = (type = '') => {
|
||||
const [t] = useI18n()
|
||||
@ -55,19 +55,26 @@ export const filterSpecialProxyType = (type = '') => {
|
||||
return !conditions.includes(type.toLowerCase())
|
||||
}
|
||||
|
||||
export const sortProxiesByOrderingType = (
|
||||
proxyNames: string[],
|
||||
orderingType: PROXIES_ORDERING_TYPE,
|
||||
) => {
|
||||
export const sortProxiesByOrderingType = ({
|
||||
proxyNames,
|
||||
orderingType,
|
||||
testUrl,
|
||||
}: {
|
||||
proxyNames: string[]
|
||||
orderingType: PROXIES_ORDERING_TYPE
|
||||
testUrl: string | null
|
||||
}) => {
|
||||
const { getLatencyByName } = useProxies()
|
||||
|
||||
if (orderingType === PROXIES_ORDERING_TYPE.NATURAL) {
|
||||
return proxyNames
|
||||
}
|
||||
|
||||
const finalTestUrl = testUrl || urlForLatencyTest()
|
||||
|
||||
return proxyNames.sort((a, b) => {
|
||||
const prevLatency = getLatencyByName(a)
|
||||
const nextLatency = getLatencyByName(b)
|
||||
const prevLatency = getLatencyByName(a, finalTestUrl)
|
||||
const nextLatency = getLatencyByName(b, finalTestUrl)
|
||||
|
||||
switch (orderingType) {
|
||||
case PROXIES_ORDERING_TYPE.LATENCY_ASC:
|
||||
@ -96,19 +103,27 @@ export const sortProxiesByOrderingType = (
|
||||
})
|
||||
}
|
||||
|
||||
export const filterProxiesByAvailability = (
|
||||
proxyNames: string[],
|
||||
enabled?: boolean,
|
||||
) => {
|
||||
export const filterProxiesByAvailability = ({
|
||||
proxyNames,
|
||||
enabled,
|
||||
testUrl,
|
||||
}: {
|
||||
proxyNames: string[]
|
||||
enabled?: boolean
|
||||
testUrl: string | null
|
||||
}) => {
|
||||
const { getLatencyByName, isProxyGroup } = useProxies()
|
||||
|
||||
const finalTestUrl = testUrl || urlForLatencyTest()
|
||||
|
||||
return enabled
|
||||
? proxyNames.filter(
|
||||
// we need proxy node with connected or the node is a group it self
|
||||
(name) => {
|
||||
return (
|
||||
isProxyGroup(name) ||
|
||||
getLatencyByName(name) !== latencyQualityMap().NOT_CONNECTED
|
||||
getLatencyByName(name, finalTestUrl) !==
|
||||
latencyQualityMap().NOT_CONNECTED
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -198,13 +198,15 @@ export default () => {
|
||||
<For each={renderProxies()}>
|
||||
{(proxyGroup) => {
|
||||
const sortedProxyNames = createMemo(() =>
|
||||
filterProxiesByAvailability(
|
||||
sortProxiesByOrderingType(
|
||||
proxyGroup.all ?? [],
|
||||
proxiesOrderingType(),
|
||||
),
|
||||
hideUnAvailableProxies(),
|
||||
),
|
||||
filterProxiesByAvailability({
|
||||
proxyNames: sortProxiesByOrderingType({
|
||||
proxyNames: proxyGroup.all ?? [],
|
||||
orderingType: proxiesOrderingType(),
|
||||
testUrl: proxyGroup.testUrl || null,
|
||||
}),
|
||||
enabled: hideUnAvailableProxies(),
|
||||
testUrl: proxyGroup.testUrl || null,
|
||||
}),
|
||||
)
|
||||
|
||||
const title = (
|
||||
@ -291,6 +293,7 @@ export default () => {
|
||||
<ProxyNodePreview
|
||||
proxyNameList={sortedProxyNames()}
|
||||
now={proxyGroup.now}
|
||||
testUrl={proxyGroup.testUrl || null}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
@ -308,6 +311,8 @@ export default () => {
|
||||
{(proxyName) => (
|
||||
<ProxyNodeCard
|
||||
proxyName={proxyName}
|
||||
testUrl={proxyGroup.testUrl || null}
|
||||
timeout={proxyGroup.timeout ?? null}
|
||||
isSelected={proxyGroup.now === proxyName}
|
||||
onClick={() =>
|
||||
void selectProxyInGroup(proxyGroup, proxyName)
|
||||
@ -327,10 +332,12 @@ export default () => {
|
||||
<For each={proxyProviders()}>
|
||||
{(proxyProvider) => {
|
||||
const sortedProxyNames = createMemo(() =>
|
||||
sortProxiesByOrderingType(
|
||||
proxyProvider.proxies.map((i) => i.name) ?? [],
|
||||
proxiesOrderingType(),
|
||||
),
|
||||
sortProxiesByOrderingType({
|
||||
orderingType: proxiesOrderingType(),
|
||||
proxyNames:
|
||||
proxyProvider.proxies.map((i) => i.name) ?? [],
|
||||
testUrl: proxyProvider.testUrl,
|
||||
}),
|
||||
)
|
||||
|
||||
const title = (
|
||||
@ -397,7 +404,10 @@ export default () => {
|
||||
</div>
|
||||
|
||||
<Show when={!collapsedMap()[proxyProvider.name]}>
|
||||
<ProxyNodePreview proxyNameList={sortedProxyNames()} />
|
||||
<ProxyNodePreview
|
||||
proxyNameList={sortedProxyNames()}
|
||||
testUrl={proxyProvider.testUrl}
|
||||
/>
|
||||
</Show>
|
||||
</>
|
||||
)
|
||||
@ -411,7 +421,13 @@ export default () => {
|
||||
}
|
||||
>
|
||||
<For each={sortedProxyNames()}>
|
||||
{(proxyName) => <ProxyNodeCard proxyName={proxyName} />}
|
||||
{(proxyName) => (
|
||||
<ProxyNodeCard
|
||||
proxyName={proxyName}
|
||||
testUrl={proxyProvider.testUrl}
|
||||
timeout={proxyProvider.timeout ?? null}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</Collapse>
|
||||
)
|
||||
|
@ -23,10 +23,7 @@ type ProxyInfo = {
|
||||
name: string
|
||||
udp: boolean
|
||||
tfo: boolean
|
||||
latencyTestHistory: {
|
||||
time: string
|
||||
delay: number
|
||||
}[]
|
||||
latencyTestHistory: Record<string, Proxy['history'] | undefined>
|
||||
latency: string
|
||||
xudp: boolean
|
||||
type: string
|
||||
@ -59,31 +56,61 @@ const [proxyProviders, setProxyProviders] = createSignal<
|
||||
>([])
|
||||
|
||||
// DO NOT use latency from latency map directly use getLatencyByName instead
|
||||
const [latencyMap, setLatencyMap] = createSignal<Record<string, number>>({})
|
||||
const [latencyMap, setLatencyMap] = createSignal<
|
||||
Record<string, Record<string, number> | undefined>
|
||||
>({})
|
||||
|
||||
const [proxyNodeMap, setProxyNodeMap] = createSignal<Record<string, ProxyInfo>>(
|
||||
{},
|
||||
)
|
||||
|
||||
type AllTestUrlLatencyInfo = {
|
||||
allTestUrlLatency: Record<string, number>
|
||||
allTestUrlLatencyHistory: Record<string, Proxy['history'] | undefined>
|
||||
}
|
||||
|
||||
const getLatencyFromProxy = (
|
||||
proxy: Pick<Proxy, 'extra' | 'history'>,
|
||||
url: string,
|
||||
fallbackDefault = true,
|
||||
) => {
|
||||
const extra = proxy.extra?.[url] as Proxy['history'] | undefined
|
||||
|
||||
if (Array.isArray(extra)) {
|
||||
const delay = extra.at(-1)?.delay
|
||||
|
||||
if (delay) {
|
||||
return delay
|
||||
): AllTestUrlLatencyInfo => {
|
||||
const extra = (proxy.extra || {}) as Record<
|
||||
string,
|
||||
{
|
||||
history?: Proxy['history']
|
||||
}
|
||||
>
|
||||
|
||||
if (!Object.keys(extra).length && fallbackDefault) {
|
||||
const testUrl = urlForLatencyTest()
|
||||
|
||||
const delay =
|
||||
proxy.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED
|
||||
|
||||
const allTestUrlLatency = { [testUrl]: delay }
|
||||
const allTestUrlLatencyHistory = { [testUrl]: proxy.history }
|
||||
|
||||
return {
|
||||
allTestUrlLatency,
|
||||
allTestUrlLatencyHistory,
|
||||
} as AllTestUrlLatencyInfo
|
||||
}
|
||||
|
||||
if (!fallbackDefault) {
|
||||
return latencyQualityMap().NOT_CONNECTED
|
||||
}
|
||||
return Object.keys(extra).reduce(
|
||||
(acc, testUrl) => {
|
||||
const data = extra[testUrl]
|
||||
const delay =
|
||||
data?.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED
|
||||
|
||||
return proxy.history?.at(-1)?.delay ?? latencyQualityMap().NOT_CONNECTED
|
||||
acc.allTestUrlLatency[testUrl] = delay
|
||||
acc.allTestUrlLatencyHistory[testUrl] = data.history
|
||||
|
||||
return acc
|
||||
},
|
||||
{
|
||||
allTestUrlLatency: {},
|
||||
allTestUrlLatencyHistory: {},
|
||||
} as AllTestUrlLatencyInfo,
|
||||
)
|
||||
}
|
||||
|
||||
const setProxiesInfo = (
|
||||
@ -93,20 +120,23 @@ const setProxiesInfo = (
|
||||
const newLatencyMap = { ...latencyMap() }
|
||||
|
||||
proxies.forEach((proxy) => {
|
||||
const { udp, xudp, type, now, history, name, tfo, provider = '' } = proxy
|
||||
const { allTestUrlLatency, allTestUrlLatencyHistory } =
|
||||
getLatencyFromProxy(proxy)
|
||||
|
||||
const { udp, xudp, type, now, name, tfo, provider = '' } = proxy
|
||||
|
||||
newProxyNodeMap[proxy.name] = {
|
||||
udp,
|
||||
xudp,
|
||||
type,
|
||||
latency: now,
|
||||
latencyTestHistory: history,
|
||||
latencyTestHistory: allTestUrlLatencyHistory,
|
||||
name,
|
||||
tfo,
|
||||
provider,
|
||||
}
|
||||
|
||||
newLatencyMap[proxy.name] = getLatencyFromProxy(proxy, urlForLatencyTest())
|
||||
newLatencyMap[proxy.name] = allTestUrlLatency
|
||||
})
|
||||
|
||||
batch(() => {
|
||||
@ -122,8 +152,18 @@ export const useProxies = () => {
|
||||
fetchProxiesAPI(),
|
||||
])
|
||||
|
||||
const proxiesWithTestUrl = Object.values(proxies).map((proxy) => {
|
||||
if (proxy.all?.length) {
|
||||
const { testUrl, timeout } = providers?.[proxy.name] || {}
|
||||
|
||||
return { ...proxy, testUrl, timeout }
|
||||
} else {
|
||||
return proxy
|
||||
}
|
||||
})
|
||||
|
||||
const sortIndex = [...(proxies['GLOBAL'].all ?? []), 'GLOBAL']
|
||||
const sortedProxies = Object.values(proxies)
|
||||
const sortedProxies = Object.values(proxiesWithTestUrl)
|
||||
.filter((proxy) => proxy.all?.length)
|
||||
.sort(
|
||||
(prev, next) =>
|
||||
@ -135,7 +175,7 @@ export const useProxies = () => {
|
||||
)
|
||||
|
||||
const allProxies = [
|
||||
...Object.values(proxies),
|
||||
...proxiesWithTestUrl,
|
||||
...sortedProviders.flatMap((provider) =>
|
||||
provider.proxies
|
||||
.filter((proxy) => !(proxy.name in proxies))
|
||||
@ -177,37 +217,53 @@ export const useProxies = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const proxyLatencyTest = async (proxyName: string, provider: string) => {
|
||||
const proxyLatencyTest = async (
|
||||
proxyName: string,
|
||||
provider: string,
|
||||
testUrl: string | null,
|
||||
timmeout: number | null,
|
||||
) => {
|
||||
const nodeName = getNowProxyNodeName(proxyName)
|
||||
|
||||
setProxyLatencyTestingMap(nodeName, async () => {
|
||||
const finalTestUrl = testUrl || urlForLatencyTest()
|
||||
const currentNodeLatency = latencyMap()?.[nodeName] || {}
|
||||
try {
|
||||
const { delay } = await proxyLatencyTestAPI(
|
||||
nodeName,
|
||||
provider,
|
||||
urlForLatencyTest(),
|
||||
latencyTestTimeoutDuration(),
|
||||
finalTestUrl,
|
||||
timmeout ?? latencyTestTimeoutDuration(),
|
||||
)
|
||||
|
||||
setLatencyMap((latencyMap) => ({
|
||||
...latencyMap,
|
||||
[nodeName]: delay,
|
||||
}))
|
||||
currentNodeLatency[finalTestUrl] = delay
|
||||
|
||||
setLatencyMap((latencyMap) => {
|
||||
return {
|
||||
...latencyMap,
|
||||
[nodeName]: currentNodeLatency,
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
currentNodeLatency[finalTestUrl] = latencyQualityMap().NOT_CONNECTED
|
||||
setLatencyMap((latencyMap) => ({
|
||||
...latencyMap,
|
||||
[nodeName]: latencyQualityMap().NOT_CONNECTED,
|
||||
[nodeName]: currentNodeLatency,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const proxyGroupLatencyTest = async (proxyGroupName: string) => {
|
||||
const currentProxyGroups = proxies()
|
||||
setProxyGroupLatencyTestingMap(proxyGroupName, async () => {
|
||||
const currentProxyGroup = currentProxyGroups.find(
|
||||
(item) => item.name === proxyGroupName,
|
||||
)
|
||||
await proxyGroupLatencyTestAPI(
|
||||
proxyGroupName,
|
||||
urlForLatencyTest(),
|
||||
latencyTestTimeoutDuration(),
|
||||
currentProxyGroup?.testUrl || urlForLatencyTest(),
|
||||
currentProxyGroup?.timeout ?? latencyTestTimeoutDuration(),
|
||||
)
|
||||
await fetchProxies()
|
||||
})
|
||||
@ -263,8 +319,10 @@ export const useProxies = () => {
|
||||
return node.name
|
||||
}
|
||||
|
||||
const getLatencyByName = (name: string) => {
|
||||
return latencyMap()[getNowProxyNodeName(name)]
|
||||
const getLatencyByName = (name: string, testUrl: string | null) => {
|
||||
const finalTestUrl = testUrl || urlForLatencyTest()
|
||||
|
||||
return latencyMap()[getNowProxyNodeName(name)]?.[finalTestUrl] || 0
|
||||
}
|
||||
|
||||
const isProxyGroup = (name: string) => {
|
||||
|
3
src/types/index.d.ts
vendored
3
src/types/index.d.ts
vendored
@ -23,6 +23,8 @@ export type Proxy = {
|
||||
xudp: boolean
|
||||
tfo: boolean
|
||||
now: string
|
||||
testUrl?: string
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export type ProxyNode = {
|
||||
@ -53,6 +55,7 @@ export type ProxyProvider = {
|
||||
name: string
|
||||
proxies: ProxyNode[]
|
||||
testUrl: string
|
||||
timeout?: number
|
||||
updatedAt: string
|
||||
vehicleType: string
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user