SOC Analyst f456c807db feat(clustering): ASN + country risk features, fix risk scoring and cluster labels
- Add country_risk() feature (index 21): lookup table 30 pays, CN/RU/KP=1.0, US/DE=0.1
- Add asn_cloud_score() feature (index 22): 50+ keyword match (cloud/CDN/VPN/crawler)
- N_FEATURES: 20 → 23 (SQL query extended with country + asn_org columns)
- Fix centroids_orig: denormalize K-means z-score centroids to [0,1] for display
- Update risk_score_from_centroid(): weights 0.30 ML + 0.12 fuzzing + 0.12 UA-CH
  + 0.12 country_risk + 0.10 asn_cloud (total=1.0)
- Update name_cluster(): new labels 'Source pays risqué', 'Bot cloud UA-CH',
  'Infrastructure cloud'; thresholds lowered for better sensitivity
- Risk scores now discriminate: pays risqué 0.35, bot cloud 0.23-0.28, sain 0.06

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 10:15:21 +01:00

🛡️ Bot Detector Dashboard

Dashboard web interactif pour visualiser et investiguer les décisions de classification du Bot Detector IA.

Version: 2.0.0 - TCP Fingerprinting Multi-Signal + Clustering IPs Multi-Métriques

🚀 Démarrage Rapide

Prérequis

  • Docker et Docker Compose
  • Le service clickhouse déjà déployé
  • Des données dans la table ml_detected_anomalies
  • Des données dans la table http_logs (pour les user-agents)

Note: Le dashboard peut fonctionner indépendamment de bot_detector_ai. Il lit les données déjà détectées dans ClickHouse.

Lancement

# 1. Vérifier que .env existe
cp .env.example .env  # Si ce n'est pas déjà fait

# 2. Lancer le dashboard (avec Docker Compose v2)
docker compose up -d dashboard_web

# Ou avec l'ancienne syntaxe
docker-compose up -d dashboard_web

# 3. Ouvrir le dashboard
# http://localhost:3000

Arrêt

docker compose stop dashboard_web

Vérifier le statut

# Voir les services en cours d'exécution
docker compose ps

# Voir les logs en temps réel
docker compose logs -f dashboard_web

📊 Fonctionnalités

Dashboard Principal

  • Métriques en temps réel : Total détections, menaces, bots connus, IPs uniques
  • Comparaison baseline J-1 : variation ▲▼ vs hier (détections, IPs uniques, CRITICAL)
  • Répartition par menace : Visualisation CRITICAL/HIGH/MEDIUM/LOW
  • Évolution temporelle : Graphique des détections sur 24h
  • Incidents clusterisés : Regroupement automatique par subnet /24
  • Top Menaces Actives : Top 10 des IPs les plus dangereuses

🧬 TCP Spoofing & Fingerprinting OS (amélioré v2.0)

  • Détection multi-signal : TTL initial + MSS + scale + fenêtre TCP (p0f-style)
  • 20 signatures OS : Linux, Windows, macOS, Android, iOS, Masscan, ZMap, Shodan, Googlebot…
  • Estimation hop-count : différence TTL initial (arrondi) TTL observé
  • Détection réseau : MSS → Ethernet (1460) / PPPoE (1452) / VPN (1420) / Tunnel (<1420)
  • Confiance 0100% : score pondéré (TTL 40% + MSS 30% + fenêtre 20% + scale 10%)
  • Badge bot-tool : Masscan détecté à 97% (win=5808, mss=1452, scale=4)
  • Distribution MSS : histogramme des MSS observés par cluster

🔬 Clustering IPs Multi-Métriques (nouveau v2.0)

  • URL: /clustering
  • Algorithme : K-means++ (Arthur & Vassilvitskii, 2007), initialisé avec k-means++, 3 runs
  • 21 features normalisées [0,1] :
    • Stack TCP : TTL initial, MSS, scale, fenêtre TCP
    • Anomalie ML : score, vélocité, fuzzing, headless, POST ratio, IP-ID zéro
    • TLS/Protocole : ALPN mismatch, ALPN absent, efficacité H2 (multiplexing)
    • Navigateur : score navigateur moderne, ordre headers, UA-CH mismatch
    • Temporel : entropie, diversité JA4, UA rotatif
  • Positionnement 2D : PCA par puissance itérative (Hotelling) + déflation
  • Nommage automatique : Masscan / Bot UA Rotatif / Bot Fuzzer / Anomalie ML / Linux / Windows / VPN

