70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
import hmac
|
|
import json
|
|
|
|
from aiohttp import web
|
|
|
|
from healthbot.alerts import alert_keyboard, alert_kind, format_alert
|
|
from healthbot.notify import notify
|
|
from healthbot.ui import esc
|
|
from utils.db import db
|
|
from utils.env import env
|
|
from utils.logging import logger
|
|
|
|
|
|
def _authorized(request: web.Request) -> bool:
|
|
expected = env.web.token.get_secret_value()
|
|
if not expected:
|
|
return True
|
|
given = request.headers.get("X-Alert-Token") or request.query.get("token") or ""
|
|
return hmac.compare_digest(given, expected)
|
|
|
|
|
|
async def handle_alert(request: web.Request) -> web.Response:
|
|
if not _authorized(request):
|
|
logger.warning(f"Alert without a valid token from {request.remote}")
|
|
return web.json_response({"error": "unauthorized"}, status=401)
|
|
|
|
raw = await request.text()
|
|
try:
|
|
payload = json.loads(raw)
|
|
except ValueError:
|
|
logger.warning(f"Alert is not JSON: {raw[:200]}")
|
|
return web.json_response({"error": "bad json"}, status=400)
|
|
|
|
if not isinstance(payload, dict):
|
|
return web.json_response({"error": "expected object"}, status=400)
|
|
|
|
kind = alert_kind(payload)
|
|
logger.info(f"Alert received: {kind} (level={payload.get('level')})")
|
|
|
|
if kind in db.settings.get("muted"):
|
|
return web.json_response({"ok": True, "muted": True})
|
|
|
|
try:
|
|
text = format_alert(payload)
|
|
markup = alert_keyboard(payload)
|
|
except Exception:
|
|
logger.exception("Alert formatting failed")
|
|
text = f"🔔 Алерт из Komodo (не разобрал формат)\n<pre>{esc(raw[:1000])}</pre>"
|
|
markup = None
|
|
|
|
await notify(text, markup)
|
|
return web.json_response({"ok": True})
|
|
|
|
|
|
async def handle_health(_: web.Request) -> web.Response:
|
|
return web.json_response({"status": "ok"})
|
|
|
|
|
|
async def run_web() -> web.AppRunner:
|
|
app = web.Application()
|
|
app.router.add_post("/komodo/alert", handle_alert)
|
|
app.router.add_get("/health", handle_health)
|
|
|
|
runner = web.AppRunner(app)
|
|
await runner.setup()
|
|
site = web.TCPSite(runner, env.web.host, env.web.port)
|
|
await site.start()
|
|
logger.info(f"Web: listening on {env.web.host}:{env.web.port}")
|
|
return runner
|