fix(ui): сохранять и проверять контекст отчёта статистики

This commit is contained in:
Denozordec committed 2026-09-23 20:30:11 +07:00
1 parent 94ad73212b
commit e05b3a38c8
5 files changed
+117 -20

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { statisticsContextError } from './statistics-context'
test('неверные параметры не превращаются в более широкий отчёт', () => {
for (const query of ['view=bad', 'planes=bad', 'dim=bad', 'tab=bad', 'pivotRow=bad', 'view=pivot&pivotRow=country&pivotCol=country', 'dim=users&tab=servers', 'serverId=NaN', 'serverId=1.5', 'serverId=0', 'asn=bad', 'country=Russia', 'country=RU&country=DE', 'userId=', 'service=']) assert.ok(statisticsContextError(new URLSearchParams(query)), query)
})
test('поддерживаемые значения и чужие параметры сохраняют совместимость', () => {
for (const query of ['', 'serverId=42&asn=64512&country=RU&iface=ether1', 'dim=users&tab=users', 'view=pivot&pivotRow=server&pivotCol=country&planes=all', 'keep=1']) assert.equal(statisticsContextError(new URLSearchParams(query)), null, query)
})
+29
View File
@@ -0,0 +1,29 @@
import { isStatisticsPivotDim, isStatisticsSliceKind } from './statistics-dims'
export const STATISTICS_CONTEXT_KEYS = ['dim', 'tab', 'view', 'planes', 'pivotRow', 'pivotCol', 'country', 'service', 'asn', 'serverId', 'userId', 'iface'] as const
export function statisticsContextError(params: URLSearchParams): string | null {
for (const key of STATISTICS_CONTEXT_KEYS) {
if (params.getAll(key).length > 1) return `Параметр ${key} указан несколько раз.`
const value = params.get(key)
if (value !== null && !value.trim()) return `Параметр ${key} пуст.`
}
for (const key of ['dim', 'tab']) {
const value = params.get(key)
if (value !== null && !isStatisticsSliceKind(value)) return 'Неизвестный разрез статистики.'
}
if (params.has('dim') && params.has('tab') && params.get('dim') !== params.get('tab')) return 'Параметры dim и tab задают разные разрезы.'
if (params.has('view') && !['explore', 'pivot'].includes(params.get('view')!)) return 'Неизвестный вид отчёта.'
if (params.has('planes') && !['unique', 'all'].includes(params.get('planes')!)) return 'Неизвестный режим учёта трафика.'
for (const key of ['pivotRow', 'pivotCol']) {
if (params.has(key) && !isStatisticsPivotDim(params.get(key)!)) return 'Неизвестное измерение сводки.'
}
if (params.get('view') === 'pivot' && (params.get('pivotRow') ?? 'country') === (params.get('pivotCol') ?? 'service')) return 'Выберите разные измерения строк и колонок.'
const serverId = params.get('serverId')?.trim()
if (serverId && (!/^\d+$/.test(serverId) || !Number.isSafeInteger(Number(serverId)) || Number(serverId) <= 0)) return 'ID сервера должен быть положительным целым числом.'
const asn = params.get('asn')?.trim()
if (asn && (!/^-?\d+$/.test(asn) || !Number.isSafeInteger(Number(asn)))) return 'ASN должен быть целым числом.'
const country = params.get('country')?.trim()
if (country && country.length !== 2) return 'Код страны должен состоять из двух символов.'
return null
}