Vue Tableau de bord (défaut) :

  • Grille de cartes groupées : Bots confirmés → Suspects → Légitimes
  • Chaque carte : label, IP count, hits, badge CRITIQUE/ÉLEVÉ/MODÉRÉ/SAIN
  • 4 mini-barres : anomalie, UA-CH mismatch, fuzzing, UA rotatif
  • Stack TCP (TTL, MSS, Scale), top pays, ASN

Vue Graphe de relations :

  • Nœuds-cartes ReactFlow (220px, texte lisible)
  • Colonnes par niveau de menace : Bots | Suspects | Légitimes
  • Arêtes colorées par similarité (orange=fort, animé=très fort)
  • Légende intégrée, minimap, contrôles zoom

Sidebar de détail :

  • RadarChart comportemental (10 axes : anomalie, UA-CH, fuzzing, headless…)
  • Toutes les métriques avec barres de progression colorées
  • Liste des IPs avec badges menace/pays/ASN
  • Export Copier IPs + ⬇ CSV

Investigation Subnet /24

  • URL: /entities/subnet/x.x.x.x_24
  • Stats globales, tableau des IPs, actions par IP

Investigation IP + Réputation

  • URL: /investigation/:ip
  • Synthèse multi-sources (ML + bruteforce + TCP + JA4 + timeline)
  • Score de risque 0100, réputation IP-API + IPinfo

Investigation (Variabilité)

  • User-Agents, JA4 fingerprints, pays, ASN, hosts, niveaux de menace
  • Insights automatiques, navigation enchaînable

🏗️ Architecture

┌─────────────────────────────────────────────────────────┐
│                    Docker Compose                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐ │
│  │  ClickHouse │  │ bot_detector│  │  dashboard_web  │ │
│  │  :8123      │  │  (existant) │  │  :8000 (web+API)│ │
│  │  :9000      │  │             │  │  network=host   │ │
│  └──────┬──────┘  └──────┬──────┘  └────────┬────────┘ │
│         └────────────────┴───────────────────┘          │
└─────────────────────────────────────────────────────────┘

Le container utilise network_mode: "host" — le frontend buildé est servi par FastAPI sur le port 8000 uniquement (pas de port 3000 en production).

Composants

Composant Technologie Description
Frontend React 18 + TypeScript 5 + Vite 5 + Tailwind CSS 3 Interface utilisateur (SPA)
Backend API FastAPI 0.111 + Python 3.11 API REST + serveur statique SPA
Database ClickHouse (existant) — port 8123 Base de données principale
Clustering K-means++ pur Python + PCA puissance itérative Algorithmes embarqués, sans dépendance ML

📁 Structure

dashboard/
├── Dockerfile                    # Multi-stage: node:20-alpine → python:3.11-slim
├── docker-compose.yaml
├── requirements.txt
├── backend/
│   ├── main.py                  # FastAPI: CORS, routers, SPA catch-all (doit être DERNIER)
│   ├── config.py                # pydantic-settings, lit .env
│   ├── database.py              # ClickHouseClient singleton (db)
│   ├── models.py                # Modèles Pydantic v2
│   ├── routes/
│   │   ├── metrics.py           # GET /api/metrics, /api/metrics/baseline
│   │   ├── detections.py        # GET /api/detections
│   │   ├── variability.py       # GET /api/variability
│   │   ├── attributes.py        # GET /api/attributes
│   │   ├── incidents.py         # GET /api/incidents/clusters
│   │   ├── entities.py          # GET /api/entities
│   │   ├── analysis.py          # GET/POST /api/analysis — classifications SOC
│   │   ├── reputation.py        # GET /api/reputation/ip/{ip}
│   │   ├── tcp_spoofing.py      # GET /api/tcp-spoofing — fingerprinting OS multi-signal
│   │   ├── clustering.py        # GET /api/clustering/clusters + /cluster/{id}/ips
│   │   └── investigation_summary.py  # GET /api/investigation/{ip}/summary
│   └── services/
│       ├── tcp_fingerprint.py   # 20 signatures OS, scoring, hop-count, réseau path
│       ├── clustering_engine.py # K-means++, PCA-2D, nommage, score risque (pur Python)
│       └── reputation_ip.py     # httpx → ip-api.com + ipinfo.io (async, sans API key)
└── frontend/
    ├── package.json
    ├── vite.config.ts           # Proxy /api → :8000 en dev
    └── src/
        ├── App.tsx              # BrowserRouter + Sidebar + TopHeader + Routes
        ├── ThemeContext.tsx     # dark/light/auto, localStorage: soc_theme
        ├── api/client.ts        # Axios baseURL=/api + toutes les interfaces TypeScript
        ├── components/
        │   ├── ClusteringView.tsx        # K-means++ clustering — 2 vues
        │   ├── TcpSpoofingView.tsx       # TCP fingerprinting OS
        │   ├── InvestigationView.tsx     # Investigation IP complète
        │   └── ...                       # Autres vues
        ├── hooks/               # useMetrics, useDetections, useVariability (polling)
        └── utils/STIXExporter.ts

