fix(config): добавить новые зависимости и обновить конфигурацию компонентов
Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 2m37s
Docker images / frontend-image (push) Successful in 2m22s
Docker images / updater-image (push) Successful in 38s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s

This commit is contained in:
Denozordec committed 2026-06-30 21:30:40 +07:00
1 parent 009011a917
commit a1a9124f3d
80 files changed
+13310 -1587

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
import type { BgpSessionRow } from "@/lib/bgp/types"
export function fmtBgpNum(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`
return String(n)
}
export function rscBgpSnippet(s: BgpSessionRow): string {
return `/routing bgp connection\nadd name=peer-as${s.remoteAs} remote.address=${s.peerIp}/32 \\\n remote.as=${s.remoteAs} local.role=${s.type === "eBGP" ? "ebgp" : "ibgp"} \\\n output.filter-chain=export-filter input.filter=import-filter \\\n routing-table=main`
}
+38
View File
@@ -0,0 +1,38 @@
export type BgpState = "Established" | "Active" | "Idle" | "Connect" | "OpenSent" | "OpenConfirm"
export type BgpType = "eBGP" | "iBGP"
export type BgpAfi = "IPv4 Unicast" | "IPv6 Unicast" | "VPNv4 Unicast"
export interface BgpSessionRow {
id: string
serverId: string
serverLabel: string
serverSite: string
peerIp: string
remoteAs: number
localAs: number
routerId: string
description: string
state: BgpState
type: BgpType
afi: BgpAfi
uptime: string | null
holdTime: number
keepalive: number
prefixesRx: number
prefixesTx: number
prefixesActive: number
inputMessages: number
outputMessages: number
capabilities: string[]
lastError: string | null
}
export const BGP_AS_NAMES: Record<number, string> = {
8359: "МТС / Tele2",
13238: "Яндекс",
12389: "Ростелеком",
24940: "Hetzner",
6777: "AMS-IX",
1299: "Telia",
65001: "iBGP internal",
}
+40
View File
@@ -0,0 +1,40 @@
import type { Filter } from "@/components/reui/filters"
type FieldAccessor<T> = Record<string, (row: T) => unknown>
function matchFilter<T>(row: T, filter: Filter, accessors: FieldAccessor<T>): boolean {
const getValue = accessors[filter.field]
if (!getValue) return true
const value = getValue(row)
const vals = filter.values
const str = (v: unknown) => String(v ?? "").toLowerCase()
switch (filter.operator) {
case "is":
return vals.length > 0 && String(value) === String(vals[0])
case "isNot":
return vals.length === 0 || String(value) !== String(vals[0])
case "isAnyOf":
return vals.some((v) => String(value) === String(v))
case "isNotAnyOf":
return !vals.some((v) => String(value) === String(v))
case "contains":
return vals.some((v) => str(value).includes(str(v)))
case "doesNotContain":
return !vals.some((v) => str(value).includes(str(v)))
default:
return true
}
}
export function applyReuiFilters<T>(
rows: T[],
filters: Filter[],
accessors: FieldAccessor<T>,
): T[] {
if (filters.length === 0) return rows
return rows.filter((row) =>
filters.every((f) => matchFilter(row, f, accessors)),
)
}
+36
View File
@@ -0,0 +1,36 @@
import type { FilterFieldConfig } from "@/components/reui/filters"
export const BGP_FILTER_FIELDS: FilterFieldConfig[] = [
{
key: "state",
label: "Состояние",
type: "multiselect",
options: [
{ value: "Established", label: "Established" },
{ value: "Active", label: "Active" },
{ value: "Idle", label: "Idle" },
{ value: "Connect", label: "Connect" },
{ value: "OpenSent", label: "OpenSent" },
{ value: "OpenConfirm", label: "OpenConfirm" },
],
},
{
key: "type",
label: "Тип",
type: "multiselect",
options: [
{ value: "eBGP", label: "eBGP" },
{ value: "iBGP", label: "iBGP" },
],
},
{
key: "afi",
label: "AFI",
type: "multiselect",
options: [
{ value: "IPv4 Unicast", label: "IPv4 Unicast" },
{ value: "IPv6 Unicast", label: "IPv6 Unicast" },
{ value: "VPNv4 Unicast", label: "VPNv4 Unicast" },
],
},
]
+45
View File
@@ -0,0 +1,45 @@
import type { FilterFieldConfig } from "@/components/reui/filters"
import type { Server, ServerType } from "@/lib/data"
export const SERVER_FILTER_FIELDS: FilterFieldConfig[] = [
{
key: "country",
label: "Страна",
type: "multiselect",
searchable: true,
options: [
{ value: "RU", label: "RU" },
{ value: "DE", label: "DE" },
{ value: "NL", label: "NL" },
{ value: "SG", label: "SG" },
{ value: "FI", label: "FI" },
{ value: "US", label: "US" },
],
},
{
key: "status",
label: "Статус",
type: "multiselect",
options: [
{ value: "online", label: "Online" },
{ value: "degraded", label: "Degraded" },
{ value: "offline", label: "Offline" },
],
},
{
key: "type",
label: "Тип",
type: "multiselect",
options: [
{ value: "jump-host", label: "JumpHost" },
{ value: "exit-node", label: "Exit Node" },
{ value: "home-router", label: "Home Router" },
],
},
]
export const SERVER_FILTER_ACCESSORS = {
country: (s: Server) => s.country ?? "",
status: (s: Server) => s.status,
type: (s: Server) => s.type as ServerType,
}