feat(statistics): добавить раздел статистики трафика
Docker images / prepare-release (push) Successful in 10s
Docker images / backend-test (push) Successful in 2m19s
Docker images / frontend-image (push) Successful in 4m47s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m53s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 13s
Docker images / prepare-release (push) Successful in 10s
Docker images / backend-test (push) Successful in 2m19s
Docker images / frontend-image (push) Successful in 4m47s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m53s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 13s
Куб IPFIX час+день с AND-слайсами и экраном отчётности /statistics, живой /traffic не меняем. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+4
-1
@@ -233,7 +233,9 @@ export function permissionForPath(pathname: string): string | null {
|
||||
}
|
||||
if (pathname.startsWith("/bgp")) return "mm:bgp:read"
|
||||
if (pathname.startsWith("/uptime")) return "mm:uptime:read"
|
||||
if (pathname.startsWith("/traffic")) return "mm:traffic:read"
|
||||
if (pathname.startsWith("/traffic") || pathname.startsWith("/statistics")) {
|
||||
return "mm:traffic:read"
|
||||
}
|
||||
if (pathname.startsWith("/alerts")) return "mm:alerts:read"
|
||||
if (pathname.startsWith("/backups")) return "mm:backups:read"
|
||||
if (pathname.startsWith("/certificates")) return "mm:certificates:read"
|
||||
@@ -257,6 +259,7 @@ export function firstAllowedPath(): string {
|
||||
"/filters",
|
||||
"/uptime",
|
||||
"/traffic",
|
||||
"/statistics",
|
||||
"/alerts",
|
||||
"/backups",
|
||||
"/settings",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { FilterFieldConfig } from "@/components/reui/filters"
|
||||
|
||||
export const STATISTICS_FILTER_FIELDS: FilterFieldConfig[] = [
|
||||
{
|
||||
key: "country",
|
||||
label: "Страна",
|
||||
type: "text",
|
||||
placeholder: "ISO, напр. US",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
{
|
||||
key: "service",
|
||||
label: "Сервис",
|
||||
type: "text",
|
||||
placeholder: "https, dns…",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
{
|
||||
key: "asn",
|
||||
label: "ASN",
|
||||
type: "text",
|
||||
placeholder: "номер ASN",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
{
|
||||
key: "serverId",
|
||||
label: "Сервер",
|
||||
type: "text",
|
||||
placeholder: "id сервера",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
{
|
||||
key: "userId",
|
||||
label: "Пользователь",
|
||||
type: "text",
|
||||
placeholder: "id пользователя",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
{
|
||||
key: "iface",
|
||||
label: "Интерфейс",
|
||||
type: "text",
|
||||
placeholder: "ether1",
|
||||
defaultOperator: "is",
|
||||
},
|
||||
]
|
||||
@@ -13,6 +13,20 @@ export function fmtGB(v: number): string {
|
||||
return `${v.toFixed(1)} ГБ`
|
||||
}
|
||||
|
||||
export function formatBytes(n: number): string {
|
||||
if (!Number.isFinite(n) || n <= 0) return "0 Б"
|
||||
if (n >= 1_000_000_000_000) return `${(n / 1_000_000_000_000).toFixed(2)} ТБ`
|
||||
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 `${Math.round(n)} Б`
|
||||
}
|
||||
|
||||
/** API bitrate is bits/s; fmtRate expects Мбит/с. */
|
||||
export function fmtBps(bps: number): string {
|
||||
return fmtRate(bps / 1_000_000)
|
||||
}
|
||||
|
||||
export const TRAFFIC_RANGE_MINUTES: Record<string, number> = {
|
||||
"5m": 5,
|
||||
"15m": 15,
|
||||
|
||||
Reference in New Issue
Block a user