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