mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Scaffold homepage, small style tweaks
This commit is contained in:
@ -2,6 +2,7 @@ const withCSS = require("@zeit/next-css");
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
|
||||
module.exports = withCSS({
|
||||
trailingSlash: true,
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
|
@ -6,7 +6,7 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"export": "next build && next export -o build"
|
||||
"export": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.2.0",
|
||||
|
91
admin/public/style.css
Normal file
91
admin/public/style.css
Normal file
@ -0,0 +1,91 @@
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold-Italic.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Bold-Italic.woff")
|
||||
format("woff");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Bold.woff")
|
||||
format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-ExtraBold-Italic.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-ExtraBold-Italic.woff")
|
||||
format("woff");
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-ExtraBold.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-ExtraBold.woff")
|
||||
format("woff");
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Italic.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Italic.woff")
|
||||
format("woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Medium-Italic.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Medium-Italic.woff")
|
||||
format("woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Medium.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Medium.woff")
|
||||
format("woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "JetBrains Mono";
|
||||
src: url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2")
|
||||
format("woff2"),
|
||||
url("https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Regular.woff")
|
||||
format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
}
|
@ -14,12 +14,13 @@ import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Box,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
import Link from "next/link";
|
||||
import MenuIcon from "@material-ui/icons/Menu";
|
||||
import HomeIcon from "@material-ui/icons/Home";
|
||||
import SettingsEthernetIcon from "@material-ui/icons/SettingsEthernet";
|
||||
import SendIcon from "@material-ui/icons/Send";
|
||||
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
|
||||
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
|
||||
import clsx from "clsx";
|
||||
@ -136,7 +137,7 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h5" noWrap>
|
||||
<span style={{ marginRight: 12 }}>🧑🔧</span>Hetty
|
||||
Hetty://
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
@ -164,22 +165,51 @@ export function Layout(props: { children: React.ReactNode }): JSX.Element {
|
||||
</div>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem button key="home" className={classes.listItem}>
|
||||
<Tooltip title="Home">
|
||||
<ListItemIcon className={classes.listItemIcon}>
|
||||
<HomeIcon />
|
||||
</ListItemIcon>
|
||||
</Tooltip>
|
||||
<ListItemText primary="Home" />
|
||||
</ListItem>
|
||||
<ListItem button key="proxy" className={classes.listItem}>
|
||||
<Tooltip title="Proxy">
|
||||
<ListItemIcon className={classes.listItemIcon}>
|
||||
<SettingsEthernetIcon />
|
||||
</ListItemIcon>
|
||||
</Tooltip>
|
||||
<ListItemText primary="Proxy" />
|
||||
</ListItem>
|
||||
<Link href="/" passHref>
|
||||
<ListItem
|
||||
button
|
||||
component="a"
|
||||
key="home"
|
||||
className={classes.listItem}
|
||||
>
|
||||
<Tooltip title="Home">
|
||||
<ListItemIcon className={classes.listItemIcon}>
|
||||
<HomeIcon />
|
||||
</ListItemIcon>
|
||||
</Tooltip>
|
||||
<ListItemText primary="Home" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
<Link href="/proxy/logs" passHref>
|
||||
<ListItem
|
||||
button
|
||||
component="a"
|
||||
key="proxy"
|
||||
className={classes.listItem}
|
||||
>
|
||||
<Tooltip title="Proxy">
|
||||
<ListItemIcon className={classes.listItemIcon}>
|
||||
<SettingsEthernetIcon />
|
||||
</ListItemIcon>
|
||||
</Tooltip>
|
||||
<ListItemText primary="Proxy" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
<Link href="/sender" passHref>
|
||||
<ListItem
|
||||
button
|
||||
component="a"
|
||||
key="sender"
|
||||
className={classes.listItem}
|
||||
>
|
||||
<Tooltip title="Sender">
|
||||
<ListItemIcon className={classes.listItemIcon}>
|
||||
<SendIcon />
|
||||
</ListItemIcon>
|
||||
</Tooltip>
|
||||
<ListItemText primary="Sender" />
|
||||
</ListItem>
|
||||
</Link>
|
||||
</List>
|
||||
</Drawer>
|
||||
<main className={classes.content}>
|
||||
|
@ -12,7 +12,10 @@ const monacoOptions = {
|
||||
type language = "html" | "typescript" | "json";
|
||||
|
||||
function editorDidMount() {
|
||||
return (window.MonacoEnvironment.getWorkerUrl = (moduleId, label) => {
|
||||
return ((window as any).MonacoEnvironment.getWorkerUrl = (
|
||||
moduleId,
|
||||
label
|
||||
) => {
|
||||
if (label === "json") return "/_next/static/json.worker.js";
|
||||
if (label === "html") return "/_next/static/html.worker.js";
|
||||
if (label === "javascript") return "/_next/static/ts.worker.js";
|
||||
@ -26,8 +29,10 @@ function languageForContentType(contentType: string): language {
|
||||
case "text/html":
|
||||
return "html";
|
||||
case "application/json":
|
||||
case "application/json; charset=utf-8":
|
||||
return "json";
|
||||
case "application/javascript":
|
||||
case "application/javascript; charset=utf-8":
|
||||
return "typescript";
|
||||
default:
|
||||
return;
|
||||
|
@ -9,25 +9,36 @@ import {
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
const paddingX = 0;
|
||||
const paddingY = theme.spacing(1) / 3;
|
||||
const tableCell = {
|
||||
paddingLeft: paddingX,
|
||||
paddingRight: paddingX,
|
||||
paddingTop: paddingY,
|
||||
paddingBottom: paddingY,
|
||||
verticalAlign: "top",
|
||||
border: "none",
|
||||
};
|
||||
return createStyles({
|
||||
table: {
|
||||
tableLayout: "fixed",
|
||||
width: "100%",
|
||||
},
|
||||
keyCell: {
|
||||
verticalAlign: "top",
|
||||
width: "30%",
|
||||
...tableCell,
|
||||
width: "40%",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
valueCell: {
|
||||
width: "70%",
|
||||
verticalAlign: "top",
|
||||
...tableCell,
|
||||
width: "60%",
|
||||
border: "none",
|
||||
wordBreak: "break-all",
|
||||
whiteSpace: "pre-wrap",
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
interface Props {
|
||||
headers: Array<{ key: string; value: string }>;
|
||||
@ -42,7 +53,7 @@ function HttpHeadersTable({ headers }: Props): JSX.Element {
|
||||
{headers.map(({ key, value }, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell component="th" scope="row" className={classes.keyCell}>
|
||||
<code>{key}</code>
|
||||
<code>{key}:</code>
|
||||
</TableCell>
|
||||
<TableCell className={classes.valueCell}>
|
||||
<code>{value}</code>
|
||||
|
@ -6,7 +6,7 @@ function HttpStatusIcon({ status }: { status: number }): JSX.Element {
|
||||
switch (Math.floor(status / 100)) {
|
||||
case 2:
|
||||
case 3:
|
||||
return <FiberManualRecordIcon style={{ ...style, color: green[600] }} />;
|
||||
return <FiberManualRecordIcon style={{ ...style, color: green[400] }} />;
|
||||
case 4:
|
||||
return <FiberManualRecordIcon style={{ ...style, color: orange[400] }} />;
|
||||
case 5:
|
||||
|
@ -51,6 +51,14 @@ function LogDetail({ requestId: id }: Props): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
if (!data.httpRequestLog) {
|
||||
return (
|
||||
<Alert severity="warning">
|
||||
Request <strong>{id}</strong> was not found.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
const { method, url, proto, headers, body, response } = data.httpRequestLog;
|
||||
|
||||
return (
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { gql, useQuery } from "@apollo/client";
|
||||
import { useState } from "react";
|
||||
import { Box, Typography, CircularProgress } from "@material-ui/core";
|
||||
@ -23,10 +24,17 @@ const HTTP_REQUEST_LOGS = gql`
|
||||
`;
|
||||
|
||||
function LogsOverview(): JSX.Element {
|
||||
const router = useRouter();
|
||||
const detailReqLogId = router.query.id as string;
|
||||
console.log(detailReqLogId);
|
||||
|
||||
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS);
|
||||
|
||||
const [detailReqLogId, setDetailReqLogId] = useState<string | null>(null);
|
||||
const handleLogClick = (reqId: string) => setDetailReqLogId(reqId);
|
||||
const handleLogClick = (reqId: string) => {
|
||||
router.push("/proxy/logs?id=" + reqId, undefined, {
|
||||
shallow: false,
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <CircularProgress />;
|
||||
@ -40,7 +48,11 @@ function LogsOverview(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Box mb={2}>
|
||||
<RequestList logs={logs} onLogClick={handleLogClick} />
|
||||
<RequestList
|
||||
logs={logs}
|
||||
selectedReqLogId={detailReqLogId}
|
||||
onLogClick={handleLogClick}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
{detailReqLogId && <LogDetail requestId={detailReqLogId} />}
|
||||
|
@ -67,7 +67,11 @@ function RequestDetail({ request }: Props): JSX.Element {
|
||||
</Typography>
|
||||
<Typography className={classes.requestTitle} variant="h6">
|
||||
{method} {decodeURIComponent(parsedUrl.pathname + parsedUrl.search)}{" "}
|
||||
<Typography component="span" color="textSecondary">
|
||||
<Typography
|
||||
component="span"
|
||||
color="textSecondary"
|
||||
style={{ fontFamily: "'JetBrains Mono', monospace" }}
|
||||
>
|
||||
{proto}
|
||||
</Typography>
|
||||
</Typography>
|
||||
@ -75,7 +79,9 @@ function RequestDetail({ request }: Props): JSX.Element {
|
||||
|
||||
<Divider />
|
||||
|
||||
<HttpHeadersTable headers={headers} />
|
||||
<Box m={2}>
|
||||
<HttpHeadersTable headers={headers} />
|
||||
</Box>
|
||||
|
||||
{body && <Editor content={body} contentType={contentType} />}
|
||||
</div>
|
||||
|
@ -6,23 +6,64 @@ import {
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
CircularProgress,
|
||||
Typography,
|
||||
Box,
|
||||
createStyles,
|
||||
makeStyles,
|
||||
Theme,
|
||||
withTheme,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import HttpStatusIcon from "./HttpStatusCode";
|
||||
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",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
interface Props {
|
||||
logs: Array<any>;
|
||||
selectedReqLogId?: string;
|
||||
onLogClick(requestId: string): void;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
function RequestList({ logs, onLogClick }: Props): JSX.Element {
|
||||
function RequestList({
|
||||
logs,
|
||||
onLogClick,
|
||||
selectedReqLogId,
|
||||
theme,
|
||||
}: Props): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<RequestListTable onLogClick={onLogClick} logs={logs} />
|
||||
<RequestListTable
|
||||
onLogClick={onLogClick}
|
||||
logs={logs}
|
||||
selectedReqLogId={selectedReqLogId}
|
||||
theme={theme}
|
||||
/>
|
||||
{logs.length === 0 && (
|
||||
<Box my={1}>
|
||||
<CenteredPaper>
|
||||
@ -36,12 +77,16 @@ function RequestList({ logs, onLogClick }: Props): JSX.Element {
|
||||
|
||||
interface RequestListTableProps {
|
||||
logs?: any;
|
||||
selectedReqLogId?: string;
|
||||
onLogClick(requestId: string): void;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
function RequestListTable({
|
||||
logs,
|
||||
selectedReqLogId,
|
||||
onLogClick,
|
||||
theme,
|
||||
}: RequestListTableProps): JSX.Element {
|
||||
return (
|
||||
<TableContainer
|
||||
@ -70,10 +115,21 @@ function RequestListTable({
|
||||
textOverflow: "ellipsis",
|
||||
} as any;
|
||||
|
||||
const rowStyle = {
|
||||
backgroundColor:
|
||||
id === selectedReqLogId
|
||||
? theme.palette.action.selected
|
||||
: "inherit",
|
||||
};
|
||||
|
||||
return (
|
||||
<TableRow key={id} onClick={() => onLogClick(id)}>
|
||||
<TableRow
|
||||
key={id}
|
||||
style={rowStyle}
|
||||
onClick={() => onLogClick(id)}
|
||||
>
|
||||
<TableCell style={{ ...cellStyle, width: "100px" }}>
|
||||
{method}
|
||||
<code>{method}</code>
|
||||
</TableCell>
|
||||
<TableCell style={{ ...cellStyle, maxWidth: "100px" }}>
|
||||
{origin}
|
||||
@ -85,7 +141,7 @@ function RequestListTable({
|
||||
{response && (
|
||||
<div>
|
||||
<HttpStatusIcon status={response.statusCode} />{" "}
|
||||
{response.status}
|
||||
<code>{response.status}</code>
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
@ -98,4 +154,4 @@ function RequestListTable({
|
||||
);
|
||||
}
|
||||
|
||||
export default RequestList;
|
||||
export default withTheme(RequestList);
|
||||
|
@ -34,7 +34,13 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
||||
>
|
||||
<HttpStatusIcon status={response.statusCode} />{" "}
|
||||
<Typography component="span" color="textSecondary">
|
||||
{response.proto}
|
||||
<Typography
|
||||
component="span"
|
||||
color="textSecondary"
|
||||
style={{ fontFamily: "'JetBrains Mono', monospace" }}
|
||||
>
|
||||
{response.proto}
|
||||
</Typography>
|
||||
</Typography>{" "}
|
||||
{response.status}
|
||||
</Typography>
|
||||
@ -42,7 +48,9 @@ function ResponseDetail({ response }: Props): JSX.Element {
|
||||
|
||||
<Divider />
|
||||
|
||||
<HttpHeadersTable headers={response.headers} />
|
||||
<Box m={2}>
|
||||
<HttpHeadersTable headers={response.headers} />
|
||||
</Box>
|
||||
|
||||
{response.body && (
|
||||
<Editor content={response.body} contentType={contentType} />
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createMuiTheme } from "@material-ui/core/styles";
|
||||
import grey from "@material-ui/core/colors/grey";
|
||||
import green from "@material-ui/core/colors/green";
|
||||
import teal from "@material-ui/core/colors/teal";
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
@ -9,7 +9,36 @@ const theme = createMuiTheme({
|
||||
main: grey[900],
|
||||
},
|
||||
secondary: {
|
||||
main: green[500],
|
||||
main: teal["A400"],
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
h2: {
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
fontWeight: 600,
|
||||
},
|
||||
h3: {
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
fontWeight: 600,
|
||||
},
|
||||
h4: {
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
fontWeight: 600,
|
||||
},
|
||||
h5: {
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
fontWeight: 600,
|
||||
},
|
||||
h6: {
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
overrides: {
|
||||
MuiTableCell: {
|
||||
stickyHeader: {
|
||||
backgroundColor: grey[900],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -22,7 +22,7 @@ function App({ Component, pageProps }: AppProps): JSX.Element {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>Hetty</title>
|
||||
<title>Hetty://</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||
|
@ -10,10 +10,15 @@ export default class MyDocument extends Document {
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<meta name="theme-color" content={theme.palette.primary.main} />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap"
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
@ -1,8 +1,81 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
createStyles,
|
||||
IconButton,
|
||||
makeStyles,
|
||||
Theme,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import SettingsEthernetIcon from "@material-ui/icons/SettingsEthernet";
|
||||
import SendIcon from "@material-ui/icons/Send";
|
||||
import Link from "next/link";
|
||||
|
||||
import Layout from "../components/Layout";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
titleHighlight: {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: "1.6rem",
|
||||
width: "60%",
|
||||
lineHeight: 2,
|
||||
marginBottom: theme.spacing(5),
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
function Index(): JSX.Element {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div>
|
||||
<h1>Hetty123</h1>
|
||||
</div>
|
||||
<Layout>
|
||||
<Box p={4}>
|
||||
<Box mb={4} width="60%">
|
||||
<Typography variant="h2">
|
||||
<span className={classes.titleHighlight}>Hetty://</span>
|
||||
<br />
|
||||
The simple HTTP toolkit for security research.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography className={classes.subtitle} paragraph>
|
||||
What if security testing was intuitive, powerful, and good looking?
|
||||
What if it was <strong>free</strong>, instead of $400 per year?{" "}
|
||||
<span className={classes.titleHighlight}>Hetty</span> is listening on{" "}
|
||||
<code>:8080</code>…
|
||||
</Typography>
|
||||
<Box>
|
||||
<Link href="/proxy" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<SettingsEthernetIcon />}
|
||||
>
|
||||
Setup proxy
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/proxy" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<SendIcon />}
|
||||
>
|
||||
Send HTTP requests
|
||||
</Button>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,29 @@
|
||||
import React from "react";
|
||||
import { Box, Button, Typography } from "@material-ui/core";
|
||||
import ListIcon from "@material-ui/icons/List";
|
||||
import Link from "next/link";
|
||||
|
||||
import Layout from "../../components/Layout";
|
||||
|
||||
function Index(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h1>Proxy123</h1>
|
||||
</div>
|
||||
<Layout>
|
||||
<Box mb={2}>
|
||||
<Typography variant="h5">Proxy setup</Typography>
|
||||
</Box>
|
||||
<Typography paragraph>Coming soon…</Typography>
|
||||
<Link href="/proxy/logs" passHref>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<ListIcon />}
|
||||
>
|
||||
View logs
|
||||
</Button>
|
||||
</Link>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
|
16
admin/src/pages/sender/index.tsx
Normal file
16
admin/src/pages/sender/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { Box, Typography } from "@material-ui/core";
|
||||
|
||||
import Layout from "../../components/Layout";
|
||||
|
||||
function Index(): JSX.Element {
|
||||
return (
|
||||
<Layout>
|
||||
<Box mb={2}>
|
||||
<Typography variant="h5">Sender</Typography>
|
||||
</Box>
|
||||
<Typography paragraph>Coming soon…</Typography>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
Reference in New Issue
Block a user