mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Tidy up proxy logs, add copy action for headers
This commit is contained in:
@ -25,6 +25,13 @@ import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
|
|||||||
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
|
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
export enum Page {
|
||||||
|
Home,
|
||||||
|
ProxySetup,
|
||||||
|
ProxyLogs,
|
||||||
|
Sender,
|
||||||
|
}
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
@ -49,7 +56,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: 36,
|
marginRight: 28,
|
||||||
},
|
},
|
||||||
hide: {
|
hide: {
|
||||||
display: "none",
|
display: "none",
|
||||||
@ -100,10 +107,20 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
listItemIcon: {
|
listItemIcon: {
|
||||||
minWidth: 42,
|
minWidth: 42,
|
||||||
},
|
},
|
||||||
|
titleHighlight: {
|
||||||
|
color: theme.palette.secondary.main,
|
||||||
|
marginRight: 4,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
interface Props {
|
||||||
|
children: React.ReactNode;
|
||||||
|
title: string;
|
||||||
|
page: Page;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Layout({ title, page, children }: Props): JSX.Element {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
@ -137,7 +154,10 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h5" noWrap>
|
<Typography variant="h5" noWrap>
|
||||||
Hetty://
|
<span className={title !== "" && classes.titleHighlight}>
|
||||||
|
Hetty://
|
||||||
|
</span>
|
||||||
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
@ -170,6 +190,7 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
button
|
button
|
||||||
component="a"
|
component="a"
|
||||||
key="home"
|
key="home"
|
||||||
|
selected={page === Page.Home}
|
||||||
className={classes.listItem}
|
className={classes.listItem}
|
||||||
>
|
>
|
||||||
<Tooltip title="Home">
|
<Tooltip title="Home">
|
||||||
@ -184,7 +205,8 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
<ListItem
|
<ListItem
|
||||||
button
|
button
|
||||||
component="a"
|
component="a"
|
||||||
key="proxy"
|
key="proxyLogs"
|
||||||
|
selected={page === Page.ProxyLogs}
|
||||||
className={classes.listItem}
|
className={classes.listItem}
|
||||||
>
|
>
|
||||||
<Tooltip title="Proxy">
|
<Tooltip title="Proxy">
|
||||||
@ -200,6 +222,7 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
button
|
button
|
||||||
component="a"
|
component="a"
|
||||||
key="sender"
|
key="sender"
|
||||||
|
selected={page === Page.Sender}
|
||||||
className={classes.listItem}
|
className={classes.listItem}
|
||||||
>
|
>
|
||||||
<Tooltip title="Sender">
|
<Tooltip title="Sender">
|
||||||
@ -214,7 +237,7 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
|||||||
</Drawer>
|
</Drawer>
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
<div className={classes.toolbar} />
|
<div className={classes.toolbar} />
|
||||||
{props.children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,10 @@ import {
|
|||||||
TableCell,
|
TableCell,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
Snackbar,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
const paddingX = 0;
|
const paddingX = 0;
|
||||||
@ -19,23 +22,35 @@ const useStyles = makeStyles((theme: Theme) => {
|
|||||||
paddingBottom: paddingY,
|
paddingBottom: paddingY,
|
||||||
verticalAlign: "top",
|
verticalAlign: "top",
|
||||||
border: "none",
|
border: "none",
|
||||||
|
whiteSpace: "nowrap" as any,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
"&:hover": {
|
||||||
|
color: theme.palette.secondary.main,
|
||||||
|
whiteSpace: "inherit" as any,
|
||||||
|
overflow: "inherit",
|
||||||
|
textOverflow: "inherit",
|
||||||
|
cursor: "copy",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return createStyles({
|
return createStyles({
|
||||||
|
root: {},
|
||||||
table: {
|
table: {
|
||||||
tableLayout: "fixed",
|
tableLayout: "fixed",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
keyCell: {
|
keyCell: {
|
||||||
...tableCell,
|
...tableCell,
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
width: "40%",
|
width: "40%",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
fontSize: ".75rem",
|
||||||
},
|
},
|
||||||
valueCell: {
|
valueCell: {
|
||||||
...tableCell,
|
...tableCell,
|
||||||
width: "60%",
|
width: "60%",
|
||||||
border: "none",
|
border: "none",
|
||||||
wordBreak: "break-all",
|
fontSize: ".75rem",
|
||||||
whiteSpace: "pre-wrap",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -46,23 +61,59 @@ interface Props {
|
|||||||
|
|
||||||
function HttpHeadersTable({ headers }: Props): JSX.Element {
|
function HttpHeadersTable({ headers }: Props): JSX.Element {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleClick = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const r = document.createRange();
|
||||||
|
r.selectNode(e.currentTarget);
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
window.getSelection().addRange(r);
|
||||||
|
document.execCommand("copy");
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = (event?: React.SyntheticEvent, reason?: string) => {
|
||||||
|
if (reason === "clickaway") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableContainer>
|
<div>
|
||||||
<Table className={classes.table} size="small">
|
<Snackbar open={open} autoHideDuration={3000} onClose={handleClose}>
|
||||||
<TableBody>
|
<Alert onClose={handleClose} severity="info">
|
||||||
{headers.map(({ key, value }, index) => (
|
Copied to clipboard.
|
||||||
<TableRow key={index}>
|
</Alert>
|
||||||
<TableCell component="th" scope="row" className={classes.keyCell}>
|
</Snackbar>
|
||||||
<code>{key}:</code>
|
<TableContainer className={classes.root}>
|
||||||
</TableCell>
|
<Table className={classes.table} size="small">
|
||||||
<TableCell className={classes.valueCell}>
|
<TableBody>
|
||||||
<code>{value}</code>
|
{headers.map(({ key, value }, index) => (
|
||||||
</TableCell>
|
<TableRow key={index}>
|
||||||
</TableRow>
|
<TableCell
|
||||||
))}
|
component="th"
|
||||||
</TableBody>
|
scope="row"
|
||||||
</Table>
|
className={classes.keyCell}
|
||||||
</TableContainer>
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<code>{key}:</code>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.valueCell} onClick={handleClick}>
|
||||||
|
<code>{value}</code>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
import { green, orange, red } from "@material-ui/core/colors";
|
import { Theme, withTheme } from "@material-ui/core";
|
||||||
|
import { orange, red } from "@material-ui/core/colors";
|
||||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||||
|
|
||||||
function HttpStatusIcon({ status }: { status: number }): JSX.Element {
|
interface Props {
|
||||||
|
status: number;
|
||||||
|
theme: Theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
function HttpStatusIcon({ status, theme }: Props): JSX.Element {
|
||||||
const style = { marginTop: "-.25rem", verticalAlign: "middle" };
|
const style = { marginTop: "-.25rem", verticalAlign: "middle" };
|
||||||
switch (Math.floor(status / 100)) {
|
switch (Math.floor(status / 100)) {
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
return <FiberManualRecordIcon style={{ ...style, color: green[400] }} />;
|
return (
|
||||||
|
<FiberManualRecordIcon
|
||||||
|
style={{ ...style, color: theme.palette.secondary.main }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return <FiberManualRecordIcon style={{ ...style, color: orange[400] }} />;
|
return (
|
||||||
|
<FiberManualRecordIcon style={{ ...style, color: orange["A400"] }} />
|
||||||
|
);
|
||||||
case 5:
|
case 5:
|
||||||
return <FiberManualRecordIcon style={{ ...style, color: red[400] }} />;
|
return <FiberManualRecordIcon style={{ ...style, color: red["A400"] }} />;
|
||||||
default:
|
default:
|
||||||
return <FiberManualRecordIcon style={style} />;
|
return <FiberManualRecordIcon style={style} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HttpStatusIcon;
|
export default withTheme(HttpStatusIcon);
|
||||||
|
@ -65,13 +65,13 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
|
|||||||
<div>
|
<div>
|
||||||
<Grid container item spacing={2}>
|
<Grid container item spacing={2}>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Box component={Paper} maxHeight="62vh" overflow="scroll">
|
<Box component={Paper}>
|
||||||
<RequestDetail request={{ method, url, proto, headers, body }} />
|
<RequestDetail request={{ method, url, proto, headers, body }} />
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
{response && (
|
{response && (
|
||||||
<Box component={Paper} maxHeight="62vh" overflow="scroll">
|
<Box component={Paper}>
|
||||||
<ResponseDetail response={response} />
|
<ResponseDetail response={response} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -57,7 +57,7 @@ function RequestDetail({ request }: Props): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Box mx={2} my={2}>
|
<Box p={2}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="overline"
|
variant="overline"
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
@ -79,7 +79,7 @@ function RequestDetail({ request }: Props): JSX.Element {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box m={2}>
|
<Box p={2}>
|
||||||
<HttpHeadersTable headers={headers} />
|
<HttpHeadersTable headers={headers} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -19,27 +19,13 @@ import CenteredPaper from "../CenteredPaper";
|
|||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
requestTitle: {
|
row: {
|
||||||
width: "calc(100% - 80px)",
|
"&:hover": {
|
||||||
fontSize: "1rem",
|
cursor: "pointer",
|
||||||
wordBreak: "break-all",
|
},
|
||||||
whiteSpace: "pre-wrap",
|
|
||||||
},
|
|
||||||
headersTable: {
|
|
||||||
tableLayout: "fixed",
|
|
||||||
width: "100%",
|
|
||||||
},
|
|
||||||
headerKeyCell: {
|
|
||||||
verticalAlign: "top",
|
|
||||||
width: "30%",
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
headerValueCell: {
|
|
||||||
width: "70%",
|
|
||||||
verticalAlign: "top",
|
|
||||||
wordBreak: "break-all",
|
|
||||||
whiteSpace: "pre-wrap",
|
|
||||||
},
|
},
|
||||||
|
/* Pseudo-class applied to the root element if `hover={true}`. */
|
||||||
|
hover: {},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -88,6 +74,7 @@ function RequestListTable({
|
|||||||
onLogClick,
|
onLogClick,
|
||||||
theme,
|
theme,
|
||||||
}: RequestListTableProps): JSX.Element {
|
}: RequestListTableProps): JSX.Element {
|
||||||
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<TableContainer
|
<TableContainer
|
||||||
component={Paper}
|
component={Paper}
|
||||||
@ -117,15 +104,15 @@ function RequestListTable({
|
|||||||
|
|
||||||
const rowStyle = {
|
const rowStyle = {
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
id === selectedReqLogId
|
id === selectedReqLogId && theme.palette.action.selected,
|
||||||
? theme.palette.action.selected
|
|
||||||
: "inherit",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={id}
|
key={id}
|
||||||
|
className={classes.row}
|
||||||
style={rowStyle}
|
style={rowStyle}
|
||||||
|
hover
|
||||||
onClick={() => onLogClick(id)}
|
onClick={() => onLogClick(id)}
|
||||||
>
|
>
|
||||||
<TableCell style={{ ...cellStyle, width: "100px" }}>
|
<TableCell style={{ ...cellStyle, width: "100px" }}>
|
||||||
|
@ -20,7 +20,7 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
|||||||
)?.value;
|
)?.value;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Box mx={2} my={2}>
|
<Box p={2}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="overline"
|
variant="overline"
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
@ -48,7 +48,7 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Box m={2}>
|
<Box p={2}>
|
||||||
<HttpHeadersTable headers={response.headers} />
|
<HttpHeadersTable headers={response.headers} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import SettingsEthernetIcon from "@material-ui/icons/SettingsEthernet";
|
|||||||
import SendIcon from "@material-ui/icons/Send";
|
import SendIcon from "@material-ui/icons/Send";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import Layout from "../components/Layout";
|
import Layout, { Page } from "../components/Layout";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@ -33,7 +33,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
function Index(): JSX.Element {
|
function Index(): JSX.Element {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout page={Page.Home} title="">
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<Box mb={4} width="60%">
|
<Box mb={4} width="60%">
|
||||||
<Typography variant="h2">
|
<Typography variant="h2">
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Box, Button, Typography } from "@material-ui/core";
|
import { Button, Typography } from "@material-ui/core";
|
||||||
import ListIcon from "@material-ui/icons/List";
|
import ListIcon from "@material-ui/icons/List";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import Layout from "../../components/Layout";
|
import Layout, { Page } from "../../components/Layout";
|
||||||
|
|
||||||
function Index(): JSX.Element {
|
function Index(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout page={Page.ProxySetup} title="Proxy setup">
|
||||||
<Box mb={2}>
|
|
||||||
<Typography variant="h5">Proxy setup</Typography>
|
|
||||||
</Box>
|
|
||||||
<Typography paragraph>Coming soon…</Typography>
|
<Typography paragraph>Coming soon…</Typography>
|
||||||
<Link href="/proxy/logs" passHref>
|
<Link href="/proxy/logs" passHref>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import { Typography, Box } from "@material-ui/core";
|
|
||||||
|
|
||||||
import LogsOverview from "../../../components/reqlog/LogsOverview";
|
import LogsOverview from "../../../components/reqlog/LogsOverview";
|
||||||
import Layout from "../../../components/Layout";
|
import Layout, { Page } from "../../../components/Layout";
|
||||||
|
|
||||||
function ProxyLogs(): JSX.Element {
|
function ProxyLogs(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout page={Page.ProxyLogs} title="Proxy logs">
|
||||||
<Box mb={2}>
|
|
||||||
<Typography variant="h5">Proxy logs</Typography>
|
|
||||||
</Box>
|
|
||||||
<LogsOverview />
|
<LogsOverview />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import { Box, Typography } from "@material-ui/core";
|
import { Box, Typography } from "@material-ui/core";
|
||||||
|
|
||||||
import Layout from "../../components/Layout";
|
import Layout, { Page } from "../../components/Layout";
|
||||||
|
|
||||||
function Index(): JSX.Element {
|
function Index(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout page={Page.Sender} title="Sender">
|
||||||
<Box mb={2}>
|
|
||||||
<Typography variant="h5">Sender</Typography>
|
|
||||||
</Box>
|
|
||||||
<Typography paragraph>Coming soon…</Typography>
|
<Typography paragraph>Coming soon…</Typography>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user