Загрузить файлы в «alert-receiver/app»
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from fastapi import FastAPI, Header, HTTPException, Request, status
|
||||
|
||||
from app.config import settings
|
||||
from app.forwarder import forward_to_processor
|
||||
from app.models import NormalizedEvent, ProcessorForwardEnvelope, ReceiverAck
|
||||
from app.normalizer import normalize_zabbix_payload
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(name)s %(message)s",
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI(
|
||||
title="alert-receiver",
|
||||
version="0.1.0",
|
||||
description="Receives Zabbix webhooks and normalizes them for alert-processor.",
|
||||
)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_check() -> None:
|
||||
if settings.require_webhook_token and not settings.webhook_token:
|
||||
raise RuntimeError(
|
||||
"WEBHOOK_TOKEN is required, but not set. "
|
||||
"Set it in environment variables or in .env file."
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Startup configuration loaded: require_webhook_token=%s forward_to_processor=%s",
|
||||
settings.require_webhook_token,
|
||||
settings.forward_to_processor,
|
||||
)
|
||||
|
||||
def _extract_token(
|
||||
x_webhook_token: str | None,
|
||||
authorization: str | None,
|
||||
) -> str | None:
|
||||
if x_webhook_token:
|
||||
return x_webhook_token.strip()
|
||||
|
||||
if authorization:
|
||||
auth = authorization.strip()
|
||||
if auth.lower().startswith("bearer "):
|
||||
return auth[7:].strip()
|
||||
return auth
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _extract_token(
|
||||
x_webhook_token: str | None,
|
||||
authorization: str | None,
|
||||
) -> str | None:
|
||||
if x_webhook_token:
|
||||
return x_webhook_token.strip()
|
||||
|
||||
if authorization:
|
||||
auth = authorization.strip()
|
||||
if auth.lower().startswith("bearer "):
|
||||
return auth[7:].strip()
|
||||
return auth
|
||||
|
||||
return None
|
||||
|
||||
def _validate_token(
|
||||
x_webhook_token: str | None,
|
||||
authorization: str | None,
|
||||
) -> None:
|
||||
if not settings.require_webhook_token:
|
||||
return
|
||||
|
||||
provided = _extract_token(x_webhook_token, authorization)
|
||||
if not provided or provided != settings.webhook_token:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid or missing webhook token",
|
||||
)
|
||||
|
||||
@app.get("/health")
|
||||
async def health() -> dict[str, str]:
|
||||
return {
|
||||
"status": "ok",
|
||||
"service": settings.app_name,
|
||||
}
|
||||
|
||||
@app.post(
|
||||
"/webhook/zabbix",
|
||||
response_model=ReceiverAck,
|
||||
status_code=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
async def receive_zabbix_webhook(
|
||||
request: Request,
|
||||
x_webhook_token: str | None = Header(default=None),
|
||||
authorization: str | None = Header(default=None),
|
||||
x_correlation_id: str | None = Header(default=None),
|
||||
) -> ReceiverAck:
|
||||
_validate_token(x_webhook_token, authorization)
|
||||
|
||||
try:
|
||||
payload = await request.json()
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Invalid JSON payload: {exc}",
|
||||
) from exc
|
||||
|
||||
if not isinstance(payload, dict):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Payload must be a JSON object",
|
||||
)
|
||||
|
||||
correlation_id = x_correlation_id or str(uuid.uuid4())
|
||||
remote_addr = request.client.host if request.client else None
|
||||
|
||||
normalized_dict = normalize_zabbix_payload(
|
||||
payload=payload,
|
||||
correlation_id=correlation_id,
|
||||
remote_addr=remote_addr,
|
||||
)
|
||||
|
||||
event = NormalizedEvent.model_validate(normalized_dict)
|
||||
envelope = ProcessorForwardEnvelope(event=event)
|
||||
|
||||
logger.info(
|
||||
"Accepted Zabbix event: correlation_id=%s event_id=%s severity=%s host=%s trigger=%s",
|
||||
event.correlation_id,
|
||||
event.event_id,
|
||||
event.severity,
|
||||
event.host,
|
||||
event.trigger_name,
|
||||
)
|
||||
|
||||
forwarded = await forward_to_processor(envelope)
|
||||
|
||||
return ReceiverAck(
|
||||
accepted=True,
|
||||
correlation_id=event.correlation_id,
|
||||
forwarded_to_processor=forwarded,
|
||||
message="Webhook accepted",
|
||||
)
|
||||
Reference in New Issue
Block a user