refactor: UI improvements and code cleanup

Frontend:
- DetectionsList: Simplify columns, improve truncation and display for IPs, hosts, bot info
- IncidentsView: Replace metric cards with compact stat cards (unique IPs, known bots, ML anomalies, threat levels)
- InvestigationView: Add section navigation anchors, reorganize layout with proper IDs
- ThreatIntelView: Add navigation links to investigation pages, add comment column, improve table layout

Backend:
- Various route and model adjustments
- Configuration updates

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
SOC Analyst
2026-03-20 09:56:49 +01:00
parent dbb9bb3f94
commit bd33fbad01
17 changed files with 444 additions and 510 deletions

View File

@ -1,11 +1,11 @@
"""
Routes pour la gestion des incidents clusterisés
"""
import hashlib
from fastapi import APIRouter, HTTPException, Query
from typing import List, Optional
from datetime import datetime, timedelta
from datetime import datetime
from ..database import db
from ..models import BaseModel
router = APIRouter(prefix="/api/incidents", tags=["incidents"])
@ -83,7 +83,6 @@ async def get_incident_clusters(
# Collect sample IPs to fetch real UA and trend data in bulk
sample_ips = [row[10] for row in result.result_rows if row[10]]
subnets_list = [row[0] for row in result.result_rows]
# Fetch real primary UA per sample IP from view_dashboard_entities
ua_by_ip: dict = {}
@ -182,7 +181,7 @@ async def get_incident_clusters(
primary_ua = ua_by_ip.get(sample_ip, "")
clusters.append({
"id": f"INC-{datetime.now().strftime('%Y%m%d')}-{len(clusters)+1:03d}",
"id": f"INC-{hashlib.md5(subnet.encode()).hexdigest()[:8].upper()}",
"score": risk_score,
"severity": severity,
"total_detections": row[1],
@ -213,22 +212,13 @@ async def get_incident_clusters(
@router.get("/{cluster_id}")
async def get_incident_details(cluster_id: str):
"""
Récupère les détails d'un incident spécifique
Récupère les détails d'un incident spécifique.
Non encore implémenté — les détails par cluster seront disponibles dans une prochaine version.
"""
try:
# Extraire le subnet du cluster_id (simplifié)
# Dans une implémentation réelle, on aurait une table de mapping
return {
"id": cluster_id,
"details": "Implementation en cours",
"timeline": [],
"entities": [],
"classifications": []
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")
raise HTTPException(
status_code=501,
detail="Détails par incident non encore implémentés. Utilisez /api/incidents/clusters pour la liste."
)
@router.post("/{cluster_id}/classify")
@ -239,34 +229,38 @@ async def classify_incident(
comment: str = ""
):
"""
Classe un incident rapidement
Classe un incident rapidement.
Non encore implémenté — utilisez /api/analysis/{ip}/classify pour classifier une IP.
"""
try:
# Implementation future - sauvegarde dans la table classifications
return {
"status": "success",
"cluster_id": cluster_id,
"label": label,
"tags": tags or [],
"comment": comment
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")
raise HTTPException(
status_code=501,
detail="Classification par incident non encore implémentée. Utilisez /api/analysis/{ip}/classify."
)
@router.get("")
async def list_incidents(
status: str = Query("active", description="Statut des incidents"),
severity: str = Query(None, description="Filtrer par sévérité"),
severity: Optional[str] = Query(None, description="Filtrer par sévérité (LOW/MEDIUM/HIGH/CRITICAL)"),
hours: int = Query(24, ge=1, le=168)
):
"""
Liste tous les incidents avec filtres
Liste tous les incidents avec filtres.
Délègue à get_incident_clusters ; le filtre severity est appliqué post-requête.
"""
try:
# Redirige vers clusters pour l'instant
return await get_incident_clusters(hours=hours, limit=50)
result = await get_incident_clusters(hours=hours, limit=100)
items = result["items"]
if severity:
sev_upper = severity.upper()
items = [c for c in items if c.get("severity") == sev_upper]
return {
"items": items,
"total": len(items),
"period_hours": hours,
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")