fix(dashboard): campaigns scatter chart — show campaigns not IPs
- API /api/campaigns/scatter: aggregate by campaign_id instead of per-IP Returns avg_score, avg_velocity, unique_ips, ja4_list, asn_list, country_list - Template: one bubble per campaign, sized by IP count - Tooltip: campaign-level info (IPs, score, velocity, ASNs, pays, JA4s) - Click navigates to campaign detail (not IP detail) - Updated doc panel text Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@ -990,27 +990,30 @@ async def campaigns_graph(days: int = 7) -> dict[str, Any]:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/campaigns/scatter — Scatter plot data (score vs velocity per IP)
|
||||
# GET /api/campaigns/scatter — Scatter plot : une bulle par campagne
|
||||
# ---------------------------------------------------------------------------
|
||||
@router.get("/campaigns/scatter")
|
||||
async def campaigns_scatter(days: int = 7) -> dict[str, Any]:
|
||||
"""Données scatter plot : score vs vélocité par IP, coloré par campagne."""
|
||||
"""Données scatter plot : score vs vélocité agrégés par campagne."""
|
||||
days = max(1, min(days, 90))
|
||||
try:
|
||||
rows = query(
|
||||
f"SELECT toString(src_ip) AS ip, "
|
||||
f"campaign_id, "
|
||||
f"min(anomaly_score) AS score, "
|
||||
f"avg(hit_velocity) AS velocity, "
|
||||
f"SELECT campaign_id, "
|
||||
f"avg(anomaly_score) AS avg_score, "
|
||||
f"avg(hit_velocity) AS avg_velocity, "
|
||||
f"sum(hits) AS total_hits, "
|
||||
f"any(ja4) AS ja4, "
|
||||
f"any(asn_org) AS asn_org, "
|
||||
f"any(threat_level) AS threat "
|
||||
f"uniqExact(src_ip) AS unique_ips, "
|
||||
f"groupUniqArray(5)(ja4) AS ja4_list, "
|
||||
f"groupUniqArray(3)(asn_org) AS asn_list, "
|
||||
f"groupUniqArray(3)(country_code) AS country_list, "
|
||||
f"any(threat_level) AS threat, "
|
||||
f"min(detected_at) AS first_seen, "
|
||||
f"max(detected_at) AS last_seen "
|
||||
f"FROM {_DB}.ml_detected_anomalies "
|
||||
"WHERE campaign_id >= 0 "
|
||||
"AND detected_at >= now() - INTERVAL {days:UInt16} DAY "
|
||||
"GROUP BY src_ip, campaign_id "
|
||||
"ORDER BY score ASC LIMIT 500",
|
||||
"GROUP BY campaign_id "
|
||||
"ORDER BY avg_score ASC LIMIT 100",
|
||||
{"days": days},
|
||||
)
|
||||
return {"data": rows}
|
||||
|
||||
Reference in New Issue
Block a user