feat(alerts): алерты всей команде — детекторы, дедупликация, список (#15)
CI / ruff · mypy · pytest (pull_request) Successful in 4m49s
CI / ruff · mypy · pytest (pull_request) Successful in 4m49s
- детекторы по состоянию после каждого раунда снапшотов: краш, OOM, цикл рестартов, unhealthy, перегрузка ≥ 1 мин, мало места, Docker недоступен; пороги в config/alerts.yaml - AlertService: ключ kind+service, повтор в 10 мин — счётчик в исходном сообщении, «✅ восстановлено» ответом; разовые события (🛡) со счётчиком - рассылка всем активным (🛡 — только админам), темп ~25/с, RetryAfter, заблокировавший бота пропускается; alert_deliveries для правок и ответов - «🚨 Алерты»: активные + 7 дней по 10, карточка; кнопки «📜 Логи», «🩺 Статус» под алертом открывают новое сообщение, а не затирают алерт - 🛡 атаки через агента идут через общий сервис алертов - убраны заглушки «Раздел в разработке» — у всех разделов свои роутеры Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+36
-5
@@ -3,7 +3,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
|
||||
from app.db.models import AlertKind
|
||||
|
||||
|
||||
class _Strict(BaseModel):
|
||||
@@ -32,9 +34,6 @@ class HelpTexts(_Strict):
|
||||
|
||||
|
||||
class SectionTexts(_Strict):
|
||||
commit: str
|
||||
alerts: str
|
||||
members: str
|
||||
cancelled: str
|
||||
|
||||
|
||||
@@ -170,7 +169,6 @@ class LogsTexts(_Strict):
|
||||
agent_unavailable: str
|
||||
agent_not_understood: str
|
||||
head_agent: str
|
||||
attack_alert: str
|
||||
to_list: str
|
||||
to_container: str
|
||||
ask_grep: str
|
||||
@@ -190,6 +188,38 @@ class LogsTexts(_Strict):
|
||||
err_regex: str
|
||||
|
||||
|
||||
class AlertsTexts(_Strict):
|
||||
kinds: dict[AlertKind, str]
|
||||
details: dict[AlertKind, str]
|
||||
message: str
|
||||
repeats: str
|
||||
resolved: str
|
||||
logs_button: str
|
||||
status_button: str
|
||||
list_button: str
|
||||
list_title: str
|
||||
list_empty: str
|
||||
list_line: str
|
||||
active_mark: str
|
||||
resolved_mark: str
|
||||
page: str
|
||||
prev: str
|
||||
next: str
|
||||
card: str
|
||||
ongoing: str
|
||||
to_list: str
|
||||
host: str
|
||||
gone: str
|
||||
|
||||
@field_validator("kinds", "details")
|
||||
@classmethod
|
||||
def _every_kind(cls, value: dict[AlertKind, str]) -> dict[AlertKind, str]:
|
||||
missing = set(AlertKind) - set(value)
|
||||
if missing:
|
||||
raise ValueError(f"no text for {sorted(missing)}")
|
||||
return value
|
||||
|
||||
|
||||
class Texts(_Strict):
|
||||
menu: MenuTexts
|
||||
start: StartTexts
|
||||
@@ -199,6 +229,7 @@ class Texts(_Strict):
|
||||
commit: CommitTexts
|
||||
status: StatusTexts
|
||||
logs: LogsTexts
|
||||
alerts: AlertsTexts
|
||||
|
||||
|
||||
def load_texts(config_dir: Path) -> Texts:
|
||||
|
||||
Reference in New Issue
Block a user