🔌 API

Endpoints

Method Endpoint Description
GET /api/metrics Métriques globales
GET /api/metrics/baseline Comparaison J-1 (détections, IPs, CRITICAL)
GET /api/metrics/threats Distribution par menace
GET /api/detections Liste des détections paginée
GET /api/detections/{id} Détails d'une détection
GET /api/variability/{type}/{value} Variabilité d'un attribut
GET /api/attributes/{type} Valeurs uniques d'un attribut
GET /api/incidents/clusters Incidents clusterisés par subnet /24
GET /api/entities/subnet/{subnet} Investigation subnet (ex: 141.98.11.0_24)
GET /api/entities/{type}/{value} Investigation entité (IP, JA4, UA…)
GET /api/reputation/ip/{ip} Réputation IP (IP-API + IPinfo)
GET /api/investigation/{ip}/summary Synthèse IP multi-sources (ML + TCP + JA4)
GET /api/analysis/{ip}/subnet Analyse subnet / ASN
GET /api/analysis/{ip}/recommendation Recommandation de classification
POST /api/analysis/classifications Sauvegarder classification SOC
GET /api/tcp-spoofing/overview Vue d'ensemble TCP spoofing + OS
GET /api/tcp-spoofing/list Liste des détections TCP spoofing
GET /api/tcp-spoofing/matrix Matrice OS déclaré vs OS réel
GET /api/clustering/clusters Clustering K-means++ (?k=14&n_samples=3000)
GET /api/clustering/cluster/{id}/ips IPs d'un cluster (drill-down)
GET /health Health check

Exemples

# Health check
curl http://localhost:8000/health

# Métriques globales + baseline
curl http://localhost:8000/api/metrics | jq '.summary'
curl http://localhost:8000/api/metrics/baseline | jq

# Détections CRITICAL
curl "http://localhost:8000/api/detections?threat_level=CRITICAL&page=1" | jq '.items | length'

# TCP Spoofing — vue d'ensemble
curl http://localhost:8000/api/tcp-spoofing/overview | jq

# Clustering IPs (14 clusters sur 3000 échantillons)
curl "http://localhost:8000/api/clustering/clusters?k=14&n_samples=3000" | jq '.stats'

# Drill-down d'un cluster
curl "http://localhost:8000/api/clustering/cluster/c0_k14/ips?limit=20" | jq '.ips[].ip'

# Réputation IP
curl http://localhost:8000/api/reputation/ip/162.55.94.175 | jq

⚙️ Configuration

Variables d'Environnement

Variable Défaut Description
CLICKHOUSE_HOST clickhouse Hôte ClickHouse
CLICKHOUSE_PORT 8123 Port HTTP ClickHouse
CLICKHOUSE_DB mabase_prod Base de données
CLICKHOUSE_USER admin Utilisateur
CLICKHOUSE_PASSWORD `` Mot de passe
API_HOST 0.0.0.0 Bind Uvicorn
API_PORT 8000 Port API + frontend
CORS_ORIGINS ["http://localhost:3000", ...] Origines CORS autorisées

Ces variables sont lues depuis le fichier .env à la racine du projet.

⚠️ Le fichier .env contient les credentials réels — ne jamais le committer.

🔍 Workflows d'Investigation

Exemple 1 : Identifier un bot Masscan

  1. 🔬 Clustering IPs → Cluster "🤖 Masscan / Scanner IP" visible en rouge
  2. Clic sur la carte → Sidebar : TTL=52, MSS=1452, Scale=4 — pattern Masscan
  3. Copier les IPs → Liste prête pour le blocage
  4. Export CSV → Import dans le SIEM ou firewall

