export type ServerStatus = "online" | "offline" | "degraded" export type ServerType = "jump-host" | "exit-node" | "home-router" export interface WanUplink { id: string name: string // "WAN1-RT" isp: string // "Rostelecom" iface: string // "ether1" ip: string // external IP maxDl: number // Mbps maxUl: number // Mbps } // ─── WireGuard ─────────────────────────────────────────────────────────────── export interface WireGuardPeer { publicKey: string allowedIps: string[] endpoint?: string // "1.2.3.4:13231" latestHandshake?: string // "2 минуты назад" transferRx?: number // bytes transferTx?: number // bytes persistent?: boolean comment?: string } export interface WireGuardInterface { id: string name: string // e.g. "wg-msk-spb" listenPort: number // default 13231 mtu: number // 1420 default in ROS 7.x peers: WireGuardPeer[] comment: string enabled: boolean status: "up" | "down" } export interface Server { id: string name: string host: string // management IP / API address model: string os: string site: string country: string // ISO 3166-1 alpha-2, e.g. "RU", "DE" asn: string type: ServerType enabled: boolean status: ServerStatus latency: number | null sessions: number comment?: string // RouterOS 7.x extras ipv6Address?: string // global or link-local wireGuardIfaces?: WireGuardInterface[] vrfNames?: string[] // VRF instances on this router rpkiEnabled?: boolean // home-router specific wanUplinks?: WanUplink[] lanSubnet?: string // e.g. "192.168.10.0/24" // snapshot / poll data (как GET /api/servers — последний poll MikroTik) uptime?: string cpuLoad?: number /** байты, поля RouterOS resource */ freeMemory?: number totalMemory?: number polledAt?: string } export interface FlowspecRule { id: string dstPrefix?: string srcPrefix?: string ipProto?: string dstPort?: string action: "rate-limit" | "discard" | "redirect" rate?: number // Mbps, for rate-limit comment?: string } export interface Filter { id: string name: string domains: number ips: number asns: number servers: string[] enabled: boolean updated: string communities: string[] gateway: string // RouterOS 7.x extras largeCommunities?: string[] // RFC 8092 format "AS:VALUE1:VALUE2" rpkiValid?: boolean // filter only RPKI-valid routes flowspecRules?: FlowspecRule[] } export interface Domain { id: string domain: string resolvedIp: string asn: string purpose: string filter: string updated: string enabled: boolean } export interface IpRange { id: string cidr: string asn: string country: string purpose: string filter: string updated: string enabled: boolean } export interface Asn { id: string asn: string org: string country: string prefixes: number filter: string updated: string enabled: boolean } export type FirewallAction = | "accept" | "drop" | "reject" | "mark-routing" | "mark-conn" | "masquerade" | "src-nat" | "dst-nat" | "fasttrack-connection" | "nfqueue" | "passthrough" | "return" | "add-src-to-address-list" | "add-dst-to-address-list" export interface FirewallRule { id: string chain: string action: FirewallAction | string proto: string src: string dst: string port: string iface: string comment: string enabled: boolean hits: number // RouterOS 7.x extras family?: "ip" | "ip6" | "any" tlsHost?: string // tls-host matcher (SNI) connRateLimit?: string // connection-rate e.g. "100/s" layer7Proto?: string // /ip firewall layer7-protocol nfqueueId?: number log?: boolean logPrefix?: string } export interface Backup { id: string server: string filename: string size: string created: string kind: "auto" | "manual" notes: string } // ─── VXLAN ─────────────────────────────────────────────────────────────────── export interface VxlanTunnel { id: string name: string // "vxlan-overlay-1" vni: number // 0–16777215 port: number // source port (default 0 = auto) dstPort: number // destination port (default 8472) serverId: string vtepIp: string // VTEP address on the server remoteVteps: string[] // remote VTEP IPs (unicast-flood) l2mtu: number arpProxy: boolean macLearning: boolean comment: string enabled: boolean status: "up" | "down" } // ─── VRF ───────────────────────────────────────────────────────────────────── export interface VrfInstance { id: string name: string // VRF name serverId: string interfaces: string[] // list of interfaces in this VRF routingTable: string // associated routing table comment: string } // ─── Container (RouterOS 7.4+) ─────────────────────────────────────────────── export interface RouterContainer { id: string name: string serverId: string image: string // e.g. "nginx:alpine" tag: string status: "running" | "stopped" | "error" envs: { key: string; value: string }[] mounts: { dst: string; src?: string }[] interfaces: string[] // veth interface names cmd?: string startOnBoot: boolean comment: string uptime?: string cpu?: number // CPU % 0-100 memMb?: number // MB } // ─── Certificate ───────────────────────────────────────────────────────────── export type CertUsage = "server" | "client" | "ca" | "crl-sign" export type CertStatus = "valid" | "expired" | "revoked" export interface RouterCertificate { id: string name: string serverId: string commonName: string sans: string[] issuedBy: string validFrom: string validUntil: string daysLeft: number keySize: number usage: CertUsage[] trusted: boolean status: CertStatus } export interface PingProbe { id: string srcServerId: string // which server sends the ping srcInterface?: string // optional interface name on source router name: string target: string filter: string rtt: number | null loss: number status: "up" | "warn" | "down" series: number[] enabled: boolean /** Показывать в блоке «Активные пробы» на дашборде (звезда в мониторинге) */ showOnDashboard?: boolean } export interface SystemEvent { id: string sev: "destructive" | "warning" | "info" title: string message: string when: string } function spark(n: number, base: number, range: number): number[] { let s = base * 1000 + range return Array.from({ length: n }, () => { s = (s * 9301 + 49297) % 233280 const r = s / 233280 return Math.max(1, Math.round(base + (r - 0.5) * range * 2)) }) } export const servers: Server[] = [ { id: "srv1", name: "mt-msk-core-01", host: "10.0.0.1", model: "CCR2004-1G-12S+2XS", os: "7.20.1", site: "MSK", country: "RU", asn: "AS65001", type: "jump-host", enabled: true, status: "online", latency: 12, sessions: 4, ipv6Address: "2a01:4f8:1c1c:a1b2::1", rpkiEnabled: true, vrfNames: ["mgmt", "bypass"], wireGuardIfaces: [ { id: "wg1", name: "wg-msk-spb", listenPort: 13231, mtu: 1420, comment: "MSK → SPB overlay", enabled: true, status: "up", peers: [ { publicKey: "SPBPublicKeyBase64AAAAAAAAAAAAAAAAAAAAAA=", allowedIps: ["10.210.0.2/32","192.168.20.0/24"], endpoint: "10.0.1.1:13231", latestHandshake: "2 мин назад", transferRx: 182_400_000, transferTx: 64_800_000, persistent: true }, ], }, { id: "wg2", name: "wg-msk-fra", listenPort: 13232, mtu: 1420, comment: "MSK → FRA overlay", enabled: true, status: "up", peers: [ { publicKey: "FRAPublicKeyBase64AAAAAAAAAAAAAAAAAAAAAA=", allowedIps: ["10.210.0.6/32"], endpoint: "10.0.2.1:13231", latestHandshake: "1 мин назад", transferRx: 421_000_000, transferTx: 210_000_000, persistent: true }, ], }, ], }, { id: "srv2", name: "mt-spb-edge-01", host: "10.0.1.1", model: "CCR2116-12G-4S+", os: "7.20.1", site: "SPB", country: "RU", asn: "AS65002", type: "exit-node", enabled: true, status: "online", latency: 28, sessions: 6, ipv6Address: "2a01:4f8:1c2c:b2c3::1", rpkiEnabled: true, wireGuardIfaces: [ { id: "wg3", name: "wg-spb-msk", listenPort: 13231, mtu: 1420, comment: "SPB → MSK overlay", enabled: true, status: "up", peers: [ { publicKey: "MSKPublicKeyBase64AAAAAAAAAAAAAAAAAAAAAA=", allowedIps: ["10.210.0.1/32"], endpoint: "10.0.0.1:13231", latestHandshake: "2 мин назад", transferRx: 64_800_000, transferTx: 182_400_000, persistent: true }, ], }, ], }, { id: "srv3", name: "mt-fra-edge-01", host: "10.0.2.1", model: "CCR2004-16G-2S+", os: "7.20.1", site: "FRA", country: "DE", asn: "AS65003", type: "exit-node", enabled: true, status: "online", latency: 41, sessions: 3, ipv6Address: "2a01:4f8:1c3c:c3d4::1", rpkiEnabled: true, wireGuardIfaces: [ { id: "wg4", name: "wg-fra-msk", listenPort: 13231, mtu: 1420, comment: "FRA → MSK overlay", enabled: true, status: "up", peers: [ { publicKey: "MSKPublicKeyBase64AAAAAAAAAAAAAAAAAAAAAA=", allowedIps: ["10.210.0.1/32"], endpoint: "10.0.0.1:13232", latestHandshake: "1 мин назад", transferRx: 210_000_000, transferTx: 421_000_000, persistent: true }, ], }, ], }, { id: "srv4", name: "mt-ams-edge-01", host: "10.0.3.1", model: "RB5009UG+S+IN", os: "7.19.3", site: "AMS", country: "NL", asn: "AS65004", type: "exit-node", enabled: true, status: "degraded", latency: 88, sessions: 2, }, { id: "srv5", name: "mt-sgp-edge-01", host: "10.0.4.1", model: "CCR2004-1G-12S+2XS", os: "7.14.2", site: "SGP", country: "SG", asn: "AS65005", type: "exit-node", enabled: false, status: "offline", latency: null, sessions: 0, }, { id: "srv6", name: "mt-ams-test-01", host: "10.0.3.2", model: "hEX S", os: "7.20.1", site: "AMS", country: "NL", asn: "AS65006", type: "jump-host", enabled: true, status: "online", latency: 92, sessions: 1, }, { id: "srv7", name: "mt-msk-lab-01", host: "10.0.0.7", model: "RB5009UG+S+IN", os: "7.20.1", site: "MSK", country: "RU", asn: "AS65007", type: "jump-host", enabled: true, status: "online", latency: 14, sessions: 2, vrfNames: ["lab"], }, { id: "home1", name: "home-msk-01", host: "192.168.10.1", model: "hEX S (RB760iGS)", os: "7.20.1", site: "MSK", country: "RU", asn: "", type: "home-router", enabled: true, status: "online", latency: 3, sessions: 0, comment: "MSK офис — основной роутер", lanSubnet: "192.168.10.0/24", wanUplinks: [ { id: "w1a", name: "WAN1-RT", isp: "Rostelecom", iface: "ether1", ip: "94.25.168.1", maxDl: 500, maxUl: 500 }, { id: "w1b", name: "WAN2-BL", isp: "Beeline", iface: "ether2", ip: "85.174.12.1", maxDl: 100, maxUl: 100 }, ], }, { id: "home2", name: "home-spb-01", host: "192.168.20.1", model: "RB5009UG+S+IN", os: "7.20.1", site: "SPB", country: "RU", asn: "", type: "home-router", enabled: true, status: "online", latency: 5, sessions: 0, comment: "SPB офис — основной роутер", lanSubnet: "192.168.20.0/24", wanUplinks: [ { id: "w2a", name: "WAN1-MTS", isp: "МТС", iface: "ether1", ip: "178.214.4.1", maxDl: 300, maxUl: 300 }, { id: "w2b", name: "WAN2-ERT", isp: "ЭР-Телеком", iface: "ether2", ip: "95.31.14.1", maxDl: 200, maxUl: 150 }, ], }, ] export const filters: Filter[] = [ { id: "f1", name: "youtube-bypass", domains: 1842, ips: 4218, asns: 12, servers: ["srv1","srv2","srv3","srv4"], enabled: true, updated: "12 мин назад", communities: ["65001:100","65001:200"], gateway: "10.0.0.1", largeCommunities: ["65001:100:1","65001:100:2"], rpkiValid: true, }, { id: "f2", name: "streaming-eu", domains: 612, ips: 2104, asns: 8, servers: ["srv2","srv3","srv4"], enabled: true, updated: "2 ч назад", communities: ["65001:200"], gateway: "10.0.1.1", largeCommunities: ["65001:200:1"], rpkiValid: true, }, { id: "f3", name: "cdn-bypass", domains: 4218, ips: 18211, asns: 22, servers: ["srv1","srv2","srv3","srv4","srv6"], enabled: true, updated: "1 ч назад", communities: ["65001:300","65002:100"], gateway: "10.0.0.1", flowspecRules: [ { id: "fs1", dstPrefix: "104.16.0.0/12", ipProto: "tcp", dstPort: "443", action: "rate-limit", rate: 100, comment: "CF rate-limit" }, ], }, { id: "f4", name: "social-block-school", domains: 84, ips: 412, asns: 4, servers: ["srv1","srv7"], enabled: false, updated: "3 дн назад", communities: ["65007:999"], gateway: "" }, { id: "f5", name: "office-direct", domains: 142, ips: 884, asns: 6, servers: ["srv1","srv2","srv3","srv4","srv6","srv7"], enabled: true, updated: "4 ч назад", communities: ["65001:400"], gateway: "10.0.0.1" }, { id: "f6", name: "gaming-low-latency", domains: 88, ips: 1212, asns: 14, servers: ["srv1","srv2","srv3","srv7"], enabled: true, updated: "24 мин назад", communities: ["65001:500","65002:200"], gateway: "10.0.2.1" }, ] export const domains: Domain[] = [ { id: "d1", domain: "youtube.com", purpose: "streaming", filter: "youtube-bypass", resolvedIp: "142.250.74.110", asn: "AS15169", updated: "12 мин", enabled: true }, { id: "d2", domain: "googlevideo.com", purpose: "streaming", filter: "youtube-bypass", resolvedIp: "142.250.74.142", asn: "AS15169", updated: "12 мин", enabled: true }, { id: "d3", domain: "ytimg.com", purpose: "streaming", filter: "youtube-bypass", resolvedIp: "172.217.16.46", asn: "AS15169", updated: "12 мин", enabled: true }, { id: "d4", domain: "netflix.com", purpose: "streaming", filter: "streaming-eu", resolvedIp: "54.246.79.9", asn: "AS2906", updated: "2 ч", enabled: true }, { id: "d5", domain: "nflxvideo.net", purpose: "streaming", filter: "streaming-eu", resolvedIp: "45.57.32.18", asn: "AS2906", updated: "2 ч", enabled: true }, { id: "d6", domain: "cloudflare.com", purpose: "cdn", filter: "cdn-bypass", resolvedIp: "104.16.132.229", asn: "AS13335", updated: "1 ч", enabled: true }, { id: "d7", domain: "akamaized.net", purpose: "cdn", filter: "cdn-bypass", resolvedIp: "23.46.18.94", asn: "AS20940", updated: "1 ч", enabled: true }, { id: "d8", domain: "fastly.net", purpose: "cdn", filter: "cdn-bypass", resolvedIp: "151.101.1.95", asn: "AS54113", updated: "1 ч", enabled: true }, { id: "d9", domain: "twitch.tv", purpose: "streaming", filter: "streaming-eu", resolvedIp: "52.223.241.72", asn: "AS16509", updated: "2 ч", enabled: false }, { id: "d10", domain: "discord.com", purpose: "voip", filter: "gaming-low-latency", resolvedIp: "162.159.135.232", asn: "AS13335", updated: "24 мин", enabled: true }, { id: "d11", domain: "steam.com", purpose: "gaming", filter: "gaming-low-latency", resolvedIp: "23.45.139.40", asn: "AS20940", updated: "24 мин", enabled: true }, { id: "d12", domain: "epicgames.com", purpose: "gaming", filter: "gaming-low-latency", resolvedIp: "34.205.247.51", asn: "AS16509", updated: "24 мин", enabled: true }, ] export const ipRanges: IpRange[] = [ { id: "i1", cidr: "142.250.0.0/15", purpose: "streaming", filter: "youtube-bypass", asn: "AS15169", country: "US", updated: "12 мин", enabled: true }, { id: "i2", cidr: "172.217.0.0/16", purpose: "streaming", filter: "youtube-bypass", asn: "AS15169", country: "US", updated: "12 мин", enabled: true }, { id: "i3", cidr: "104.16.0.0/12", purpose: "cdn", filter: "cdn-bypass", asn: "AS13335", country: "US", updated: "1 ч", enabled: true }, { id: "i4", cidr: "23.32.0.0/11", purpose: "cdn", filter: "cdn-bypass", asn: "AS20940", country: "US", updated: "1 ч", enabled: true }, { id: "i5", cidr: "151.101.0.0/16", purpose: "cdn", filter: "cdn-bypass", asn: "AS54113", country: "US", updated: "1 ч", enabled: true }, { id: "i6", cidr: "54.246.0.0/16", purpose: "streaming", filter: "streaming-eu", asn: "AS2906", country: "IE", updated: "2 ч", enabled: true }, { id: "i7", cidr: "45.57.0.0/16", purpose: "streaming", filter: "streaming-eu", asn: "AS2906", country: "NL", updated: "2 ч", enabled: true }, { id: "i8", cidr: "162.159.128.0/19", purpose: "voip", filter: "gaming-low-latency", asn: "AS13335", country: "US", updated: "24 мин", enabled: true }, { id: "i9", cidr: "52.223.240.0/20", purpose: "streaming", filter: "streaming-eu", asn: "AS16509", country: "IE", updated: "2 ч", enabled: false }, ] export const asns: Asn[] = [ { id: "a1", asn: "AS15169", org: "GOOGLE", country: "US", prefixes: 842, filter: "youtube-bypass", updated: "12 мин", enabled: true }, { id: "a2", asn: "AS13335", org: "CLOUDFLARENET", country: "US", prefixes: 1284, filter: "cdn-bypass", updated: "1 ч", enabled: true }, { id: "a3", asn: "AS16509", org: "AMAZON-02", country: "US", prefixes: 4128, filter: "streaming-eu", updated: "2 ч", enabled: true }, { id: "a4", asn: "AS20940", org: "AKAMAI-ASN1", country: "US", prefixes: 614, filter: "cdn-bypass", updated: "1 ч", enabled: true }, { id: "a5", asn: "AS54113", org: "FASTLY", country: "US", prefixes: 122, filter: "cdn-bypass", updated: "1 ч", enabled: true }, { id: "a6", asn: "AS2906", org: "AS-SSI (Netflix)", country: "US", prefixes: 88, filter: "streaming-eu", updated: "2 ч", enabled: true }, { id: "a7", asn: "AS32934", org: "FACEBOOK", country: "US", prefixes: 412, filter: "social-block-school",updated: "3 дн", enabled: false }, { id: "a8", asn: "AS8075", org: "MICROSOFT-CORP", country: "US", prefixes: 882, filter: "office-direct", updated: "4 ч", enabled: true }, ] export const firewallRules: FirewallRule[] = [ { id: "fw1", chain: "forward", action: "accept", proto: "tcp", src: "youtube-bypass", dst: "0.0.0.0/0", port: "443", iface: "wan-bgp", comment: "YouTube bypass traffic", enabled: true, hits: 188421 }, { id: "fw2", chain: "forward", action: "mark-routing", proto: "tcp", src: "10.10.0.0/24", dst: "cdn-bypass", port: "—", iface: "lan", comment: "CDN routing mark", enabled: true, hits: 4128821 }, { id: "fw3", chain: "srcnat", action: "masquerade", proto: "all", src: "10.10.0.0/16", dst: "0.0.0.0/0", port: "—", iface: "wan-msk", comment: "Main NAT rule", enabled: true, hits: 12889241 }, { id: "fw4", chain: "forward", action: "drop", proto: "tcp", src: "0.0.0.0/0", dst: "social-block-school", port: "—", iface: "lan-school", comment: "School social block", enabled: true, hits: 4218 }, { id: "fw5", chain: "input", action: "accept", proto: "tcp", src: "10.10.0.0/16", dst: "—", port: "22,443,8291",iface: "—", comment: "Management access", enabled: true, hits: 18412 }, { id: "fw6", chain: "input", action: "drop", proto: "tcp", src: "0.0.0.0/0", dst: "—", port: "22", iface: "wan-msk", comment: "Block SSH from WAN", enabled: true, hits: 882412 }, ] export const backups: Backup[] = [ { id: "b1", server: "mt-msk-core-01", filename: "mt-msk-core-01_2026-04-26_03-00.rsc", size: "124 КБ", created: "Сегодня, 03:00", kind: "auto", notes: "снапшот перед обновлением" }, { id: "b2", server: "mt-msk-core-01", filename: "mt-msk-core-01_2026-04-25_03-00.rsc", size: "124 КБ", created: "Вчера, 03:00", kind: "auto", notes: "" }, { id: "b3", server: "mt-spb-edge-01", filename: "mt-spb-edge-01_2026-04-26_03-00.rsc", size: "88 КБ", created: "Сегодня, 03:00", kind: "auto", notes: "" }, { id: "b4", server: "mt-fra-edge-01", filename: "mt-fra-edge-01_2026-04-26_manual.rsc",size: "92 КБ", created: "Сегодня, 14:18", kind: "manual", notes: "перед изменением BGP" }, { id: "b5", server: "mt-ams-edge-01", filename: "mt-ams-edge-01_2026-04-25_03-00.rsc", size: "64 КБ", created: "Вчера, 03:00", kind: "auto", notes: "" }, ] export const pingProbes: PingProbe[] = [ // srv1 — mt-msk-core-01 (JH, MSK) → основной мониторинг { id: "p1", srcServerId: "srv1", name: "youtube.com", target: "142.250.74.110", filter: "youtube-bypass", rtt: 18, loss: 0, status: "up", series: spark(40, 18, 4), enabled: true }, { id: "p2", srcServerId: "srv1", name: "cloudflare.com", target: "104.16.132.229", filter: "cdn-bypass", rtt: 4, loss: 0, status: "up", series: spark(40, 4, 1), enabled: true }, { id: "p3", srcServerId: "srv1", name: "netflix-eu", target: "54.246.79.9", filter: "streaming-eu", rtt: 28, loss: 0, status: "up", series: spark(40, 28, 5), enabled: true }, { id: "p4", srcServerId: "srv1", name: "fastly-edge", target: "151.101.1.95", filter: "cdn-bypass", rtt: 8, loss: 0, status: "up", series: spark(40, 8, 2), enabled: true }, // srv7 — mt-msk-lab-01 (JH, MSK) → лаборатория { id: "p5", srcServerId: "srv7", name: "discord-voip", target: "162.159.135.232", filter: "gaming-low-latency", rtt: 12, loss: 0, status: "up", series: spark(40, 12, 2), enabled: true }, { id: "p6", srcServerId: "srv7", name: "steam-cdn", target: "23.45.139.40", filter: "gaming-low-latency", rtt: 38, loss: 1.2, status: "warn", series: spark(40, 38, 12), enabled: true }, { id: "p7", srcServerId: "srv7", name: "akamai-edge", target: "23.46.18.94", filter: "cdn-bypass", rtt: 22, loss: 0, status: "up", series: spark(40, 22, 4), enabled: true }, // srv2 — mt-spb-edge-01 (EN, SPB) → исходящий трафик SPB { id: "p8", srcServerId: "srv2", name: "youtube.com", target: "142.250.74.110", filter: "youtube-bypass", rtt: 22, loss: 0, status: "up", series: spark(40, 22, 5), enabled: true }, { id: "p9", srcServerId: "srv2", name: "office-365", target: "104.47.0.22", filter: "office-direct", rtt: 31, loss: 0, status: "up", series: spark(40, 31, 6), enabled: false }, // srv3 — mt-fra-edge-01 (EN, FRA) → исходящий трафик Frankfurt { id: "p10", srcServerId: "srv3", name: "youtube.com", target: "142.250.74.110", filter: "youtube-bypass", rtt: 12, loss: 0, status: "up", series: spark(40, 12, 3), enabled: true }, { id: "p11", srcServerId: "srv3", name: "netflix-eu", target: "54.246.79.9", filter: "streaming-eu", rtt: 9, loss: 0, status: "up", series: spark(40, 9, 2), enabled: true }, // srv4 — mt-ams-edge-01 (EN, AMS, degraded) → деградировавший узел { id: "p12", srcServerId: "srv4", name: "cloudflare.com", target: "104.16.132.229", filter: "cdn-bypass", rtt: 88, loss: 3.4, status: "warn", series: spark(40, 88, 22), enabled: true }, { id: "p13", srcServerId: "srv4", name: "sgp-probe", target: "139.180.22.4", filter: "—", rtt: null,loss: 100, status: "down", series: Array(40).fill(0), enabled: true }, ] export const systemEvents: SystemEvent[] = [ { id: "e1", sev: "destructive", title: "BGP-сессия упала", message: "mt-sgp-edge-01 · пир 139.180.22.4 · простой 2ч", when: "2ч" }, { id: "e2", sev: "destructive", title: "Ошибка синхронизации фильтра", message: "cdn-bypass → mt-ams-edge-01 · таймаут", when: "14м" }, { id: "e3", sev: "warning", title: "Высокая задержка на AMS", message: "среднее 88мс · порог 60мс", when: "8м" }, { id: "e4", sev: "warning", title: "Бэкап просрочен", message: "mt-msk-lab-01 · последний 5д назад", when: "1ч" }, { id: "e5", sev: "info", title: "Обновление BGP завершено", message: "+412 префиксов · −188 отозвано", when: "12м" }, { id: "e6", sev: "info", title: "Фильтр youtube-bypass обновлён", message: "+88 доменов · ↑ 24 сервера", when: "12м" }, ] export const dashLatency = { msk: spark(60, 12, 4), spb: spark(60, 28, 6), fra: spark(60, 41, 9), ams: spark(60, 88, 18), } export const traffic = { rx: Array.from({ length: 60 }, (_, i) => Math.round(280 + Math.sin(i / 6) * 60 + ((i * 7919) % 31))), tx: Array.from({ length: 60 }, (_, i) => Math.round(220 + Math.sin(i / 5) * 50 + ((i * 6271) % 21))), } // ─── GRE ──────────────────────────────────────────────────────────────────── export type GreStatus = "up" | "down" | "degraded" export type IpsecEncAlg = "aes-128" | "aes-192" | "aes-256" export type IpsecAuthAlg = "sha1" | "sha256" | "sha512" export type IpsecDhGroup = "modp1024" | "modp2048" | "modp4096" | "ecp256" | "ecp384" | "ecp521" export type IkeVersion = "ikev1" | "ikev2" export type DscpMode = "inherit" | number export interface GreIpsec { secret: string // ipsec-secret → auto-creates peer+policy+proposal encAlg: IpsecEncAlg // proposal enc-algorithms authAlg: IpsecAuthAlg // proposal auth-algorithms dhGroup: IpsecDhGroup // proposal pfs-group / peer dh-group ikeVersion: IkeVersion // peer exchange-mode lifetime: string // proposal lifetime (e.g. "1d 00:00:00") pfs: boolean // perfect forward secrecy } export interface GreTunnel { id: string name: string // interface name, e.g. gre-msk-spb serverId: string // which MikroTik runs this tunnel localAddress: string // local GRE endpoint ("0.0.0.0" = auto) remoteAddress: string // remote GRE endpoint (required) localInnerIp: string // /ip address on this side, e.g. 10.200.0.1/30 remoteInnerIp: string // /ip address on remote side, e.g. 10.200.0.2/30 poolId: string // which IP pool the inner IPs come from ipsec: GreIpsec | null // null = no IPsec (plain GRE) mtu: number // default 1476 keepaliveInterval: number // seconds, 0 = disabled keepaliveRetries: number dscp: DscpMode // "inherit" or 0-63 clampTcpMss: boolean allowFastPath: boolean comment: string enabled: boolean status: GreStatus } export interface GrePool { id: string name: string cidr: string allocated: number total: number comment: string } const defaultIpsec = (secret: string, ikeVersion: IkeVersion = "ikev2"): GreIpsec => ({ secret, encAlg: "aes-256", authAlg: "sha256", dhGroup: "modp2048", ikeVersion, lifetime: "1d 00:00:00", pfs: true, }) export const grePools: GrePool[] = [ { id: "gp1", name: "pool-gre-core", cidr: "10.200.0.0/24", allocated: 16, total: 64, comment: "Ядровые межузловые туннели" }, { id: "gp2", name: "pool-gre-mgmt", cidr: "10.201.0.0/24", allocated: 8, total: 64, comment: "Управляющие туннели (lab, test)" }, ] export const greTunnels: GreTunnel[] = [ { id: "gre1", name: "gre-msk-spb", serverId: "srv1", localAddress: "10.0.0.1", remoteAddress: "10.0.1.1", localInnerIp: "10.200.0.1/30", remoteInnerIp: "10.200.0.2/30", poolId: "gp1", ipsec: defaultIpsec("xK9#mQ2$vLp8nR4"), mtu: 1476, keepaliveInterval: 10, keepaliveRetries: 10, dscp: "inherit", clampTcpMss: true, allowFastPath: true, comment: "MSK → SPB core interconnect", enabled: true, status: "up", }, { id: "gre2", name: "gre-msk-fra", serverId: "srv1", localAddress: "10.0.0.1", remoteAddress: "10.0.2.1", localInnerIp: "10.200.0.5/30", remoteInnerIp: "10.200.0.6/30", poolId: "gp1", ipsec: defaultIpsec("pW3@jT7!hD5kYe1"), mtu: 1476, keepaliveInterval: 10, keepaliveRetries: 10, dscp: "inherit", clampTcpMss: true, allowFastPath: true, comment: "MSK → FRA core interconnect", enabled: true, status: "up", }, { id: "gre3", name: "gre-msk-ams", serverId: "srv1", localAddress: "10.0.0.1", remoteAddress: "10.0.3.1", localInnerIp: "10.200.0.9/30", remoteInnerIp: "10.200.0.10/30", poolId: "gp1", ipsec: defaultIpsec("rN6%bF2*cM8sZa0"), mtu: 1476, keepaliveInterval: 10, keepaliveRetries: 10, dscp: "inherit", clampTcpMss: true, allowFastPath: false, comment: "MSK → AMS core interconnect", enabled: true, status: "degraded", }, { id: "gre4", name: "gre-msk-sgp", serverId: "srv1", localAddress: "10.0.0.1", remoteAddress: "10.0.4.1", localInnerIp: "10.200.0.13/30", remoteInnerIp: "10.200.0.14/30", poolId: "gp1", ipsec: defaultIpsec("qA4^eU9#wG1oXi6"), mtu: 1476, keepaliveInterval: 10, keepaliveRetries: 10, dscp: "inherit", clampTcpMss: true, allowFastPath: true, comment: "MSK → SGP core interconnect", enabled: false, status: "down", }, { id: "gre5", name: "gre-lab-core", serverId: "srv7", localAddress: "10.0.0.7", remoteAddress: "10.0.0.1", localInnerIp: "10.201.0.1/30", remoteInnerIp: "10.201.0.2/30", poolId: "gp2", ipsec: null, mtu: 1500, keepaliveInterval: 0, keepaliveRetries: 0, dscp: "inherit", clampTcpMss: false, allowFastPath: true, comment: "Lab → core (internal, no IPsec)", enabled: true, status: "up", }, { id: "gre6", name: "gre-ams-test", serverId: "srv6", localAddress: "10.0.3.2", remoteAddress: "10.0.3.1", localInnerIp: "10.201.0.5/30", remoteInnerIp: "10.201.0.6/30", poolId: "gp2", ipsec: null, mtu: 1476, keepaliveInterval: 5, keepaliveRetries: 5, dscp: 0, clampTcpMss: true, allowFastPath: true, comment: "AMS test → AMS edge", enabled: true, status: "up", }, ] // ─── Filter Rules (Community → Gateway per server) ──────────────────────────── export interface FilterRule { id: string community: string // BGP community, e.g. "65001:100" communityName?: string // optional human-readable name action?: "route" | "blackhole" // default "route"; "blackhole" → null-route (RTBH) gateway: string // next-hop IP for filter rule; empty for blackhole /** GRE: id туннеля / имя интерфейса; рекурсивный маршрут: `rec:` строки recursive_routes на сервере */ gatewayTunnelId: string description: string } export interface ServerFilterRuleset { serverId: string rules: FilterRule[] } export const serverFilterRulesets: ServerFilterRuleset[] = [ // srv1 — mt-msk-core-01 (jump-host, MSK): gre1=SPB, gre2=FRA, gre3=AMS(deg), gre4=SGP(down) { serverId: "srv1", rules: [ { id: "r1", community: "65001:100", communityName: "youtube-bypass", gateway: "10.200.0.6", gatewayTunnelId: "gre2", description: "YouTube/Google Video → Frankfurt (IX)" }, { id: "r2", community: "65001:200", communityName: "streaming-eu", gateway: "10.200.0.6", gatewayTunnelId: "gre2", description: "Netflix, Twitch EU → Frankfurt" }, { id: "r3", community: "65001:300", communityName: "cdn-bypass", gateway: "10.200.0.2", gatewayTunnelId: "gre1", description: "Cloudflare / Akamai / Fastly → SPB" }, { id: "r4", community: "65002:100", communityName: "cdn-secondary", gateway: "10.200.0.10", gatewayTunnelId: "gre3", description: "CDN резерв → Amsterdam (degraded)" }, { id: "r5", community: "65001:400", communityName: "office-direct", gateway: "10.200.0.2", gatewayTunnelId: "gre1", description: "MS Office 365 / Teams → SPB direct" }, { id: "r6", community: "65001:500", communityName: "gaming-ll", gateway: "10.200.0.2", gatewayTunnelId: "gre1", description: "Discord / Steam / Epic — низкая задержка → SPB" }, { id: "r10", community: "65535:666", communityName: "BLACKHOLE (RFC 7999)", action: "blackhole", gateway: "", gatewayTunnelId: "", description: "RTBH — null-route входящих маршрутов" }, { id: "r11", community: "65001:666", communityName: "blackhole-local", action: "blackhole", gateway: "", gatewayTunnelId: "", description: "Локальная блокировка — сброс трафика" }, ], }, // srv7 — mt-msk-lab-01 (jump-host, MSK lab): gre5=core { serverId: "srv7", rules: [ { id: "r7", community: "65001:100", communityName: "youtube-bypass", gateway: "10.201.0.2", gatewayTunnelId: "gre5", description: "Lab: YouTube → core" }, { id: "r8", community: "65007:999", communityName: "school-block", gateway: "10.201.0.2", gatewayTunnelId: "gre5", description: "Lab: школьная блокировка" }, ], }, // srv6 — mt-ams-test-01 (jump-host, AMS test): gre6=AMS edge { serverId: "srv6", rules: [ { id: "r9", community: "65001:300", communityName: "cdn-bypass", gateway: "10.201.0.6", gatewayTunnelId: "gre6", description: "AMS test: CDN → AMS edge" }, ], }, // Exit nodes — нет правил по умолчанию { serverId: "srv2", rules: [] }, { serverId: "srv3", rules: [] }, { serverId: "srv4", rules: [] }, { serverId: "srv5", rules: [] }, ] // ─── VXLAN mock data ───────────────────────────────────────────────────────── export const vxlanTunnels: VxlanTunnel[] = [ { id: "vx1", name: "vxlan-overlay-msk", vni: 10010, port: 0, dstPort: 8472, serverId: "srv1", vtepIp: "10.0.0.1", remoteVteps: ["10.0.1.1","10.0.2.1"], l2mtu: 1500, arpProxy: true, macLearning: true, comment: "MSK overlay — сегмент bypass", enabled: true, status: "up", }, { id: "vx2", name: "vxlan-overlay-spb", vni: 10010, port: 0, dstPort: 8472, serverId: "srv2", vtepIp: "10.0.1.1", remoteVteps: ["10.0.0.1","10.0.2.1"], l2mtu: 1500, arpProxy: true, macLearning: true, comment: "SPB overlay — сегмент bypass", enabled: true, status: "up", }, { id: "vx3", name: "vxlan-overlay-fra", vni: 10010, port: 0, dstPort: 8472, serverId: "srv3", vtepIp: "10.0.2.1", remoteVteps: ["10.0.0.1","10.0.1.1"], l2mtu: 1500, arpProxy: true, macLearning: false, comment: "FRA overlay — сегмент bypass", enabled: true, status: "up", }, { id: "vx4", name: "vxlan-mgmt-lab", vni: 20001, port: 0, dstPort: 4789, serverId: "srv7", vtepIp: "10.0.0.7", remoteVteps: ["10.0.0.1"], l2mtu: 1500, arpProxy: false, macLearning: true, comment: "Lab management VXLAN", enabled: true, status: "up", }, { id: "vx5", name: "vxlan-overlay-ams", vni: 10010, port: 0, dstPort: 8472, serverId: "srv4", vtepIp: "10.0.3.1", remoteVteps: ["10.0.0.1"], l2mtu: 1500, arpProxy: true, macLearning: true, comment: "AMS overlay (degraded)", enabled: true, status: "down", }, ] // ─── Container mock data ───────────────────────────────────────────────────── export const routerContainers: RouterContainer[] = [ { id: "c1", name: "adguard-msk", serverId: "srv1", image: "adguard/adguardhome", tag: "latest", status: "running", envs: [{ key: "AGH_CONFIG", value: "/opt/adguardhome/conf/AdGuardHome.yaml" }], mounts: [{ dst: "/opt/adguardhome/conf", src: "/disk1/adguard" }, { dst: "/opt/adguardhome/work", src: "/disk1/adguard-work" }], interfaces: ["veth-adguard"], startOnBoot: true, comment: "AdGuard Home — DNS фильтрация", uptime: "12д 4ч", cpu: 3, memMb: 64, }, { id: "c2", name: "prometheus-msk", serverId: "srv1", image: "prom/prometheus", tag: "v2.52.0", status: "running", envs: [], mounts: [{ dst: "/prometheus", src: "/disk1/prom-data" }], interfaces: ["veth-prom"], cmd: "--config.file=/etc/prometheus/prometheus.yml --storage.tsdb.retention.time=30d", startOnBoot: true, comment: "Prometheus метрики", uptime: "5д 11ч", cpu: 8, memMb: 256, }, { id: "c3", name: "grafana-msk", serverId: "srv1", image: "grafana/grafana", tag: "10.4.2", status: "running", envs: [{ key: "GF_SECURITY_ADMIN_PASSWORD", value: "*** (hidden)" }], mounts: [{ dst: "/var/lib/grafana", src: "/disk1/grafana" }], interfaces: ["veth-grafana"], startOnBoot: true, comment: "Grafana — дашборды мониторинга", uptime: "5д 11ч", cpu: 5, memMb: 128, }, { id: "c4", name: "wg-portal-spb", serverId: "srv2", image: "wgportal/wg-portal", tag: "v1.3.1", status: "running", envs: [{ key: "WG_PORTAL_ADMIN_USER", value: "admin" }], mounts: [{ dst: "/app/data", src: "/disk1/wgportal" }], interfaces: ["veth-wgportal"], startOnBoot: true, comment: "WireGuard Portal — управление клиентами", uptime: "3д 7ч", cpu: 1, memMb: 48, }, { id: "c5", name: "unbound-fra", serverId: "srv3", image: "mvance/unbound", tag: "1.19.3", status: "stopped", envs: [], mounts: [], interfaces: ["veth-unbound"], startOnBoot: false, comment: "Unbound DNS резолвер (в резерве)", cpu: 0, memMb: 0, }, ] // ─── Certificate mock data ──────────────────────────────────────────────────── export const routerCertificates: RouterCertificate[] = [ { id: "cert1", name: "mt-msk-core-01-server", serverId: "srv1", commonName: "mt-msk-core-01.routerlists.io", sans: ["mt-msk-core-01.routerlists.io","10.0.0.1"], issuedBy: "RouterLists Internal CA", validFrom: "2024-01-15", validUntil: "2026-01-15", daysLeft: 261, keySize: 4096, usage: ["server"], trusted: true, status: "valid", }, { id: "cert2", name: "routerlists-internal-ca", serverId: "srv1", commonName: "RouterLists Internal CA", sans: [], issuedBy: "RouterLists Internal CA (self-signed)", validFrom: "2023-06-01", validUntil: "2033-06-01", daysLeft: 2589, keySize: 4096, usage: ["ca","crl-sign"], trusted: true, status: "valid", }, { id: "cert3", name: "mt-spb-edge-01-server", serverId: "srv2", commonName: "mt-spb-edge-01.routerlists.io", sans: ["mt-spb-edge-01.routerlists.io","10.0.1.1"], issuedBy: "RouterLists Internal CA", validFrom: "2024-01-15", validUntil: "2026-01-15", daysLeft: 261, keySize: 4096, usage: ["server"], trusted: true, status: "valid", }, { id: "cert4", name: "mt-fra-edge-01-server", serverId: "srv3", commonName: "mt-fra-edge-01.routerlists.io", sans: ["mt-fra-edge-01.routerlists.io","10.0.2.1"], issuedBy: "Let's Encrypt", validFrom: "2026-01-10", validUntil: "2026-04-10", daysLeft: -19, keySize: 2048, usage: ["server"], trusted: true, status: "expired", }, { id: "cert5", name: "wg-client-admin", serverId: "srv1", commonName: "admin.routerlists.io", sans: ["admin.routerlists.io"], issuedBy: "RouterLists Internal CA", validFrom: "2025-06-01", validUntil: "2026-06-01", daysLeft: 33, keySize: 2048, usage: ["client"], trusted: true, status: "valid", }, { id: "cert6", name: "mt-ams-edge-01-server", serverId: "srv4", commonName: "mt-ams-edge-01.routerlists.io", sans: ["mt-ams-edge-01.routerlists.io"], issuedBy: "RouterLists Internal CA", validFrom: "2024-04-01", validUntil: "2026-05-15", daysLeft: 16, keySize: 2048, usage: ["server"], trusted: false, status: "valid", }, { id: "cert7", name: "mt-msk-lab-01-server", serverId: "srv7", commonName: "mt-msk-lab-01.routerlists.io", sans: ["mt-msk-lab-01.routerlists.io","10.0.0.7"], issuedBy: "RouterLists Internal CA", validFrom: "2025-09-01", validUntil: "2026-09-01", daysLeft: 125, keySize: 4096, usage: ["server"], trusted: true, status: "valid", }, ]