mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import { formatSize } from "@yaakapp-internal/lib/formatSize";
|
|
import type { Flow } from "@yaakapp-internal/proxy-lib";
|
|
import { DetailsBanner, formatMillis, KeyValueRow, KeyValueRows } from "@yaakapp-internal/ui";
|
|
import { format } from "date-fns";
|
|
import type { HTMLAttributes, ReactElement, ReactNode } from "react";
|
|
import { utcTimestamp } from "../lib/flowViews";
|
|
import { folderOf } from "../lib/search";
|
|
import { AppIcon } from "./AppIcon";
|
|
|
|
/**
|
|
* Where a flow came from and how it travelled: in a proxy you didn't send the request, so the
|
|
* connection on each leg, the app and its process, timing, sizes and the flow's record are part
|
|
* of the answer.
|
|
*/
|
|
export function FlowInfo({ flow }: { flow: Flow }) {
|
|
const folder = folderOf(flow);
|
|
const clientPort = portOf(flow.clientAddr);
|
|
|
|
return (
|
|
<div className="overflow-auto h-full pb-4 gap-y-3 flex flex-col pr-0.5">
|
|
<Section title="Connection">
|
|
{[
|
|
row("remote", "Remote Address", flow.remoteAddr, { copy: true }),
|
|
row("client_version", "Client Version", flow.clientHttpVersion),
|
|
row("server_version", "Server Version", flow.upstreamHttpVersion),
|
|
row("tls", "TLS", flow.upstreamTlsVersion),
|
|
row("cipher", "Cipher", flow.upstreamTlsCipher),
|
|
row("client_port", "Client Port", clientPort),
|
|
]}
|
|
</Section>
|
|
<Section title="App">
|
|
{[
|
|
row(
|
|
"app",
|
|
"Name",
|
|
flow.clientName == null ? null : (
|
|
<span className="flex items-center gap-1.5">
|
|
{flow.clientPath != null && <AppIcon path={flow.clientPath} className="size-4" />}
|
|
{flow.clientName}
|
|
{folder != null && <span className="text-text-subtlest">{folder}</span>}
|
|
</span>
|
|
),
|
|
),
|
|
flow.clientBundleId != null
|
|
? row("bundle", "Bundle ID", flow.clientBundleId, { copy: true })
|
|
: row("path", "Path", flow.clientPath, { copy: true }),
|
|
row("pid", "PID", flow.clientPid == null ? null : `${flow.clientPid}`),
|
|
row("cwd", "Directory", flow.clientCwd, { copy: true }),
|
|
]}
|
|
</Section>
|
|
<Section title="Timing">
|
|
{[
|
|
row(
|
|
"started",
|
|
"Started",
|
|
format(utcTimestamp(flow.startedAt), "MMM d, yyyy, h:mm:ss.SSS a"),
|
|
),
|
|
row("headers", "Headers", ms(flow.elapsedHeadersMs)),
|
|
row("total", "Total", ms(flow.elapsedMs)),
|
|
]}
|
|
</Section>
|
|
<Section title="Size">
|
|
{[
|
|
row("request_size", "Request", formatSize(flow.requestSize)),
|
|
row("response_size", "Response", formatSize(flow.responseSize)),
|
|
]}
|
|
</Section>
|
|
<Section title="Record">
|
|
{[
|
|
row("number", "Number", `#${flow.number}`, { copy: true }),
|
|
row("id", "ID", flow.id, { copy: true }),
|
|
row("saved", "Saved As", flow.savedRequestId, { copy: true }),
|
|
]}
|
|
</Section>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** A row as `KeyValueRows` takes it */
|
|
type Row = ReactElement<HTMLAttributes<HTMLTableColElement>>;
|
|
|
|
function Section({ title, children }: { title: string; children: (Row | null)[] }) {
|
|
const rows = children.filter((r): r is Row => r != null);
|
|
if (rows.length === 0) return null;
|
|
return (
|
|
<DetailsBanner defaultOpen summary={<h2>{title}</h2>}>
|
|
<KeyValueRows>{rows}</KeyValueRows>
|
|
</DetailsBanner>
|
|
);
|
|
}
|
|
|
|
/** A row, or nothing when there's no value for it */
|
|
function row(
|
|
key: string,
|
|
label: string,
|
|
value: ReactNode | null | undefined,
|
|
opts: { copy?: boolean } = {},
|
|
): Row | null {
|
|
if (value == null || value === "") return null;
|
|
return (
|
|
<KeyValueRow
|
|
key={key}
|
|
labelColor="secondary"
|
|
label={label}
|
|
enableCopy={opts.copy}
|
|
copy={copyText}
|
|
>
|
|
{value}
|
|
</KeyValueRow>
|
|
);
|
|
}
|
|
|
|
function ms(value: number | null): string | null {
|
|
return value == null ? null : formatMillis(value);
|
|
}
|
|
|
|
/** The port of `127.0.0.1:54321` or `[::1]:54321` */
|
|
export function portOf(addr: string | null | undefined): string | null {
|
|
const port = addr?.match(/:(\d+)$/)?.[1];
|
|
return port ?? null;
|
|
}
|
|
|
|
function copyText(text: string) {
|
|
navigator.clipboard.writeText(text).catch(console.error);
|
|
}
|