Files
onGuard24/onguard24/ingress/grafana.py

59 lines
1.9 KiB
Python
Raw Normal View History

import json
import logging
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, Header, HTTPException, Request
from starlette.responses import Response
from onguard24.domain.entities import Alert, Severity
logger = logging.getLogger(__name__)
router = APIRouter(tags=["ingress"])
async def get_pool(request: Request):
return getattr(request.app.state, "pool", None)
@router.post("/ingress/grafana", status_code=202)
async def grafana_webhook(
request: Request,
pool=Depends(get_pool),
x_onguard_secret: str | None = Header(default=None, alias="X-OnGuard-Secret"),
):
settings = request.app.state.settings
if settings.grafana_webhook_secret and x_onguard_secret != settings.grafana_webhook_secret:
raise HTTPException(status_code=401, detail="unauthorized")
raw = await request.body()
if len(raw) > 1_000_000:
raise HTTPException(status_code=400, detail="body too large")
try:
body = json.loads(raw.decode() or "{}")
except json.JSONDecodeError:
body = {}
if pool is None:
logger.warning("ingress: database not configured, event not persisted")
return Response(status_code=202)
async with pool.acquire() as conn:
row = await conn.fetchrow(
"INSERT INTO ingress_events (source, body) VALUES ($1, $2::jsonb) RETURNING id",
"grafana",
json.dumps(body),
)
raw_id = row["id"] if row else None
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]
alert = Alert(
source="grafana",
title=title,
severity=Severity.WARNING,
payload=body,
received_at=datetime.now(timezone.utc),
)
await bus.publish_alert_received(alert, raw_payload_ref=raw_id)
return Response(status_code=202)