Should check provider health if a group contains provider proxies
This commit is contained in:
parent
69423bdfff
commit
38571da24a
4 changed files with 104 additions and 69 deletions
|
@ -47,7 +47,7 @@ type ProxyProps = {
|
|||
name: string;
|
||||
now?: boolean;
|
||||
proxy: any;
|
||||
latency: any;
|
||||
latency?: { number?: number };
|
||||
isSelectable?: boolean;
|
||||
onClick?: (proxyName: string) => unknown;
|
||||
};
|
||||
|
@ -130,10 +130,10 @@ function ProxyImpl({ now, name, proxy, latency, isSelectable, onClick }: ProxyPr
|
|||
const className = useMemo(() => {
|
||||
return cx(s0.proxy, {
|
||||
[s0.now]: now,
|
||||
[s0.error]: latency && latency.error,
|
||||
// [s0.error]: latency && latency.error,
|
||||
[s0.selectable]: isSelectable,
|
||||
});
|
||||
}, [isSelectable, now, latency]);
|
||||
}, [isSelectable, now]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -161,10 +161,8 @@ function ProxyImpl({ now, name, proxy, latency, isSelectable, onClick }: ProxyPr
|
|||
const mapState = (s: State, { name }) => {
|
||||
const proxies = getProxies(s);
|
||||
const delay = getDelay(s);
|
||||
return {
|
||||
proxy: proxies[name],
|
||||
latency: delay[name],
|
||||
};
|
||||
const proxy = proxies[name] || { name, type: 'Unknown', history: [] };
|
||||
return { proxy, latency: delay[name] };
|
||||
};
|
||||
|
||||
export const Proxy = connect(mapState)(ProxyImpl);
|
||||
|
|
|
@ -2,7 +2,8 @@ import Tooltip from '@reach/tooltip';
|
|||
import * as React from 'react';
|
||||
|
||||
import { useState2 } from '$src/hooks/basic';
|
||||
import { State } from '$src/store/types';
|
||||
import { DelayMapping, DispatchFn, ProxiesMapping, State } from '$src/store/types';
|
||||
import { ClashAPIConfig } from '$src/types';
|
||||
|
||||
import { getCollapsibleIsOpen, getHideUnavailableProxies, getProxySortBy } from '../../store/app';
|
||||
import { getProxies, switchProxy } from '../../store/proxies';
|
||||
|
@ -16,6 +17,20 @@ import { ProxyList, ProxyListSummaryView } from './ProxyList';
|
|||
|
||||
const { createElement, useCallback, useMemo } = React;
|
||||
|
||||
type ProxyGroupImplProps = {
|
||||
name: string;
|
||||
all: string[];
|
||||
delay: DelayMapping;
|
||||
hideUnavailableProxies: boolean;
|
||||
proxySortBy: string;
|
||||
proxies: ProxiesMapping;
|
||||
type: string;
|
||||
now: string;
|
||||
isOpen: boolean;
|
||||
apiConfig: ClashAPIConfig;
|
||||
dispatch: DispatchFn;
|
||||
};
|
||||
|
||||
function ProxyGroupImpl({
|
||||
name,
|
||||
all: allItems,
|
||||
|
@ -28,7 +43,7 @@ function ProxyGroupImpl({
|
|||
isOpen,
|
||||
apiConfig,
|
||||
dispatch,
|
||||
}) {
|
||||
}: ProxyGroupImplProps) {
|
||||
const all = useFilteredAndSorted(allItems, delay, hideUnavailableProxies, proxySortBy, proxies);
|
||||
|
||||
const isSelectable = useMemo(() => type === 'Selector', [type]);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { atom } from 'recoil';
|
||||
import {
|
||||
DelayMapping,
|
||||
DispatchFn,
|
||||
FormattedProxyProvider,
|
||||
GetStateFn,
|
||||
LatencyHistory,
|
||||
ProxiesMapping,
|
||||
ProxyItem,
|
||||
ProxyProvider,
|
||||
|
@ -50,6 +52,19 @@ export const getProxyProviders = (s: State) => s.proxies.proxyProviders || [];
|
|||
export const getDangleProxyNames = (s: State) => s.proxies.dangleProxyNames;
|
||||
export const getShowModalClosePrevConns = (s: State) => s.proxies.showModalClosePrevConns;
|
||||
|
||||
function mapLatency(names: string[], getProxy: (name: string) => { history: LatencyHistory }) {
|
||||
const result: DelayMapping = {};
|
||||
for (const name of names) {
|
||||
const p = getProxy(name) || { history: [] };
|
||||
const history = p.history;
|
||||
const h = history[history.length - 1];
|
||||
if (h && typeof h.delay === 'number') {
|
||||
result[name] = { number: h.delay };
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function fetchProxies(apiConfig: ClashAPIConfig) {
|
||||
return async (dispatch: any, getState: any) => {
|
||||
const [proxiesData, providersData] = await Promise.all([
|
||||
|
@ -57,36 +72,28 @@ export function fetchProxies(apiConfig: ClashAPIConfig) {
|
|||
proxiesAPI.fetchProviderProxies(apiConfig),
|
||||
]);
|
||||
|
||||
const { providers: proxyProviders, proxies: providerProxies } = formatProxyProviders(
|
||||
providersData.providers
|
||||
);
|
||||
const proxies = { ...proxiesData.proxies, ...providerProxies };
|
||||
const { proxyProviders, providerProxyRecord } = formatProxyProviders(providersData.providers);
|
||||
|
||||
const proxies = { ...providerProxyRecord, ...proxiesData.proxies };
|
||||
const [groupNames, proxyNames] = retrieveGroupNamesFrom(proxies);
|
||||
|
||||
const delayPrev = getDelay(getState());
|
||||
const delayNext = { ...delayPrev };
|
||||
|
||||
for (let i = 0; i < proxyNames.length; i++) {
|
||||
const name = proxyNames[i];
|
||||
const { history } = proxies[name] || { history: [] };
|
||||
const h = history[history.length - 1];
|
||||
if (h && typeof h.delay === 'number') {
|
||||
delayNext[name] = { number: h.delay };
|
||||
}
|
||||
}
|
||||
const delayNext = {
|
||||
...getDelay(getState()),
|
||||
...mapLatency(Object.keys(proxies), (name) => proxies[name]),
|
||||
};
|
||||
|
||||
// proxies that are not from a provider
|
||||
const dangleProxyNames = [];
|
||||
for (const v of proxyNames) {
|
||||
if (!providerProxies[v]) dangleProxyNames.push(v);
|
||||
if (!providerProxyRecord[v]) dangleProxyNames.push(v);
|
||||
}
|
||||
|
||||
dispatch('store/proxies#fetchProxies', (s: State) => {
|
||||
s.proxies.proxies = proxies;
|
||||
s.proxies.groupNames = groupNames;
|
||||
s.proxies.dangleProxyNames = dangleProxyNames;
|
||||
s.proxies.delay = delayNext;
|
||||
s.proxies.proxyProviders = proxyProviders;
|
||||
s.proxies.dangleProxyNames = dangleProxyNames;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -201,11 +208,6 @@ async function switchProxyImpl(
|
|||
// no wait
|
||||
closePrevConns(apiConfig, proxies, { groupName, itemName });
|
||||
}
|
||||
|
||||
/* dispatch('showModalClosePrevConns', (s: GlobalState) => { */
|
||||
/* s.proxies.showModalClosePrevConns = true; */
|
||||
/* s.proxies.switchProxyCtx = { to: { groupName, itemName } }; */
|
||||
/* }); */
|
||||
}
|
||||
|
||||
function closeModalClosePrevConns() {
|
||||
|
@ -273,15 +275,7 @@ function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) {
|
|||
error = res.statusText;
|
||||
}
|
||||
const { delay } = await res.json();
|
||||
|
||||
const delayPrev = getDelay(getState());
|
||||
const delayNext = {
|
||||
...delayPrev,
|
||||
[name]: {
|
||||
error,
|
||||
number: delay,
|
||||
},
|
||||
};
|
||||
const delayNext = { ...getDelay(getState()), [name]: { error, number: delay } };
|
||||
|
||||
dispatch('requestDelayForProxyOnce', (s) => {
|
||||
s.proxies.delay = delayNext;
|
||||
|
@ -297,12 +291,33 @@ export function requestDelayForProxy(apiConfig: ClashAPIConfig, name: string) {
|
|||
|
||||
export function requestDelayForProxies(apiConfig: ClashAPIConfig, names: string[]) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const proxyNames = getDangleProxyNames(getState());
|
||||
const proxies = getProxies(getState());
|
||||
const latencyTestUrl = getLatencyTestUrl(getState());
|
||||
|
||||
const works = names
|
||||
// remove names that are provided by proxy providers
|
||||
.filter((p) => proxyNames.indexOf(p) > -1)
|
||||
.map((p) => dispatch(requestDelayForProxy(apiConfig, p)));
|
||||
const proxyDedupMap = new Map<string, boolean>();
|
||||
const providerDedupMap = new Map<string, boolean>();
|
||||
|
||||
const works = names.map((name) => {
|
||||
const p = proxies[name];
|
||||
if (!p.__provider) {
|
||||
if (proxyDedupMap.get(name)) {
|
||||
return undefined;
|
||||
} else {
|
||||
proxyDedupMap.set(name, true);
|
||||
return proxiesAPI.requestDelayForProxy(apiConfig, name, latencyTestUrl);
|
||||
}
|
||||
} else if (p.__provider) {
|
||||
// this one is from a proxy provider
|
||||
if (providerDedupMap.get(p.__provider)) {
|
||||
return undefined;
|
||||
} else {
|
||||
providerDedupMap.set(p.__provider, true);
|
||||
return healthcheckProviderByNameInternal(apiConfig, p.__provider);
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
await Promise.all(works);
|
||||
await dispatch(fetchProxies(apiConfig));
|
||||
};
|
||||
|
@ -311,7 +326,10 @@ export function requestDelayForProxies(apiConfig: ClashAPIConfig, names: string[
|
|||
export function requestDelayAll(apiConfig: ClashAPIConfig) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const proxyNames = getDangleProxyNames(getState());
|
||||
await Promise.all(proxyNames.map((p) => dispatch(requestDelayForProxy(apiConfig, p))));
|
||||
const latencyTestUrl = getLatencyTestUrl(getState());
|
||||
await Promise.all(
|
||||
proxyNames.map((p) => proxiesAPI.requestDelayForProxy(apiConfig, p, latencyTestUrl))
|
||||
);
|
||||
const proxyProviders = getProxyProviders(getState());
|
||||
// one by one
|
||||
for (const p of proxyProviders) {
|
||||
|
@ -353,12 +371,13 @@ type ProvidersRaw = {
|
|||
};
|
||||
|
||||
function formatProxyProviders(providersInput: ProvidersRaw): {
|
||||
providers: Array<FormattedProxyProvider>;
|
||||
proxies: { [key: string]: ProxyItem };
|
||||
proxyProviders: Array<FormattedProxyProvider>;
|
||||
providerProxyRecord: ProxiesMapping;
|
||||
} {
|
||||
const keys = Object.keys(providersInput);
|
||||
const providers = [];
|
||||
const proxies = {};
|
||||
const proxyProviders = [];
|
||||
const providerProxyRecord: ProxiesMapping = {};
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const provider: ProxyProvider = providersInput[keys[i]];
|
||||
if (provider.name === 'default' || provider.vehicleType === 'Compatible') {
|
||||
|
@ -368,19 +387,16 @@ function formatProxyProviders(providersInput: ProvidersRaw): {
|
|||
const names = [];
|
||||
for (let j = 0; j < proxiesArr.length; j++) {
|
||||
const proxy = proxiesArr[j];
|
||||
proxies[proxy.name] = proxy;
|
||||
providerProxyRecord[proxy.name] = { ...proxy, __provider: provider.name };
|
||||
names.push(proxy.name);
|
||||
}
|
||||
|
||||
// mutate directly
|
||||
provider.proxies = names;
|
||||
providers.push(provider);
|
||||
proxyProviders.push(provider);
|
||||
}
|
||||
|
||||
return {
|
||||
providers,
|
||||
proxies,
|
||||
};
|
||||
return { proxyProviders, providerProxyRecord };
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
|
|
|
@ -27,39 +27,45 @@ export type ClashGeneralConfig = {
|
|||
|
||||
///// store.proxies
|
||||
|
||||
type LatencyHistory = Array<{ time: string; delay: number }>;
|
||||
type PrimitiveProxyType = 'Shadowsocks' | 'Snell' | 'Socks5' | 'Http' | 'Vmess';
|
||||
type LatencyHistoryItem = { time: string; delay: number };
|
||||
export type LatencyHistory = LatencyHistoryItem[];
|
||||
|
||||
export type ProxyItem = {
|
||||
name: string;
|
||||
type: PrimitiveProxyType;
|
||||
type: string;
|
||||
history: LatencyHistory;
|
||||
all?: string[];
|
||||
now?: string;
|
||||
|
||||
__provider?: string;
|
||||
};
|
||||
|
||||
export type ProxyDelayItem = {
|
||||
number?: number;
|
||||
};
|
||||
|
||||
export type ProxiesMapping = Record<string, ProxyItem>;
|
||||
export type DelayMapping = Record<string, { number?: number }>;
|
||||
export type DelayMapping = Record<string, ProxyDelayItem>;
|
||||
|
||||
export type ProxyProvider = {
|
||||
name: string;
|
||||
type: 'Proxy';
|
||||
updatedAt: string;
|
||||
vehicleType: 'HTTP' | 'File' | 'Compatible';
|
||||
proxies: Array<ProxyItem>;
|
||||
proxies: ProxyItem[];
|
||||
};
|
||||
|
||||
export type FormattedProxyProvider = Omit<ProxyProvider, 'proxies'> & {
|
||||
proxies: string[];
|
||||
};
|
||||
export type FormattedProxyProvider = Omit<ProxyProvider, 'proxies'> & { proxies: string[] };
|
||||
|
||||
export type SwitchProxyCtxItem = { groupName: string; itemName: string };
|
||||
type SwitchProxyCtx = {
|
||||
to: SwitchProxyCtxItem;
|
||||
};
|
||||
type SwitchProxyCtx = { to: SwitchProxyCtxItem };
|
||||
|
||||
export type StateProxies = {
|
||||
proxies: ProxiesMapping;
|
||||
delay: DelayMapping;
|
||||
groupNames: string[];
|
||||
proxyProviders?: FormattedProxyProvider[];
|
||||
|
||||
proxies: ProxiesMapping;
|
||||
delay: DelayMapping;
|
||||
dangleProxyNames?: string[];
|
||||
|
||||
showModalClosePrevConns: boolean;
|
||||
|
|
Loading…
Reference in a new issue