feat: show RDP session duration in Overview and Events (0.5.16)
Add session_duration_sec to EventSummary and a Duration column (HH:MM:SS / Nd HH:MM:SS).
This commit is contained in:
@@ -78,6 +78,7 @@ class EventSummary(BaseModel):
|
||||
title: str
|
||||
summary: str
|
||||
actor_user: str | None = None
|
||||
session_duration_sec: int | None = None
|
||||
rdg_flap: bool = False
|
||||
rdg_flap_pair_event_id: int | None = None
|
||||
rdg_flap_qwinsta_event_id: int | None = None
|
||||
|
||||
@@ -6,6 +6,7 @@ from app.services.event_actor_user import extract_event_actor_user
|
||||
from app.services.host_sessions import event_session_terminated
|
||||
from app.services.rdg_display import build_rdg_display
|
||||
from app.services.rdg_session_flap import resolve_rdg_flap_summary
|
||||
from app.services.session_duration import extract_session_duration_sec
|
||||
|
||||
|
||||
def event_to_summary(event: Event, db: Session | None = None) -> EventSummary:
|
||||
@@ -44,6 +45,9 @@ def event_to_summary(event: Event, db: Session | None = None) -> EventSummary:
|
||||
title=title,
|
||||
summary=summary,
|
||||
actor_user=extract_event_actor_user(event.type, event.details),
|
||||
session_duration_sec=extract_session_duration_sec(
|
||||
event.details if isinstance(event.details, dict) else None
|
||||
),
|
||||
rdg_flap=rdg_flap,
|
||||
rdg_flap_pair_event_id=rdg_flap_pair_event_id,
|
||||
rdg_flap_qwinsta_event_id=rdg_flap_qwinsta_event_id,
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Format and extract RDP/RDG session_duration_sec for UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def extract_session_duration_sec(details: dict[str, Any] | None) -> int | None:
|
||||
if not isinstance(details, dict):
|
||||
return None
|
||||
raw = details.get("session_duration_sec")
|
||||
if raw is None or raw == "":
|
||||
return None
|
||||
try:
|
||||
value = int(raw)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if value < 0:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def format_session_duration(seconds: int) -> str:
|
||||
"""
|
||||
Human-readable session length for lists:
|
||||
- under 24h → HH:MM:SS (05:05:24)
|
||||
- 24h+ → Nд HH:MM:SS (1д 00:01:25)
|
||||
"""
|
||||
if seconds < 0:
|
||||
return "—"
|
||||
days, rem = divmod(int(seconds), 86_400)
|
||||
hours, rem = divmod(rem, 3600)
|
||||
minutes, secs = divmod(rem, 60)
|
||||
clock = f"{hours:02d}:{minutes:02d}:{secs:02d}"
|
||||
if days:
|
||||
return f"{days}д {clock}"
|
||||
return clock
|
||||
@@ -564,9 +564,11 @@ def _format_rdg_html(event: Event) -> str:
|
||||
err = _detail(details, "gateway_error_code", "error_code", default="")
|
||||
if err != "-":
|
||||
msg += _line("⚠️", "Код ошибки", html_escape(err))
|
||||
dur = _detail(details, "session_duration_sec", default="")
|
||||
if dur not in ("-", "0", ""):
|
||||
msg += _line("⏱️", "Длительность", f"{html_escape(dur)} с")
|
||||
from app.services.session_duration import extract_session_duration_sec, format_session_duration
|
||||
|
||||
dur_sec = extract_session_duration_sec(details if isinstance(details, dict) else None)
|
||||
if dur_sec is not None and dur_sec > 0:
|
||||
msg += _line("⏱️", "Длительность", html_escape(format_session_duration(dur_sec)))
|
||||
msg += _line("🕐", "Время", format_time(event.occurred_at))
|
||||
win_id = _detail(details, "event_id_windows", default="")
|
||||
if win_id != "-":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Единый источник версии SAC (API, health, логи, OpenAPI)."""
|
||||
|
||||
APP_NAME = "Security Alert Center"
|
||||
APP_VERSION = "0.5.15"
|
||||
APP_VERSION = "0.5.16"
|
||||
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Tests for session_duration_sec extract/format."""
|
||||
|
||||
from app.services.session_duration import extract_session_duration_sec, format_session_duration
|
||||
|
||||
|
||||
def test_extract_session_duration_sec():
|
||||
assert extract_session_duration_sec({"session_duration_sec": 18324}) == 18324
|
||||
assert extract_session_duration_sec({"session_duration_sec": "42"}) == 42
|
||||
assert extract_session_duration_sec({"session_duration_sec": ""}) is None
|
||||
assert extract_session_duration_sec({}) is None
|
||||
assert extract_session_duration_sec(None) is None
|
||||
assert extract_session_duration_sec({"session_duration_sec": -1}) is None
|
||||
|
||||
|
||||
def test_format_session_duration():
|
||||
assert format_session_duration(0) == "00:00:00"
|
||||
assert format_session_duration(18324) == "05:05:24"
|
||||
assert format_session_duration(2428) == "00:40:28"
|
||||
assert format_session_duration(86400 + 85) == "1д 00:01:25"
|
||||
assert format_session_duration(2 * 86400 + 3661) == "2д 01:01:01"
|
||||
Reference in New Issue
Block a user