Exemple 2 : Analyser des bots UA-rotatifs (cloud)

  1. Clustering → Cluster "🤖 Bot UA Rotatif + CH Mismatch" (risque 50%)
  2. RadarChart → UA-CH=100%, UA rotatif=100%, anomalie=59%
  3. Top ASN → Microsoft, Google, Akamai — cloud providers
  4. 🧬 TCP Spoofing → Confirmer : ces IPs déclarent Windows UA mais ont TTL Linux
  5. Investigation IP → Détail complet avec timeline 24h

Exemple 3 : Détecter le spoofing d'OS

  1. 🧬 TCP Spoofing → Liste des IPs avec mismatch OS
  2. Matrice UA×OS → User-Agent Android mais stack TCP Windows = spoof
  3. Confiance 85% → MSS=1460 (Ethernet), scale=7, TTL≈64 → Linux réel
  4. Action → Classer comme bot avec IP proxy

Exemple 4 : Investiguer une IP suspecte

  1. 🎯 Détections → IP classifiée 🔴 CRITICAL
  2. Clic sur l'IP → Synthèse : ML + TCP + JA4 + bruteforce + timeline
  3. Score de risque : 85/100
  4. User-Agents → 3 UA différents en 24h (rotation)
  5. TCP → TTL initial 128 (Windows) mais UA Linux → spoof
  6. Action → Blacklist immédiate

🧬 Services techniques (v2.0)

backend/services/tcp_fingerprint.py

Détection multi-signal de l'OS réel basée sur la stack TCP :

from backend.services.tcp_fingerprint import fingerprint_os, detect_spoof

result = fingerprint_os(ttl=52, win=5808, scale=4, mss=1452)
# → OSFingerprint(os_family="Masscan/Scanner", confidence=0.97, is_bot_tool=True)

spoof = detect_spoof(declared_ua="Chrome/Windows", fingerprint=result)
# → SpoofResult(is_spoof=True, reason="UA Windows mais stack Masscan", risk_score=30)

Poids du scoring : TTL initial 40% + MSS 30% + fenêtre 20% + scale 10%

Estimation hop-count :

  • TTL observé 52 → TTL initial arrondi = 64 → hops = 64 52 = 12
  • TTL observé 119 → TTL initial = 128 → hops = 9

MSS → chemin réseau :

MSS Réseau détecté
1460 Ethernet standard
1452 PPPoE / DSL
14201452 VPN probable
< 1420 Tunnel / double-encap

backend/services/clustering_engine.py

K-means++ + PCA-2D embarqués en pur Python (sans numpy/sklearn) :

K-means++ init   : O(k·n) distances, n_init=3 runs → meilleure inertie
Power iteration  : X^T(Xv) trick → O(n·d) par itération, pas de matrice n×n
Déflation Hotelling : retire PC1 de X avant de calculer PC2

21 features normalisées [0,1] — voir FEATURES dans le fichier.

Nommage automatique par priorité décroissante :

  1. Pattern Masscan (mss 14401460, scale 35, TTL<60)
  2. Fuzzing agressif (fuzzing_index normalisé > 0.35 ≈ valeur brute > 100)
  3. UA rotatif + UA-CH mismatch simultanés
  4. UA-CH mismatch seul > 80%
  5. Score anomalie ML > 20% + signal comportemental
  6. Classification réseau / OS par TTL/MSS

🗄️ Tables ClickHouse utilisées

Table / Vue Routes
mabase_prod.ml_detected_anomalies metrics, detections, variability, analysis, clustering
mabase_prod.agg_host_ip_ja4_1h tcp_spoofing, clustering, investigation_summary
mabase_prod.view_dashboard_entities entities (UA, JA4, paths, query params)
mabase_prod.classifications analysis (classifications SOC manuelles)
mabase_prod.audit_logs audit (optionnel — silencieux si absent)

Conventions SQL :

  • IPs stockées en IPv6-mappé : replaceRegexpAll(toString(src_ip), '^::ffff:', '')
  • anomaly_score peut être négatif : toujours utiliser abs()
  • fuzzing_index peut dépasser 200 : normaliser avec log1p
  • multiplexing_efficiency peut dépasser 1 : normaliser avec log1p
  • Paramètres SQL : syntaxe %(name)s (dict ClickHouse)
  • SPA catch-all DOIT être le dernier router dans main.py

