feat(proxy): finish proxy page ui and api support

This commit is contained in:
GyDi 2021-12-11 21:28:19 +08:00
parent 0a3c59450b
commit 5b3f63ef02
5 changed files with 221 additions and 1 deletions

View File

@ -18,6 +18,7 @@
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-router-dom": "^6.0.2",
"react-virtuoso": "^2.3.1",
"recoil": "^0.5.2"
},
"devDependencies": {

View File

@ -0,0 +1,142 @@
import { useState } from "react";
import { Virtuoso } from "react-virtuoso";
import {
Box,
Collapse,
Divider,
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import {
SendRounded,
ExpandLessRounded,
ExpandMoreRounded,
MyLocationRounded,
NetworkCheckRounded,
CheckCircleOutlineRounded,
} from "@mui/icons-material";
import services from "../services";
import type { ProxyItem, ProxyGroupItem } from "../services/proxy";
interface ItemProps {
proxy: ProxyItem;
selected: boolean;
onClick?: (name: string) => void;
}
const Item = ({ proxy, selected, onClick }: ItemProps) => {
return (
<ListItem sx={{ py: 0, pl: 4 }}>
<ListItemButton
selected={selected}
onClick={() => onClick?.(proxy.name)}
sx={{ borderRadius: 1, py: 0.5 }}
>
<ListItemText title={proxy.name} secondary={proxy.name} />
<ListItemIcon
sx={{ justifyContent: "flex-end", color: "primary.main" }}
>
{selected && <CheckCircleOutlineRounded sx={{ fontSize: 16 }} />}
</ListItemIcon>
</ListItemButton>
</ListItem>
);
};
interface Props {
group: ProxyGroupItem;
}
const ProxyGroup = ({ group }: Props) => {
const [open, setOpen] = useState(false);
const [now, setNow] = useState(group.now);
const proxies = group.all ?? [];
const onUpdate = async (name: string) => {
// can not call update
if (group.type !== "Selector") {
// Todo
// error Tips
return;
}
const oldValue = now;
try {
setNow(name);
await services.updateProxy(group.name, name);
} catch {
setNow(oldValue);
// Todo
// error tips
}
};
return (
<>
<ListItem button onClick={() => setOpen(!open)}>
<ListItemText
primary={group.name}
secondary={
<>
<SendRounded color="primary" sx={{ mr: 1, fontSize: 14 }} />
<span>{now}</span>
</>
}
secondaryTypographyProps={{
sx: { display: "flex", alignItems: "center" },
}}
/>
{open ? <ExpandLessRounded /> : <ExpandMoreRounded />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ pl: 4, pr: 3, my: 0.5 }}>
<IconButton size="small" title="location">
<MyLocationRounded />
</IconButton>
<IconButton size="small" title="check">
<NetworkCheckRounded />
</IconButton>
</Box>
{proxies.length >= 10 ? (
<Virtuoso
style={{ height: "400px", marginBottom: "4px" }}
totalCount={proxies.length}
itemContent={(index) => (
<Item
proxy={proxies[index]}
selected={proxies[index].name === now}
onClick={onUpdate}
/>
)}
/>
) : (
<List
component="div"
disablePadding
sx={{ maxHeight: "400px", overflow: "auto", mb: "4px" }}
>
{proxies.map((proxy) => (
<Item
key={proxy.name}
proxy={proxy}
selected={proxy.name === now}
onClick={onUpdate}
/>
))}
</List>
)}
<Divider variant="middle" />
</Collapse>
</>
);
};
export default ProxyGroup;

View File

@ -1,5 +1,33 @@
import { useEffect, useState } from "react";
import { Box, List, Typography } from "@mui/material";
import services from "../services";
import ProxyGroup from "../components/proxy-group";
import type { ProxyGroupItem } from "../services/proxy";
const ProxyPage = () => {
return <h1>Proxy</h1>;
const [groups, setGroups] = useState<ProxyGroupItem[]>([]);
useEffect(() => {
// Todo
// result cache
services.getProxyInfo().then((res) => {
setGroups(res.groups);
});
}, []);
return (
<Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}>
<Typography variant="h4" component="h1" sx={{ py: 2 }}>
Proxy Groups
</Typography>
<List sx={{ borderRadius: 1, boxShadow: 2 }}>
{groups.map((group) => (
<ProxyGroup key={group.name} group={group} />
))}
</List>
</Box>
);
};
export default ProxyPage;

View File

@ -1,5 +1,7 @@
import * as proxy from "./proxy";
import * as traffic from "./traffic";
export default {
...proxy,
...traffic,
};

47
src/services/proxy.ts Normal file
View File

@ -0,0 +1,47 @@
import axiosIns from "./base";
export interface ProxyItem {
name: string;
type: string;
udp: boolean;
history: {
time: string;
delay: number;
}[];
all?: string[];
now?: string;
}
export type ProxyGroupItem = Omit<ProxyItem, "all" | "now"> & {
all?: ProxyItem[];
now?: string;
};
/// Get the Proxy infomation
export async function getProxyInfo() {
const response = (await axiosIns.get("/proxies")) as any;
const results = (response?.proxies ?? {}) as Record<string, ProxyItem>;
const global = results["GLOBAL"] || results["global"];
const proxies = Object.values(results).filter((each) => each.all == null);
const groups = Object.values(results).filter(
(each) => each.name.toLocaleUpperCase() !== "GLOBAL" && each.all != null
) as ProxyGroupItem[];
groups.forEach((each) => {
// @ts-ignore
each.all = each.all?.map((item) => results[item]).filter((e) => e);
});
return {
global,
groups,
proxies,
};
}
/// Update the Proxy Choose
export async function updateProxy(group: string, proxy: string) {
return axiosIns.put(`/proxies/${group}`, { name: proxy });
}