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:
njzy 2024-11-27 10:53:34 +08:00 committed by GitHub
parent f610333717
commit af3081371d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 190 additions and 78 deletions

View File

@ -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() || '---'}

View File

@ -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>

View File

@ -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>

View File

@ -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>
)

View File

@ -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>
)

View File

@ -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
)
},
)

View File

@ -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>
)

View File

@ -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) => {

View File

@ -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
}