🎨 Thème

Le dashboard utilise un thème sombre optimisé SOC (dark par défaut, clair et auto disponibles) :

  • Tokens CSS sémantiques : bg-background, bg-background-card, text-text-primary, text-text-secondary
  • Taxonomie menaces : rouge CRITICAL / orange HIGH / jaune MEDIUM / vert LOW
  • Persistance : localStorage clé soc_theme
  • Ne jamais utiliser de classes Tailwind brutes (slate-800) — toujours les tokens sémantiques

📝 Logs

Les logs du dashboard sont accessibles via Docker :

# Logs du container
docker logs dashboard_web

# Logs en temps réel
docker logs -f dashboard_web

🧪 Tests et Validation

Script de test rapide

Créez un fichier test_dashboard.sh :

#!/bin/bash
echo "=== Test Dashboard Bot Detector ==="

# 1. Health check
echo -n "1. Health check... "
curl -s http://localhost:3000/health > /dev/null && echo "✅ OK" || echo "❌ ÉCHOUÉ"

# 2. API Metrics
echo -n "2. API Metrics... "
curl -s http://localhost:3000/api/metrics | jq -e '.summary' > /dev/null && echo "✅ OK" || echo "❌ ÉCHOUÉ"

# 3. API Detections
echo -n "3. API Detections... "
curl -s http://localhost:3000/api/detections | jq -e '.items' > /dev/null && echo "✅ OK" || echo "❌ ÉCHOUÉ"

# 4. Frontend
echo -n "4. Frontend HTML... "
curl -s http://localhost:3000 | grep -q "Bot Detector" && echo "✅ OK" || echo "❌ ÉCHOUÉ"

echo "=== Tests terminés ==="

Rendez-le exécutable et lancez-le :

chmod +x test_dashboard.sh
./test_dashboard.sh

Tests manuels de l'API

# 1. Health check
curl http://localhost:3000/health

# 2. Métriques globales
curl http://localhost:3000/api/metrics | jq

# 3. Liste des détections (page 1, 25 items)
curl "http://localhost:3000/api/detections?page=1&page_size=25" | jq

# 4. Filtrer par menace CRITICAL
curl "http://localhost:3000/api/detections?threat_level=CRITICAL" | jq '.items[].src_ip'

# 5. Distribution par menace
curl http://localhost:3000/api/metrics/threats | jq

# 6. Liste des IPs uniques (top 10)
curl "http://localhost:3000/api/attributes/ip?limit=10" | jq

# 7. Variabilité d'une IP (remplacer par une IP réelle)
curl http://localhost:3000/api/variability/ip/192.168.1.100 | jq

# 8. Variabilité d'un pays
curl http://localhost:3000/api/variability/country/FR | jq

# 9. Variabilité d'un ASN
curl http://localhost:3000/api/variability/asn/16276 | jq

Test du Frontend

# Vérifier que le HTML est servi
curl -s http://localhost:3000 | head -20

# Ou ouvrir dans le navigateur
# http://localhost:3000

Scénarios de test utilisateur

  1. Navigation de base

  2. Recherche et filtres

    • Rechercher une IP : 192.168
    • Filtrer par menace : CRITICAL
    • Changer de page
  3. Investigation (variabilité)

    • Cliquer sur une IP dans le tableau
    • Vérifier la section "User-Agents" (plusieurs valeurs ?)
    • Cliquer sur un User-Agent pour investiguer
    • Utiliser le breadcrumb pour revenir en arrière
  4. Insights

    • Trouver une IP avec plusieurs User-Agents
    • Vérifier que l'insight "Possible rotation/obfuscation" s'affiche

Vérifier les données ClickHouse

# Compter les détections (24h)
docker compose exec clickhouse clickhouse-client -d mabase_prod -q \
  "SELECT count() FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 24 HOUR"

# Voir un échantillon
docker compose exec clickhouse clickhouse-client -d mabase_prod -q \
  "SELECT src_ip, threat_level, model_name, detected_at FROM ml_detected_anomalies ORDER BY detected_at DESC LIMIT 5"

# Vérifier les vues du dashboard
docker compose exec clickhouse clickhouse-client -d mabase_prod -q \
  "SELECT * FROM view_dashboard_summary"

