"use client" import { useMemo, useState } from "react" import { PageHeader } from "@/components/page-header" import { vxlanTunnels, servers } from "@/lib/data" import type { VxlanTunnel } from "@/lib/data" import { Frame, FramePanel } from "@/components/reui/frame" import { IconTile } from "@/components/reui/icon-tile" import { OpsPanel } from "@/components/ops-panel" import { DataPageCard } from "@/components/data-page-card" import { DataPageToolbar } from "@/components/data-page-toolbar" import { VxlanDataGrid } from "@/components/data-grids/vxlan-data-grid" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" import { NetworkIcon, PlusIcon, CopyIcon, CheckIcon, CodeXmlIcon, LayersIcon, } from "lucide-react" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetClose, } from "@/components/ui/sheet" // ─── helpers ────────────────────────────────────────────────────────────────── function serverFor(id: string) { return servers.find((s) => s.id === id) } // ─── RSC generator ─────────────────────────────────────────────────────────── function generateVxlanRsc(t: VxlanTunnel): string { const srv = serverFor(t.serverId) const lines: string[] = [] lines.push(`# VXLAN — ${t.name} · VNI ${t.vni}`) if (srv) lines.push(`# Сервер: ${srv.name} (${srv.host})`) lines.push(`# RouterOS 7.x · /interface/vxlan`) lines.push(``) lines.push(`/interface/vxlan/add \\`) lines.push(` name=${t.name} \\`) lines.push(` vni=${t.vni} \\`) lines.push(` port=${t.dstPort} \\`) lines.push(` vtep-mac-address=auto \\`) lines.push(` arp-proxy=${t.arpProxy ? "yes" : "no"} \\`) lines.push(` mac-learning=${t.macLearning ? "yes" : "no"} \\`) lines.push(` l2mtu=${t.l2mtu} \\`) if (t.comment) lines.push(` comment="${t.comment}" \\`) if (!t.enabled) lines.push(` disabled=yes \\`) lines.push(``) // FDB entries for remote VTEPs for (const vtep of t.remoteVteps) { lines.push(`/interface/vxlan/vteps/add \\`) lines.push(` interface=${t.name} \\`) lines.push(` remote-ip=${vtep}`) lines.push(``) } // Bridge lines.push(`# Добавить в bridge:`) lines.push(`/interface/bridge/port/add \\`) lines.push(` bridge=bridge-overlay \\`) lines.push(` interface=${t.name}`) return lines.join("\n") } // ─── Export Sheet ───────────────────────────────────────────────────────────── function ExportSheet({ open, tunnel, onClose }: { open: boolean; tunnel: VxlanTunnel | null; onClose: () => void }) { const [copied, setCopied] = useState(false) const code = useMemo(() => tunnel ? generateVxlanRsc(tunnel) : "", [tunnel]) function handleCopy() { navigator.clipboard.writeText(code).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000) }) } return ( { if (!v) onClose() }}>
Экспорт VXLAN RouterOS 7.x · /interface/vxlan + vteps
            {code.split("\n").map((line, i) => {
              const isComment = line.startsWith("#")
              const isCmd = /^\//.test(line.trimStart())
              const isParam = /^\s+[a-z]/.test(line)
              return (
                
                  {line}{"\n"}
                
              )
            })}
          
}>Закрыть
) } // ─── Export Sheet ───────────────────────────────────────────────────────────── export default function VxlanPage() { const [search, setSearch] = useState("") const [exportTunnel, setExportTunnel] = useState(null) const filtered = useMemo(() => { if (!search) return vxlanTunnels const q = search.toLowerCase() return vxlanTunnels.filter((t) => t.name.includes(q) || String(t.vni).includes(q) || t.vtepIp.includes(q) || (serverFor(t.serverId)?.name.toLowerCase().includes(q) ?? false) ) }, [search]) const upCount = vxlanTunnels.filter((t) => t.status === "up").length const vnis = new Set(vxlanTunnels.map((t) => t.vni)).size return (
Новый VXLAN } />
{/* KPI */}
{[ { label: "Туннелей", value: vxlanTunnels.length, icon: }, { label: "Активных", value: upCount, icon: }, { label: "Уникальных VNI", value: vnis, icon: }, { label: "Серверов", value: new Set(vxlanTunnels.map((t) => t.serverId)).size, icon: }, ].map((s) => (

{s.label}

{s.value}

))}
{/* Info banner */}

VXLAN — L2-over-L3 оверлей для RouterOS 7.x

Доступен с RouterOS 7.1+. VNI (Virtual Network Identifier) — уникальный идентификатор сегмента (0–16777215). Рекомендуется использовать совместно с WireGuard или GRE туннелями для шифрования.

{/* Table */} {/* Reference */}
{[ { title: "Создать VXLAN", lines: [ "/interface/vxlan/add \\", " name=vxlan-10 \\", " vni=10010 \\", " port=8472 \\", " arp-proxy=yes \\", " mac-learning=yes", ], }, { title: "Добавить VTEP", lines: [ "/interface/vxlan/vteps/add \\", " interface=vxlan-10 \\", " remote-ip=10.0.1.1", "", "/interface/vxlan/vteps/add \\", " interface=vxlan-10 \\", " remote-ip=10.0.2.1", ], }, { title: "Bridge + IP", lines: [ "/interface/bridge/add \\", " name=br-overlay", "", "/interface/bridge/port/add \\", " bridge=br-overlay \\", " interface=vxlan-10", "", "/ip/address/add \\", " address=10.100.0.1/24 \\", " interface=br-overlay", ], }, ].map((b) => (

{b.title}

                      {b.lines.join("\n")}
                    
))}
setExportTunnel(null)} />
) }