feat: notify RD Gateway events in Telegram outside min_severity

Route rdg.connection.* through notify_rdg_connection and add disconnected type.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-01 08:14:37 +10:00
parent 7e13be3c38
commit bc77f82307
5 changed files with 52 additions and 2 deletions
+4
View File
@@ -21,11 +21,13 @@ from app.services.notify_dispatch import (
AUTH_LOGIN_SUCCESS_TYPES, AUTH_LOGIN_SUCCESS_TYPES,
DAILY_REPORT_EVENT_TYPES, DAILY_REPORT_EVENT_TYPES,
LIFECYCLE_EVENT_TYPE, LIFECYCLE_EVENT_TYPE,
RDG_CONNECTION_TYPES,
notify_auth_login, notify_auth_login,
notify_daily_report, notify_daily_report,
notify_event, notify_event,
notify_lifecycle, notify_lifecycle,
notify_problem, notify_problem,
notify_rdg_connection,
) )
router = APIRouter(prefix="/events", tags=["events"]) router = APIRouter(prefix="/events", tags=["events"])
@@ -79,6 +81,8 @@ def post_event(
notify_lifecycle(event, db=db) notify_lifecycle(event, db=db)
elif event.type in AUTH_LOGIN_SUCCESS_TYPES: elif event.type in AUTH_LOGIN_SUCCESS_TYPES:
notify_auth_login(event, db=db) notify_auth_login(event, db=db)
elif event.type in RDG_CONNECTION_TYPES:
notify_rdg_connection(event, db=db)
else: else:
notify_event(event, db=db) notify_event(event, db=db)
if problem is not None and problem_created: if problem is not None and problem_created:
+13
View File
@@ -15,6 +15,11 @@ logger = logging.getLogger(__name__)
LIFECYCLE_EVENT_TYPE = "agent.lifecycle" LIFECYCLE_EVENT_TYPE = "agent.lifecycle"
DAILY_REPORT_EVENT_TYPES = frozenset({"report.daily.ssh", "report.daily.rdp"}) DAILY_REPORT_EVENT_TYPES = frozenset({"report.daily.ssh", "report.daily.rdp"})
AUTH_LOGIN_SUCCESS_TYPES = frozenset({"rdp.login.success", "ssh.login.success"}) AUTH_LOGIN_SUCCESS_TYPES = frozenset({"rdp.login.success", "ssh.login.success"})
RDG_CONNECTION_TYPES = frozenset({
"rdg.connection.success",
"rdg.connection.disconnected",
"rdg.connection.failed",
})
def _event_telegram_via_agent(event: Event) -> bool: def _event_telegram_via_agent(event: Event) -> bool:
@@ -94,3 +99,11 @@ def notify_auth_login(event: Event, *, db: Session | None = None) -> None:
if not should_notify_event(event, db): if not should_notify_event(event, db):
return return
_dispatch_lifecycle_channels(event, db=db, policy=policy) _dispatch_lifecycle_channels(event, db=db, policy=policy)
def notify_rdg_connection(event: Event, *, db: Session | None = None) -> None:
"""RD Gateway 302/303 — ingest всегда; Telegram SAC вне min_severity (как auth login)."""
policy = get_effective_notification_policy(db)
if not should_notify_event(event, db):
return
_dispatch_lifecycle_channels(event, db=db, policy=policy)
+6 -2
View File
@@ -428,8 +428,12 @@ def format_event_telegram_html(event: Event) -> str:
def _format_rdg_html(event: Event) -> str: def _format_rdg_html(event: Event) -> str:
details = _details_dict(event) details = _details_dict(event)
ok = event.type.endswith(".success") if event.type.endswith(".success"):
header = "✅ RD Gateway: подключение" if ok else "❌ RD Gateway: ошибка" header = "✅ RD Gateway: подключение"
elif event.type.endswith(".disconnected"):
header = "️ RD Gateway: отключение"
else:
header = "❌ RD Gateway: ошибка"
msg = f"<b>{header}</b>\n" msg = f"<b>{header}</b>\n"
msg += _line("👤", "Пользователь", html_escape(_detail(details, "user", "username"))) msg += _line("👤", "Пользователь", html_escape(_detail(details, "user", "username")))
msg += _line("🏢", "Хост", host_label(event.host)) msg += _line("🏢", "Хост", host_label(event.host))
+27
View File
@@ -116,6 +116,33 @@ def test_notify_auth_login_bypasses_min_severity():
mock_tg.notify_event.assert_called_once() mock_tg.notify_event.assert_called_once()
def test_notify_rdg_connection_bypasses_min_severity():
event = Event(
event_id="00000000-0000-4000-8000-000000000603",
host_id=1,
occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc),
category="auth",
type="rdg.connection.success",
severity="info",
title="RD Gateway event 302",
summary="RDG 302 papatramp -> 192.168.160.3",
details={"telegram_via": "sac"},
payload={},
)
policy = NotificationPolicyConfig(
min_severity="warning",
use_telegram=True,
use_webhook=False,
use_email=False,
source="db",
)
with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy):
with patch.object(notify_dispatch, "should_notify_event", return_value=True):
with patch.object(notify_dispatch, "telegram_notify") as mock_tg:
notify_dispatch.notify_rdg_connection(event)
mock_tg.notify_event.assert_called_once()
def test_notify_auth_login_skips_telegram_when_via_agent(): def test_notify_auth_login_skips_telegram_when_via_agent():
event = Event( event = Event(
event_id="00000000-0000-4000-8000-000000000602", event_id="00000000-0000-4000-8000-000000000602",
+2
View File
@@ -99,6 +99,8 @@
"rdp.shadow.control.permission", "rdp.shadow.control.permission",
"winrm.session.started", "winrm.session.started",
"rdg.connection.success", "rdg.connection.success",
"rdg.connection.disconnected",
"rdg.connection.failed",
"report.daily.ssh", "report.daily.ssh",
"agent.heartbeat", "agent.heartbeat",
"agent.test" "agent.test"