🐛 Dépannage

Diagnostic rapide

# 1. Vérifier que les services tournent
docker compose ps

# 2. Vérifier les logs du dashboard
docker compose logs dashboard_web | tail -50

# 3. Tester la connexion ClickHouse depuis le dashboard
docker compose exec dashboard_web curl -v http://clickhouse:8123/ping

Le dashboard ne démarre pas

# Vérifier les logs
docker compose logs dashboard_web

# Erreur courante: Port déjà utilisé
# Solution: Changer le port dans docker-compose.yml

# Erreur courante: Image non construite
docker compose build dashboard_web
docker compose up -d dashboard_web

Aucune donnée affichée (dashboard vide)

# 1. Vérifier qu'il y a des données dans ClickHouse
docker compose exec clickhouse clickhouse-client -d mabase_prod -q \
  "SELECT count() FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 24 HOUR"

# Si le résultat est 0:
# - Lancer bot_detector_ai pour générer des données
docker compose up -d bot_detector_ai
docker compose logs -f bot_detector_ai

# - Ou importer des données manuellement

Erreur "Connexion ClickHouse échoué"

# 1. Vérifier que ClickHouse est démarré
docker compose ps clickhouse

# 2. Tester la connexion
docker compose exec clickhouse clickhouse-client -q "SELECT 1"

# 3. Vérifier les credentials dans .env
cat .env | grep CLICKHOUSE

# 4. Redémarrer le dashboard
docker compose restart dashboard_web

# 5. Vérifier les logs d'erreur
docker compose logs dashboard_web | grep -i error

Erreur 404 sur les routes API

# Vérifier que l'API répond
curl http://localhost:3000/health
curl http://localhost:3000/api/metrics

# Si 404, redémarrer le dashboard
docker compose restart dashboard_web

Port 3000 déjà utilisé

# Option 1: Changer le port dans docker-compose.yml
# Remplacer: - "3000:8000"
# Par: - "8080:8000"

# Option 2: Trouver et tuer le processus
lsof -i :3000
kill <PID>

# Puis redémarrer
docker compose up -d dashboard_web

Frontend ne se charge pas (page blanche)

# 1. Vérifier la console du navigateur (F12)
# 2. Vérifier que le build frontend existe
docker compose exec dashboard_web ls -la /app/frontend/dist

# 3. Si vide, reconstruire l'image
docker compose build --no-cache dashboard_web
docker compose up -d dashboard_web

Logs d'erreur courants

Erreur Cause Solution
Connection refused ClickHouse pas démarré docker compose up -d clickhouse
Authentication failed Mauvais credentials Vérifier .env
Table doesn't exist Vues non créées Lancer deploy_views.sql
No data available Pas de données Lancer bot_detector_ai

🔒 Sécurité

  • Pas d'authentification : Dashboard conçu pour un usage local
  • CORS restreint : Seulement localhost:3000
  • Rate limiting : 100 requêtes/minute
  • Credentials : Via variables d'environnement (jamais en dur)

📊 Performances

  • Temps de chargement : < 2s (avec données)
  • Requêtes ClickHouse : Optimisées avec agrégations
  • Rafraîchissement auto : 30 secondes (métriques)

🧪 Développement

Build local (sans Docker)

# Backend
cd dashboard
pip install -r requirements.txt
python -m uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000

# Frontend (dans un autre terminal)
cd dashboard/frontend
npm install
npm run dev  # http://localhost:5173

Documentation API interactive

L'API inclut une documentation Swagger interactive :

# Ouvrir dans le navigateur
http://localhost:3000/docs

# Ou directement sur le port API
http://localhost:8000/docs

Tests unitaires (à venir)

# Backend (pytest)
cd dashboard
pytest backend/tests/

# Frontend (jest)
cd dashboard/frontend
npm test

📄 License

Même license que le projet principal Bot Detector.


📞 Support

Pour toute question ou problème :

  1. Vérifier la section 🐛 Dépannage ci-dessus
  2. Consulter les logs : docker compose logs dashboard_web
  3. Vérifier que ClickHouse contient des données
  4. Ouvrir une issue sur le dépôt
Description
No description provided
Readme 1 MiB
Languages
TypeScript 64.5%
Python 34.9%
CSS 0.3%
JavaScript 0.2%