dashboard changes for Session tab

This commit is contained in:
bot
2026-04-05 20:59:22 +03:00
parent e01bbd9e56
commit fee23f0f66
4 changed files with 340 additions and 0 deletions
+106
View File
@@ -407,6 +407,107 @@ def build_presence() -> dict:
}
SESSION_GAP_SECS = 120 # gap > 2 min between events from same src = new session
def _finalize_session(active: dict) -> dict:
reasons = active["reasons"]
dominant = max(set(reasons), key=reasons.count) if reasons else 0
_, classification = deauth_reason_info(dominant)
rssi_vals = [r for r in active["rssi_vals"] if r is not None]
start_dt = datetime.datetime.fromisoformat(active["start"])
end_dt = datetime.datetime.fromisoformat(active["end"])
duration = int((end_dt - start_dt).total_seconds())
frames = len(active["frames"])
if classification == "attack" and frames >= 5:
severity = "attack"
elif frames >= 3 or classification in ("attack", "suspicious"):
severity = "suspicious"
else:
severity = "normal"
src_vendor, src_rand = oui_lookup(active["src"])
return {
"src": active["src"],
"src_vendor": src_vendor,
"src_randomized": src_rand,
"start": active["start"],
"end": active["end"],
"duration_secs": duration,
"frame_count": frames,
"unique_bssids": list(active["bssids"]),
"unique_targets": list(active["targets"]),
"node_count": len(active["nodes"]),
"nodes": list(active["nodes"]),
"peak_rssi": max(rssi_vals) if rssi_vals else None,
"avg_rssi": round(sum(rssi_vals) / len(rssi_vals), 1) if rssi_vals else None,
"dominant_reason": dominant,
"severity": severity,
"events": active["frames"][:50],
}
def build_sessions() -> list[dict]:
cutoff = (datetime.datetime.utcnow() - datetime.timedelta(days=7)).isoformat(timespec="seconds")
rows = query("""
SELECT src, dst, bssid, reason, rssi, received_at, node_id, subtype
FROM deauth_events
WHERE received_at > ?
ORDER BY src, received_at
""", (cutoff,))
if not rows:
return []
sessions: list[dict] = []
active: dict | None = None
for row in rows:
src = row["src"]
ts = row["received_at"]
new_session = False
if active is None or active["src"] != src:
new_session = True
else:
last_dt = datetime.datetime.fromisoformat(active["end"])
curr_dt = datetime.datetime.fromisoformat(ts)
if (curr_dt - last_dt).total_seconds() > SESSION_GAP_SECS:
new_session = True
if new_session:
if active is not None:
sessions.append(_finalize_session(active))
active = {
"src": src,
"start": ts,
"end": ts,
"bssids": set(),
"targets": set(),
"nodes": set(),
"reasons": [],
"rssi_vals": [],
"frames": [],
}
active["end"] = ts
active["bssids"].add(row["bssid"])
active["targets"].add(row["dst"])
active["nodes"].add(row["node_id"])
active["reasons"].append(row["reason"])
active["rssi_vals"].append(row["rssi"])
active["frames"].append(row)
if active is not None:
sessions.append(_finalize_session(active))
sessions.sort(key=lambda s: s["start"], reverse=True)
return sessions[:100]
def build_alerts() -> dict:
now = datetime.datetime.utcnow()
cutoff_5m = (now - datetime.timedelta(minutes=5)).isoformat(timespec="seconds")
@@ -881,6 +982,11 @@ async def api_rssi_history(bssid: str, hours: int = 2):
return series
@app.get("/api/sessions")
async def api_sessions():
return build_sessions()
@app.get("/api/search")
async def api_search(q: str = ""):
return build_search(q)