feat(alerts): metric-alert rule-management UI (Phase 2)

Completes metric-threshold alerting end-to-end: /metric-alert-rules
list/new/edit routes (mirroring log-scan-rules) with metric/comparator/
threshold fields, the workload scope picker, ToggleSwitch, and a
ConfirmDialog delete flow; an api.ts MetricAlertRule CRUD client; an
"Observe" nav entry; and a full metricalert.* i18n namespace (en/ru
parity). Create-form cooldown defaults to 300s to match the server.

Rules are now manageable in the WebUI; breaches already surface in the
per-app activity timeline and fire any configured event-trigger webhook.

Reviewed: typescript APPROVE (0 CRITICAL/HIGH).
This commit is contained in:
2026-05-29 14:34:01 +03:00
parent 2e26f555c5
commit 7576f54e76
7 changed files with 1927 additions and 1 deletions
+100
View File
@@ -17,6 +17,7 @@
"apps": "Приложения",
"eventTriggers": "Триггеры событий",
"logScanRules": "Лог-правила",
"metricAlertRules": "Метрик-алерты",
"triggers": "Триггеры",
"proxies": "Прокси",
"events": "События",
@@ -887,6 +888,105 @@
"disabled": "выключен"
}
},
"metricalert": {
"title": "Правила метрик-алертов",
"titleNew": "Создать новый алерт",
"titleSingular": "Правило алерта",
"lede": "Пороговые проверки, которые наблюдатель выполняет по выборкам CPU и памяти каждого запущенного контейнера. Когда выборка пересекает порог, правило записывается в event_log с указанной важностью, откуда триггеры событий подхватывают его и рассылают по настроенным вебхукам. Включено {enabled} из {total}.",
"ledeNew": "Выберите метрику, оператор сравнения и порог. Оставьте поле рабочей нагрузки пустым, чтобы создать глобальное правило для всех нагрузок, или ограничьте его одной нагрузкой.",
"stat": {
"total": "ВСЕГО",
"global": "ГЛОБАЛЬНЫЕ",
"workload": "НАГРУЗКА",
"enabled": "ВКЛЮЧЕНО"
},
"toolbar": {
"newButton": "Новый алерт",
"backToList": "К списку алертов"
},
"filter": {
"all": "ВСЕ",
"global": "ГЛОБАЛЬНЫЕ",
"workload": "НАГРУЗКА"
},
"empty": {
"heading": "Пока нет правил алертов",
"body": "Начните с глобального правила, например «CPU больше 80%», затем сузьте его, ограничив правило отдельной рабочей нагрузкой.",
"cta": "Создать первый алерт"
},
"list": {
"name": "Название",
"condition": "Условие",
"scope": "Область",
"severity": "Важность",
"status": "Статус",
"open": "Открыть"
},
"detail": {
"config": "Конфигурация",
"configSub": "id #{id} · область {scope}",
"dangerZone": "Опасная зона",
"dangerZoneSub": "Удаление правила алерта немедленно убирает его и прекращает срабатывания.",
"deleteButton": "Удалить алерт",
"deleteTitle": "Удалить правило алерта?",
"deleteMessage": "Правило «{name}» будет удалено немедленно и перестанет срабатывать."
},
"form": {
"name": "Название",
"namePlaceholder": "напр. Перегрузка CPU воркера",
"condition": "Условие",
"metric": "Метрика",
"comparator": "Оператор",
"threshold": "Порог",
"thresholdPlaceholder": "напр. 80",
"thresholdHintPercent": "Процент от лимита (0–100). Правило срабатывает, когда выборка пересекает этот порог.",
"thresholdHintBytes": "Абсолютные байты (напр. 536870912 для 512 МиБ). Правило срабатывает, когда выборка памяти пересекает этот порог.",
"matchShape": "Параметры срабатывания",
"matchShapeOpts": "ВАЖНОСТЬ · ЗАДЕРЖКА",
"severity": "Важность",
"cooldown": "Задержка (с)",
"cooldownHint": "Задержка действует на каждое правило и контейнер отдельно — одно правило на двух контейнерах работает независимо. Она ограничивает, как часто длительное превышение повторно пишется в event_log.",
"scope": "Область",
"scopeHint": "Правила, привязанные к нагрузке, применяются только к её контейнерам. Оставьте пустым, чтобы применить правило ко всем нагрузкам.",
"scopeGlobal": "Глобально (применяется ко всем нагрузкам)",
"scopePick": "Выбрать нагрузку…",
"scopePickTitle": "Выберите нагрузку",
"scopeClear": "Сделать глобальным",
"scopeSelected": "Нагрузка",
"scopeUnknown": "Неизвестная нагрузка",
"enabled": "Включено",
"enabledHint": "Отключённые правила остаются в таблице, но не срабатывают.",
"required": "ОБЯЗАТЕЛЬНО",
"optional": "НЕОБЯЗАТЕЛЬНО",
"submit": "Создать алерт",
"submitting": "Создаём…"
},
"metric": {
"cpu_percent": "CPU %",
"memory_percent": "Память %",
"memory_bytes": "Память (байты)"
},
"metricShort": {
"cpu": "CPU",
"memory": "Память"
},
"comparator": {
"gt": "больше чем",
"lt": "меньше чем"
},
"unit": {
"percent": "%",
"bytes": "байт"
},
"scope": {
"global": "глобально",
"workload": "нагрузка {id}"
},
"status": {
"enabled": "включено",
"disabled": "отключено"
}
},
"logscan": {
"title": "Правила сканирования логов",
"titleNew": "Новое правило",