release: v1.9.0 — IRM-алерты отдельно от инцидентов
Some checks failed
Deploy / deploy (push) Has been cancelled
CI / test (push) Successful in 37s

- Alembic 005: таблицы irm_alerts и incident_alert_links
- Модуль alerts: API/UI, Ack/Resolve, привязка к инциденту через alert_ids
- Вебхук Grafana: одна транзакция ingress + irm_alerts; разбор payload в grafana_payload
- По умолчанию инцидент из вебхука не создаётся (AUTO_INCIDENT_FROM_ALERT)
- Документация IRM_GRAFANA_PARITY.md, обновления IRM.md и CHANGELOG

Made-with: Cursor
This commit is contained in:
Alexandr
2026-04-03 15:26:38 +03:00
parent 3cb75eb7b7
commit a8ccf1d35c
19 changed files with 722 additions and 60 deletions

View File

@ -9,6 +9,7 @@ from starlette.responses import Response
from onguard24.domain.entities import Alert, Severity
from onguard24.grafana_sources import sources_by_slug, webhook_authorized
from onguard24.ingress.grafana_payload import extract_alert_row_from_grafana_body
logger = logging.getLogger(__name__)
router = APIRouter(tags=["ingress"])
@ -119,19 +120,38 @@ async def _grafana_webhook_impl(
logger.warning("ingress: database not configured, event not persisted")
return Response(status_code=202)
title_row, sev_row, labels_row, fp_row = extract_alert_row_from_grafana_body(body)
async with pool.acquire() as conn:
row = await conn.fetchrow(
"""
INSERT INTO ingress_events (source, body, org_slug, service_name)
VALUES ($1, $2::jsonb, $3, $4)
RETURNING id
""",
"grafana",
json.dumps(body),
stored_org_slug,
service_name,
)
raw_id = row["id"] if row else None
async with conn.transaction():
row = await conn.fetchrow(
"""
INSERT INTO ingress_events (source, body, org_slug, service_name)
VALUES ($1, $2::jsonb, $3, $4)
RETURNING id
""",
"grafana",
json.dumps(body),
stored_org_slug,
service_name,
)
raw_id = row["id"] if row else None
if raw_id is not None:
await conn.execute(
"""
INSERT INTO irm_alerts (
ingress_event_id, status, title, severity, source,
grafana_org_slug, service_name, labels, fingerprint
)
VALUES ($1, 'firing', $2, $3, 'grafana', $4, $5, $6::jsonb, $7)
""",
raw_id,
title_row or "",
sev_row,
stored_org_slug,
service_name,
json.dumps(labels_row),
fp_row,
)
bus = getattr(request.app.state, "event_bus", None)
if bus and raw_id is not None:
title = str(body.get("title") or body.get("ruleName") or "")[:500]