Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Failing after 1m31s
Docker images / frontend-image (push) Successful in 2m36s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / publish-release (push) Skipped
Считать payload отдельно от overlay GRE/ESP и mesh; вкладка Пути и KPI Wire из счётчиков iface. Co-authored-by: Cursor <[email protected]>
151 lines
6.0 KiB
TypeScript
151 lines
6.0 KiB
TypeScript
"use client"
|
|
|
|
import { useMemo } from "react"
|
|
import { type ColumnDef, getCoreRowModel, useReactTable } from "@tanstack/react-table"
|
|
import type { FlowTalkerDto } from "@mmapp/contracts/traffic-flow"
|
|
import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell"
|
|
import {
|
|
DATA_GRID_CELL_PAD,
|
|
DATA_GRID_CELL_PAD_FIRST,
|
|
DATA_GRID_CELL_PAD_LAST,
|
|
} from "@/components/data-grids/shared/data-grid-layout"
|
|
import { fmtRate } from "@/lib/fmt-rate"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
function formatBytes(n: number): string {
|
|
if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(2)} ГБ`
|
|
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)} МБ`
|
|
if (n >= 1000) return `${(n / 1000).toFixed(1)} КБ`
|
|
return `${n} Б`
|
|
}
|
|
|
|
function TrafficFlowsDataGrid({
|
|
rows,
|
|
emptyHint,
|
|
}: {
|
|
rows: FlowTalkerDto[]
|
|
emptyHint?: string
|
|
}) {
|
|
const columns = useMemo<ColumnDef<FlowTalkerDto>[]>(
|
|
() => [
|
|
{
|
|
id: "client",
|
|
accessorFn: (r) => r.clientName ?? "",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Клиент</span>,
|
|
cell: ({ row }) => <span className="text-xs">{row.original.clientName || "—"}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST },
|
|
},
|
|
{
|
|
id: "server",
|
|
accessorKey: "serverName",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">JH</span>,
|
|
cell: ({ row }) => <span className="text-sm font-medium">{row.original.serverName}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "src",
|
|
accessorKey: "src",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Src</span>,
|
|
cell: ({ row }) => (
|
|
<span className="font-mono text-xs">
|
|
{row.original.src}
|
|
{row.original.srcPort ? `:${row.original.srcPort}` : ""}
|
|
</span>
|
|
),
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "dst",
|
|
accessorKey: "dst",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Dst</span>,
|
|
cell: ({ row }) => (
|
|
<span className="font-mono text-xs">
|
|
{row.original.dst}
|
|
{row.original.dstPort ? `:${row.original.dstPort}` : ""}
|
|
</span>
|
|
),
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "app",
|
|
accessorFn: (r) => r.application ?? r.protoName,
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">App</span>,
|
|
cell: ({ row }) => (
|
|
<span className="flex min-w-0 flex-col gap-0.5 text-xs">
|
|
<span>{row.original.application ?? row.original.protoName}</span>
|
|
{row.original.category || row.original.service ? (
|
|
<span className="text-[10px] text-muted-foreground truncate">
|
|
{[row.original.category, row.original.service].filter(Boolean).join(" · ")}
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
),
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "proto",
|
|
accessorKey: "protoName",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Proto</span>,
|
|
cell: ({ row }) => <span className="text-xs">{row.original.protoName}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "rate",
|
|
accessorFn: (r) => r.bps,
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Скорость</span>,
|
|
cell: ({ row }) => <span className="text-xs tabular-nums">{fmtRate(row.original.bps / 1_000_000)}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "bytes",
|
|
accessorKey: "bytes",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Байты</span>,
|
|
cell: ({ row }) => <span className="text-xs tabular-nums">{formatBytes(row.original.bytes)}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "en",
|
|
accessorFn: (r) => r.enName ?? "",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">EN</span>,
|
|
cell: ({ row }) => <span className="text-xs">{row.original.enName || "—"}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "plane",
|
|
accessorFn: (r) => r.plane ?? "",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Плоскость</span>,
|
|
cell: ({ row }) => <span className="text-xs text-muted-foreground">{row.original.plane || "—"}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
|
|
},
|
|
{
|
|
id: "iface",
|
|
accessorKey: "inIface",
|
|
header: () => <span className="text-xs font-medium text-muted-foreground">Iface</span>,
|
|
cell: ({ row }) => <span className="font-mono text-xs text-muted-foreground">{row.original.inIface || "—"}</span>,
|
|
meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: cn(DATA_GRID_CELL_PAD_LAST) },
|
|
},
|
|
],
|
|
[],
|
|
)
|
|
|
|
const table = useReactTable({
|
|
data: rows,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getRowId: (row, i) => `${row.serverId}-${row.src}-${row.dst}-${row.proto}-${row.srcPort}-${row.dstPort}-${i}`,
|
|
})
|
|
|
|
return (
|
|
<DataGridShell
|
|
table={table}
|
|
recordCount={rows.length}
|
|
emptyMessage={
|
|
emptyHint
|
|
|| "Пока нет IPFIX. Поднимите wg-flow на хосте MM и подключите jump-host одним кликом."
|
|
}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export { TrafficFlowsDataGrid }
|