Display current status for each mode (#7097)

* display current status for each mode

* review changes

* update snapshots

* update tests

* [autofix.ci] apply automated fixes

* fix bug

* use type

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Matteo Luppi 2024-08-14 15:48:36 +02:00 committed by GitHub
parent 8f5540d073
commit 51b45b8599
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 218 additions and 211 deletions

View File

@ -5,6 +5,9 @@
.modes {
padding: 1em 2em;
overflow-y: auto;
height: 100%;
width: 100vw;
h3 {
margin-top: 30px;
@ -47,9 +50,10 @@
}
}
.mode-error {
.mode-status {
margin-left: 10px;
margin-top: 5px;
font-weight: 600;
}
.mode-local-input {
@ -81,13 +85,16 @@
margin: 0 5px;
}
.mode-reverse-servers {
display: flex;
flex-direction: column;
gap: 10px;
}
.mode-reverse-add-server {
margin-left: 10px;
margin-top: 15px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
cursor: pointer;
font-weight: 500;
@ -96,10 +103,4 @@
margin-right: 5px;
}
}
.mode-reverse-servers {
display: flex;
flex-direction: column;
gap: 10px;
}
}

View File

@ -1,7 +1,7 @@
import * as React from "react";
import { render } from "../test-utils";
import CaptureSetup from "../../components/CaptureSetup";
import { TStore } from "../ducks/tutils";
import { render } from "../../test-utils";
import CaptureSetup from "../../../components/Modes/CaptureSetup";
import { TStore } from "../../ducks/tutils";
test("CaptureSetup", async () => {
const store = TStore();

View File

@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CaptureSetup 1`] = `
<DocumentFragment>
<div
style="padding: 1em 2em;"
>
<h3>
mitmproxy is running.
</h3>
<p>
No flows have been recorded yet.
<br />
To start capturing traffic, please configure your settings in the Capture tab.
</p>
</div>
</DocumentFragment>
`;

View File

@ -28,6 +28,17 @@ exports[`RegularSpec 1`] = `
tabindex="0"
/>
</div>
<div
class="mode-status"
>
<div>
<div
class="text-success"
>
HTTP(S) proxy listening at 127.0.0.1:8080 and [::1]:8080.
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
@ -62,9 +73,13 @@ exports[`RegularSpec 2`] = `
/>
</div>
<div
class="mode-error text-danger"
class="mode-status"
>
port already in use
<div
class="text-danger"
>
port already in use
</div>
</div>
</div>
</div>

View File

@ -1,42 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CaptureSetup 1`] = `
<DocumentFragment>
<div
style="padding: 1em 2em;"
>
<h3>
mitmproxy is running.
</h3>
<p>
No flows have been recorded yet.
<br />
Configure your client to use one of the following proxy servers:
</p>
<ul
class="fa-ul"
>
<li>
<i
class="fa fa-li fa-check text-success"
/>
HTTP(S) proxy listening at 127.0.0.1:8080 and [::1]:8080.
</li>
<li>
<i
class="fa fa-li fa-exclamation text-error"
/>
Reverse proxy to example.com (reverse:example.com):
<br />
I failed somehow.
</li>
<li>
<i
class="fa fa-li fa-pause text-warning"
/>
SOCKS v5 proxy (socks5)
</li>
</ul>
</div>
</DocumentFragment>
`;

View File

@ -1,119 +0,0 @@
import * as React from "react";
import { useEffect, useRef } from "react";
import { useAppSelector } from "../ducks";
import { ServerInfo } from "../ducks/backendState";
import { formatAddress } from "../utils";
import QRCode from "qrcode";
export default function CaptureSetup() {
const servers = Object.values(
useAppSelector((state) => state.backendState.servers),
);
let configure_action_text;
if (servers.length === 0) {
configure_action_text = "";
} else if (servers.length === 1) {
configure_action_text =
"Configure your client to use the following proxy server:";
} else {
configure_action_text =
"Configure your client to use one of the following proxy servers:";
}
return (
<div style={{ padding: "1em 2em" }}>
<h3>mitmproxy is running.</h3>
<p>
No flows have been recorded yet.
<br />
{configure_action_text}
</p>
<ul className="fa-ul">
{servers.map((server) => (
<li key={server.full_spec}>
<ServerDescription {...server} />
</li>
))}
</ul>
{/*
<p>You can also start additional servers:</p>
<ul>
<li>TODO</li>
</ul>
*/}
</div>
);
}
export function ServerDescription({
description,
listen_addrs,
last_exception,
is_running,
full_spec,
wireguard_conf,
}: ServerInfo) {
const qrCode = useRef(null);
useEffect(() => {
if (wireguard_conf && qrCode.current)
QRCode.toCanvas(qrCode.current, wireguard_conf, {
margin: 0,
scale: 3,
});
}, [wireguard_conf]);
let listen_str;
const all_same_port =
listen_addrs.length === 1 ||
(listen_addrs.length === 2 &&
listen_addrs[0][1] === listen_addrs[1][1]);
const unbound = listen_addrs.every((addr) =>
["::", "0.0.0.0"].includes(addr[0]),
);
if (all_same_port && unbound) {
listen_str = formatAddress(["*", listen_addrs[0][1]]);
} else {
listen_str = listen_addrs.map(formatAddress).join(" and ");
}
description = description[0].toUpperCase() + description.substr(1);
let desc, icon;
if (last_exception) {
icon = "fa-exclamation text-error";
desc = (
<>
{description} ({full_spec}):
<br />
{last_exception}
</>
);
} else if (!is_running) {
icon = "fa-pause text-warning";
desc = (
<>
{description} ({full_spec})
</>
);
} else {
icon = "fa-check text-success";
desc = `${description} listening at ${listen_str}.`;
if (wireguard_conf) {
desc = (
<>
{desc}
<div className="wireguard-config">
<pre>{wireguard_conf}</pre>
<canvas ref={qrCode} />
</div>
</>
);
}
}
return (
<>
<i className={`fa fa-li ${icon}`} />
{desc}
</>
);
}

View File

@ -3,7 +3,7 @@ import Splitter from "./common/Splitter";
import FlowTable from "./FlowTable";
import FlowView from "./FlowView";
import { useAppSelector } from "../ducks";
import CaptureSetup from "./CaptureSetup";
import CaptureSetup from "./Modes/CaptureSetup";
import Modes from "./Modes";
import { Tab } from "../ducks/ui/tabs";

View File

@ -0,0 +1,99 @@
import * as React from "react";
import { useEffect, useRef } from "react";
import { ServerInfo } from "../../ducks/backendState";
import { formatAddress } from "../../utils";
import QRCode from "qrcode";
export default function CaptureSetup() {
return (
<div style={{ padding: "1em 2em" }}>
<h3>mitmproxy is running.</h3>
<p>
No flows have been recorded yet.
<br />
To start capturing traffic, please configure your settings in
the Capture tab.
</p>
</div>
);
}
function ServerDescription({
description,
listen_addrs,
is_running,
full_spec,
wireguard_conf,
type,
}: ServerInfo) {
const qrCode = useRef(null);
useEffect(() => {
if (wireguard_conf && qrCode.current)
QRCode.toCanvas(qrCode.current, wireguard_conf, {
margin: 0,
scale: 3,
});
}, [wireguard_conf]);
let listen_str;
const all_same_port =
listen_addrs.length === 1 ||
(listen_addrs.length === 2 &&
listen_addrs[0][1] === listen_addrs[1][1]);
const unbound = listen_addrs.every((addr) =>
["::", "0.0.0.0"].includes(addr[0]),
);
if (all_same_port && unbound) {
listen_str = formatAddress(["*", listen_addrs[0][1]]);
} else {
listen_str = listen_addrs.map(formatAddress).join(" and ");
}
description = description[0].toUpperCase() + description.substr(1);
let desc;
if (!is_running) {
desc = (
<>
<div className="text-warning">
{description} ({full_spec})
</div>
</>
);
} else {
desc = (
<>
{type === "local" ? (
<div className="text-success">{description} is active.</div>
) : (
<div className="text-success">
{description} listening at {listen_str}.
</div>
)}
{wireguard_conf && (
<div className="wireguard-config">
<pre>{wireguard_conf}</pre>
<canvas ref={qrCode} />
</div>
)}
</>
);
}
return <div>{desc}</div>;
}
export function ServerStatus({
error,
backendState,
}: {
error?: string;
backendState?: ServerInfo;
}) {
return (
<div className="mode-status">
{error ? (
<div className="text-danger">{error}</div>
) : (
backendState && <ServerDescription {...backendState} />
)}
</div>
);
}

View File

@ -4,17 +4,21 @@ import { useAppDispatch, useAppSelector } from "../../ducks";
import { setActive, setApplications } from "../../ducks/modes/local";
import ValueEditor from "../editors/ValueEditor";
import { getSpec, LocalState } from "../../modes/local";
import { ServerStatus } from "./CaptureSetup";
import { ServerInfo } from "../../ducks/backendState";
export default function Local() {
const serverState = useAppSelector((state) => state.modes.local);
const backendState = useAppSelector((state) => state.backendState.servers);
const servers = serverState.map((server) => {
const error =
server.error ||
backendState[getSpec(server)]?.last_exception ||
undefined;
return <LocalRow key={server.ui_id} server={server} error={error} />;
return (
<LocalRow
key={server.ui_id}
server={server}
backendState={backendState[getSpec(server)]}
/>
);
});
return (
@ -28,9 +32,17 @@ export default function Local() {
);
}
function LocalRow({ server, error }: { server: LocalState; error?: string }) {
function LocalRow({
server,
backendState,
}: {
server: LocalState;
backendState?: ServerInfo;
}) {
const dispatch = useAppDispatch();
const error = server.error || backendState?.last_exception || undefined;
return (
<div>
<ModeToggle
@ -50,7 +62,7 @@ function LocalRow({ server, error }: { server: LocalState; error?: string }) {
}
/>
</ModeToggle>
{error && <div className="mode-error text-danger">{error}</div>}
<ServerStatus error={error} backendState={backendState} />
</div>
);
}

View File

@ -4,17 +4,21 @@ import { useAppDispatch, useAppSelector } from "../../ducks";
import { setListenPort, setActive } from "../../ducks/modes/regular";
import ValueEditor from "../editors/ValueEditor";
import { getSpec, RegularState } from "../../modes/regular";
import { ServerInfo } from "../../ducks/backendState";
import { ServerStatus } from "./CaptureSetup";
export default function Regular() {
const serverState = useAppSelector((state) => state.modes.regular);
const backendState = useAppSelector((state) => state.backendState.servers);
const servers = serverState.map((server) => {
const error =
server.error ||
backendState[getSpec(server)]?.last_exception ||
undefined;
return <RegularRow key={server.ui_id} server={server} error={error} />;
return (
<RegularRow
key={server.ui_id}
server={server}
backendState={backendState[getSpec(server)]}
/>
);
});
return (
@ -31,15 +35,18 @@ export default function Regular() {
function RegularRow({
server,
error,
backendState,
}: {
server: RegularState;
error?: string;
backendState?: ServerInfo;
}) {
const dispatch = useAppDispatch();
server.listen_host && console.warn("TODO: implement listen_host");
const error = server.error || backendState?.last_exception || undefined;
return (
<div>
<ModeToggle
@ -63,7 +70,7 @@ function RegularRow({
}
/>
</ModeToggle>
{error && <div className="mode-error text-danger">{error}</div>}
<ServerStatus error={error} backendState={backendState} />
</div>
);
}

View File

@ -2,11 +2,13 @@ import * as React from "react";
import ReverseToggleRow from "./ReverseToggleRow";
import { useAppDispatch, useAppSelector } from "../../ducks";
import { addServer } from "../../ducks/modes/reverse";
import { getSpec } from "../../modes/reverse";
export default function Reverse() {
const dispatch = useAppDispatch();
const servers = useAppSelector((state) => state.modes.reverse);
const backendState = useAppSelector((state) => state.backendState.servers);
return (
<div>
@ -16,15 +18,19 @@ export default function Reverse() {
</p>
<div className="mode-reverse-servers">
{servers.map((server) => (
<ReverseToggleRow key={server.ui_id} server={server} />
<ReverseToggleRow
key={server.ui_id}
server={server}
backendState={backendState[getSpec(server)]}
/>
))}
</div>
<div
className="mode-reverse-add-server"
onClick={() => dispatch(addServer())}
>
<i className="fa fa-plus-square-o" aria-hidden="true"></i>Add
additional server
<div
className="mode-reverse-add-server"
onClick={() => dispatch(addServer())}
>
<i className="fa fa-plus-square-o" aria-hidden="true"></i>
Add additional server
</div>
</div>
</div>
);

View File

@ -13,12 +13,18 @@ import {
} from "../../ducks/modes/reverse";
import { ReverseProxyProtocols } from "../../backends/consts";
import { ReverseState } from "../../modes/reverse";
import { ServerStatus } from "./CaptureSetup";
import { ServerInfo } from "../../ducks/backendState";
interface ReverseToggleRowProps {
server: ReverseState;
backendState?: ServerInfo;
}
export default function ReverseToggleRow({ server }: ReverseToggleRowProps) {
export default function ReverseToggleRow({
server,
backendState,
}: ReverseToggleRowProps) {
const dispatch = useAppDispatch();
const protocols = Object.values(ReverseProxyProtocols);
@ -37,6 +43,8 @@ export default function ReverseToggleRow({ server }: ReverseToggleRowProps) {
await dispatch(removeServer(server));
};
const error = server.error || backendState?.last_exception || undefined;
return (
<div>
<ModeToggle
@ -99,9 +107,7 @@ export default function ReverseToggleRow({ server }: ReverseToggleRowProps) {
onClick={deleteServer}
></i>
</ModeToggle>
{server.error && (
<div className="mode-error text-danger">{server.error}</div>
)}
<ServerStatus error={error} backendState={backendState} />
</div>
);
}

View File

@ -3,18 +3,20 @@ import { ModeToggle } from "./ModeToggle";
import { useAppDispatch, useAppSelector } from "../../ducks";
import { getSpec, WireguardState } from "../../modes/wireguard";
import { setActive } from "../../ducks/modes/wireguard";
import { ServerInfo } from "../../ducks/backendState";
import { ServerStatus } from "./CaptureSetup";
export default function Wireguard() {
const serverState = useAppSelector((state) => state.modes.wireguard);
const backendState = useAppSelector((state) => state.backendState.servers);
const servers = serverState.map((server) => {
const error =
server.error ||
backendState[getSpec(server)]?.last_exception ||
undefined;
return (
<WireGuardRow key={server.ui_id} server={server} error={error} />
<WireGuardRow
key={server.ui_id}
server={server}
backendState={backendState[getSpec(server)]}
/>
);
});
@ -32,13 +34,15 @@ export default function Wireguard() {
function WireGuardRow({
server,
error,
backendState,
}: {
server: WireguardState;
error?: string;
backendState?: ServerInfo;
}) {
const dispatch = useAppDispatch();
const error = server.error || backendState?.last_exception || undefined;
return (
<div>
<ModeToggle
@ -49,7 +53,7 @@ function WireGuardRow({
>
Run WireGuard Server
</ModeToggle>
{error && <div className="mode-error text-danger">{error}</div>}
<ServerStatus error={error} backendState={backendState} />
</div>
);
}