fix: hide ????????? after session terminate (0.3.8)

Persist session_terminated in event details, expose in API, and update Events/Dashboard UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-24 14:45:33 +10:00
parent 8b3e43f30d
commit 0601aafd2d
13 changed files with 86 additions and 18 deletions
+2
View File
@@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
from app.models.event import Event
from app.schemas.list_models import EventSummary
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
@@ -48,4 +49,5 @@ def event_to_summary(event: Event, db: Session | None = None) -> EventSummary:
rdg_flap_qwinsta_event_id=rdg_flap_qwinsta_event_id,
rdg_access_path=rdg_access_path,
rdg_qwinsta_enabled=rdg_qwinsta_enabled,
session_terminated=event_session_terminated(event),
)
+20
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import json
import re
from dataclasses import dataclass
from datetime import datetime, timezone
from app.models import Event, Host
from app.services.event_actor_user import extract_event_actor_user
@@ -37,6 +38,9 @@ SESSION_EVENT_TYPES_LINUX = frozenset(
)
SESSION_EVENT_TYPES_WINDOWS = frozenset({"rdp.login.success"})
SESSION_TERMINATED_AT_KEY = "session_terminated_at"
SESSION_TERMINATED_BY_KEY = "session_terminated_by"
_LOGIND_SESSION_ID_RE = re.compile(r"^\d+$|^c\d+$", re.IGNORECASE)
_LOGIND_USER_RE = re.compile(r"^[a-z_][a-z0-9._-]*$", re.IGNORECASE)
_LOGIND_STATES = frozenset(
@@ -77,6 +81,22 @@ def _event_login_user(event: Event) -> str:
return str(_details_dict(event).get("user") or "").strip()
def event_session_terminated(event: Event) -> bool:
details = _details_dict(event)
if details.get("session_terminated") is True:
return True
at = details.get(SESSION_TERMINATED_AT_KEY)
return at is not None and str(at).strip() != ""
def mark_event_session_terminated(event: Event, *, by_username: str | None = None) -> None:
details = dict(_details_dict(event))
details[SESSION_TERMINATED_AT_KEY] = datetime.now(timezone.utc).isoformat()
if by_username and by_username.strip():
details[SESSION_TERMINATED_BY_KEY] = by_username.strip()
event.details = details
def event_session_id(event: Event) -> str | None:
details = _details_dict(event)
for key in ("session_id", "sid"):