Docker images / prepare-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 1m37s
Docker images / frontend-image (push) Successful in 1m50s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
"use client"
|
|
|
|
import type { WireGuardPeer } from "@/lib/data"
|
|
import { cn } from "@/lib/utils"
|
|
import {
|
|
ArrowDownIcon,
|
|
ArrowUpIcon,
|
|
KeyRoundIcon,
|
|
} from "lucide-react"
|
|
|
|
function fmtBytes(n: number | undefined): string {
|
|
if (!n) return "—"
|
|
if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)} ГБ`
|
|
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)} МБ`
|
|
if (n >= 1_000) return `${(n / 1_000).toFixed(0)} КБ`
|
|
return `${n} Б`
|
|
}
|
|
|
|
function truncKey(key: string): string {
|
|
if (key.length <= 20) return key
|
|
return `${key.slice(0, 8)}…${key.slice(-8)}`
|
|
}
|
|
|
|
function WireGuardPeersDetail({ peers }: { peers: WireGuardPeer[] }) {
|
|
if (peers.length === 0) {
|
|
return (
|
|
<div className="px-5 py-4 text-xs text-muted-foreground text-center border-t border-border/50">
|
|
Нет пиров
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="border-t border-border/50">
|
|
<div className="grid grid-cols-[1fr_1fr_auto_auto_auto] gap-3 px-5 py-1.5 bg-muted/10 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
|
|
<span>Public Key</span>
|
|
<span>Allowed IPs</span>
|
|
<span>Последнее рукопожатие</span>
|
|
<span>RX / TX</span>
|
|
<span>Endpoint</span>
|
|
</div>
|
|
{peers.map((peer) => (
|
|
<div
|
|
key={peer.publicKey}
|
|
className="grid grid-cols-[1fr_1fr_auto_auto_auto] gap-3 px-5 py-2.5 items-center text-xs border-t border-border/50 bg-muted/20"
|
|
>
|
|
<div className="flex items-center gap-1.5 min-w-0">
|
|
<KeyRoundIcon className="size-3 text-muted-foreground shrink-0" />
|
|
<span className="font-mono text-muted-foreground truncate" title={peer.publicKey}>
|
|
{truncKey(peer.publicKey)}
|
|
</span>
|
|
</div>
|
|
<div className="font-mono text-muted-foreground truncate">
|
|
{peer.allowedIps.join(", ")}
|
|
</div>
|
|
<span
|
|
className={cn(
|
|
"font-mono text-[11px] whitespace-nowrap",
|
|
peer.latestHandshake ? "text-emerald-600 dark:text-emerald-400" : "text-muted-foreground",
|
|
)}
|
|
>
|
|
{peer.latestHandshake ?? "нет рукопожатия"}
|
|
</span>
|
|
<div className="flex items-center gap-2 text-muted-foreground whitespace-nowrap">
|
|
<span className="flex items-center gap-0.5">
|
|
<ArrowDownIcon className="size-3 text-emerald-500" />
|
|
{fmtBytes(peer.transferRx)}
|
|
</span>
|
|
<span className="flex items-center gap-0.5">
|
|
<ArrowUpIcon className="size-3 text-blue-400" />
|
|
{fmtBytes(peer.transferTx)}
|
|
</span>
|
|
</div>
|
|
<span className="font-mono text-muted-foreground/60 text-[11px]">{peer.endpoint ?? "—"}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export { WireGuardPeersDetail, fmtBytes, truncKey }
|