feat: support check delay
This commit is contained in:
parent
e7bba968b3
commit
d0e678b5e9
@ -1,5 +1,6 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
|
import { useSWRConfig } from "swr";
|
||||||
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Collapse,
|
Collapse,
|
||||||
@ -19,6 +20,7 @@ import {
|
|||||||
import { ApiType } from "../../services/types";
|
import { ApiType } from "../../services/types";
|
||||||
import { updateProxy } from "../../services/api";
|
import { updateProxy } from "../../services/api";
|
||||||
import { getProfiles, patchProfile } from "../../services/cmds";
|
import { getProfiles, patchProfile } from "../../services/cmds";
|
||||||
|
import delayManager from "../../services/delay";
|
||||||
import ProxyItem from "./proxy-item";
|
import ProxyItem from "./proxy-item";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -26,13 +28,15 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ProxyGroup = ({ group }: Props) => {
|
const ProxyGroup = ({ group }: Props) => {
|
||||||
|
const { mutate } = useSWRConfig();
|
||||||
|
|
||||||
const listRef = useRef<any>();
|
const listRef = useRef<any>();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [now, setNow] = useState(group.now);
|
const [now, setNow] = useState(group.now);
|
||||||
|
|
||||||
const proxies = group.all ?? [];
|
const proxies = group.all ?? [];
|
||||||
|
|
||||||
const onUpdate = async (name: string) => {
|
const onSelect = async (name: string) => {
|
||||||
// can not call update
|
// can not call update
|
||||||
if (group.type !== "Selector") {
|
if (group.type !== "Selector") {
|
||||||
// Todo
|
// Todo
|
||||||
@ -80,6 +84,21 @@ const ProxyGroup = ({ group }: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCheckAll = async () => {
|
||||||
|
let names = proxies.map((p) => p.name);
|
||||||
|
|
||||||
|
while (names.length) {
|
||||||
|
const list = names.slice(0, 10);
|
||||||
|
names = names.slice(10);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
list.map((n) => delayManager.checkDelay(n, group.name))
|
||||||
|
);
|
||||||
|
|
||||||
|
mutate("getProxies");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListItem button onClick={() => setOpen(!open)} dense>
|
<ListItem button onClick={() => setOpen(!open)} dense>
|
||||||
@ -104,7 +123,7 @@ const ProxyGroup = ({ group }: Props) => {
|
|||||||
<IconButton size="small" title="location" onClick={onLocation}>
|
<IconButton size="small" title="location" onClick={onLocation}>
|
||||||
<MyLocationRounded />
|
<MyLocationRounded />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton size="small" title="check">
|
<IconButton size="small" title="check" onClick={onCheckAll}>
|
||||||
<NetworkCheckRounded />
|
<NetworkCheckRounded />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
@ -116,10 +135,11 @@ const ProxyGroup = ({ group }: Props) => {
|
|||||||
totalCount={proxies.length}
|
totalCount={proxies.length}
|
||||||
itemContent={(index) => (
|
itemContent={(index) => (
|
||||||
<ProxyItem
|
<ProxyItem
|
||||||
|
groupName={group.name}
|
||||||
proxy={proxies[index]}
|
proxy={proxies[index]}
|
||||||
selected={proxies[index].name === now}
|
selected={proxies[index].name === now}
|
||||||
sx={{ py: 0, pl: 4 }}
|
sx={{ py: 0, pl: 4 }}
|
||||||
onClick={onUpdate}
|
onClick={onSelect}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -132,10 +152,11 @@ const ProxyGroup = ({ group }: Props) => {
|
|||||||
{proxies.map((proxy) => (
|
{proxies.map((proxy) => (
|
||||||
<ProxyItem
|
<ProxyItem
|
||||||
key={proxy.name}
|
key={proxy.name}
|
||||||
|
groupName={group.name}
|
||||||
proxy={proxy}
|
proxy={proxy}
|
||||||
selected={proxy.name === now}
|
selected={proxy.name === now}
|
||||||
sx={{ py: 0, pl: 4 }}
|
sx={{ py: 0, pl: 4 }}
|
||||||
onClick={onUpdate}
|
onClick={onSelect}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
|
@ -1,24 +1,51 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
import { CheckCircleOutlineRounded } from "@mui/icons-material";
|
import { CheckCircleOutlineRounded } from "@mui/icons-material";
|
||||||
import {
|
import {
|
||||||
alpha,
|
alpha,
|
||||||
|
Box,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
|
styled,
|
||||||
SxProps,
|
SxProps,
|
||||||
Theme,
|
Theme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { ApiType } from "../../services/types";
|
import { ApiType } from "../../services/types";
|
||||||
|
import delayManager from "../../services/delay";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
groupName: string;
|
||||||
proxy: ApiType.ProxyItem;
|
proxy: ApiType.ProxyItem;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
onClick?: (name: string) => void;
|
onClick?: (name: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Widget = styled(Box)(() => ({
|
||||||
|
padding: "4px 6px",
|
||||||
|
fontSize: 14,
|
||||||
|
}));
|
||||||
|
|
||||||
const ProxyItem = (props: Props) => {
|
const ProxyItem = (props: Props) => {
|
||||||
const { proxy, selected, sx, onClick } = props;
|
const { groupName, proxy, selected, sx, onClick } = props;
|
||||||
|
const [delay, setDelay] = useState(-1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (proxy) {
|
||||||
|
setDelay(delayManager.getDelay(proxy.name, groupName));
|
||||||
|
}
|
||||||
|
}, [proxy]);
|
||||||
|
|
||||||
|
const onDelay = (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
delayManager
|
||||||
|
.checkDelay(proxy.name, groupName)
|
||||||
|
.then((result) => setDelay(result))
|
||||||
|
.catch(() => setDelay(1e6));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem sx={sx}>
|
<ListItem sx={sx}>
|
||||||
@ -27,9 +54,7 @@ const ProxyItem = (props: Props) => {
|
|||||||
selected={selected}
|
selected={selected}
|
||||||
onClick={() => onClick?.(proxy.name)}
|
onClick={() => onClick?.(proxy.name)}
|
||||||
sx={[
|
sx={[
|
||||||
{
|
{ borderRadius: 1 },
|
||||||
borderRadius: 1,
|
|
||||||
},
|
|
||||||
({ palette: { mode, primary } }) => {
|
({ palette: { mode, primary } }) => {
|
||||||
const bgcolor =
|
const bgcolor =
|
||||||
mode === "light"
|
mode === "light"
|
||||||
@ -37,7 +62,16 @@ const ProxyItem = (props: Props) => {
|
|||||||
: alpha(primary.main, 0.35);
|
: alpha(primary.main, 0.35);
|
||||||
const color = mode === "light" ? primary.main : primary.light;
|
const color = mode === "light" ? primary.main : primary.light;
|
||||||
|
|
||||||
|
const showDelay = delay > 0;
|
||||||
|
const showIcon = !showDelay && selected;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
".the-check": { display: "none" },
|
||||||
|
".the-delay": { display: showDelay ? "block" : "none" },
|
||||||
|
".the-icon": { display: showIcon ? "block" : "none" },
|
||||||
|
"&:hover .the-check": { display: !showDelay ? "block" : "none" },
|
||||||
|
"&:hover .the-delay": { display: showDelay ? "block" : "none" },
|
||||||
|
"&:hover .the-icon": { display: "none" },
|
||||||
"&.Mui-selected": { bgcolor },
|
"&.Mui-selected": { bgcolor },
|
||||||
"&.Mui-selected .MuiListItemText-secondary": { color },
|
"&.Mui-selected .MuiListItemText-secondary": { color },
|
||||||
};
|
};
|
||||||
@ -45,10 +79,32 @@ const ProxyItem = (props: Props) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ListItemText title={proxy.name} secondary={proxy.name} />
|
<ListItemText title={proxy.name} secondary={proxy.name} />
|
||||||
|
|
||||||
<ListItemIcon
|
<ListItemIcon
|
||||||
sx={{ justifyContent: "flex-end", color: "primary.main" }}
|
sx={{ justifyContent: "flex-end", color: "primary.main" }}
|
||||||
>
|
>
|
||||||
{selected && <CheckCircleOutlineRounded sx={{ fontSize: 16 }} />}
|
<Widget className="the-check" onClick={onDelay}>
|
||||||
|
Check
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<Widget
|
||||||
|
className="the-delay"
|
||||||
|
onClick={onDelay}
|
||||||
|
color={
|
||||||
|
delay > 500
|
||||||
|
? "error.main"
|
||||||
|
: delay < 100
|
||||||
|
? "success.main"
|
||||||
|
: "text.secondary"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{delay > 1e5 ? "Error" : delay > 3000 ? "Timeout" : `${delay}ms`}
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<CheckCircleOutlineRounded
|
||||||
|
className="the-icon"
|
||||||
|
sx={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
@ -114,6 +114,7 @@ const ProxyPage = () => {
|
|||||||
totalCount={filterProxies.length}
|
totalCount={filterProxies.length}
|
||||||
itemContent={(index) => (
|
itemContent={(index) => (
|
||||||
<ProxyItem
|
<ProxyItem
|
||||||
|
groupName="GLOBAL"
|
||||||
proxy={filterProxies[index]}
|
proxy={filterProxies[index]}
|
||||||
selected={filterProxies[index].name === curProxy}
|
selected={filterProxies[index].name === curProxy}
|
||||||
onClick={onChangeProxy}
|
onClick={onChangeProxy}
|
||||||
|
@ -60,10 +60,24 @@ export async function getRules() {
|
|||||||
return instance.get("/rules") as Promise<ApiType.RuleItem[]>;
|
return instance.get("/rules") as Promise<ApiType.RuleItem[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get Proxy delay
|
||||||
|
export async function getProxyDelay(
|
||||||
|
name: string,
|
||||||
|
url?: string
|
||||||
|
): Promise<{ delay: number }> {
|
||||||
|
const params = {
|
||||||
|
timeout: 3000,
|
||||||
|
url: url || "http://www.gstatic.com/generate_204",
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = await getAxios();
|
||||||
|
return instance.get(`/proxies/${encodeURIComponent(name)}/delay`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the Proxy Choose
|
/// Update the Proxy Choose
|
||||||
export async function updateProxy(group: string, proxy: string) {
|
export async function updateProxy(group: string, proxy: string) {
|
||||||
const instance = await getAxios();
|
const instance = await getAxios();
|
||||||
return instance.put(`/proxies/${group}`, { name: proxy });
|
return instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the Proxy infomation
|
/// Get the Proxy infomation
|
||||||
|
37
src/services/delay.ts
Normal file
37
src/services/delay.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { getProxyDelay } from "./api";
|
||||||
|
|
||||||
|
const hashKey = (name: string, group: string) => `${group ?? ""}::${name}`;
|
||||||
|
|
||||||
|
class DelayManager {
|
||||||
|
private cache = new Map<string, [number, number]>();
|
||||||
|
|
||||||
|
setDelay(name: string, group: string, delay: number) {
|
||||||
|
this.cache.set(hashKey(name, group), [Date.now(), delay]);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDelay(name: string, group: string) {
|
||||||
|
if (!name) return -1;
|
||||||
|
|
||||||
|
const result = this.cache.get(hashKey(name, group));
|
||||||
|
if (result && Date.now() - result[0] <= 18e5) {
|
||||||
|
return result[1];
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkDelay(name: string, group: string) {
|
||||||
|
let delay = -1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await getProxyDelay(name);
|
||||||
|
delay = result.delay;
|
||||||
|
} catch {
|
||||||
|
delay = 1e6; // error
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setDelay(name, group, delay);
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DelayManager();
|
Loading…
Reference in New Issue
Block a user