- base.html: collapsible sidebar navigation, doc tooltip system, JS helpers (fmtNum, fmtPct, fmtDuration, ecGrid, buildTable, docHTML) - overview.html: SOC command center with stacked timeline, live alerts, campaigns panel, browser donut, 6 KPIs - detections.html: threat color dots, raw score column, click-to-navigate rows - network.html: JA4 rotation, brute-force, persistent threats tables, 6 KPIs - ip_detail.html: ASN/country KPIs, AE/XGB/campaign columns, enriched features - scores/traffic/features/models/classify: page_title blocks + doc tooltips - api.py: 9 new endpoints (campaigns, brute-force, ja4-rotation, recurrence, cascade, alerts, timeline-detail, ua-rotation) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
74 lines
4.2 KiB
HTML
74 lines
4.2 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}JA4 SOC — Modèles{% endblock %}
|
|
{% block page_title %}
|
|
Modèles ML
|
|
<span class="relative inline-block ml-1"><button onclick="docToggle(this)" class="doc-btn">?</button><div class="doc-panel">
|
|
<h4>État des modèles ML</h4>
|
|
<p>Ensemble triple-voix : Extended Isolation Forest (EIF) + Autoencoder (AE) + XGBoost supervisé.</p>
|
|
<p><strong>Versions :</strong> Chaque cycle crée un nouveau modèle si une dérive est détectée (95% features). Les anciens modèles restent en cache.</p>
|
|
<p class="doc-source">Source : /data/models/*.json, ml_all_scores</p>
|
|
</div></span>
|
|
{% endblock %}
|
|
{% block content %}
|
|
<div class="space-y-6">
|
|
<!-- Scoring stats from ClickHouse -->
|
|
<div class="bg-gray-900 rounded-xl border border-gray-800 overflow-hidden">
|
|
<h3 class="text-sm font-medium text-gray-400 px-5 py-3 border-b border-gray-800">Statistiques de scoring (7 derniers jours)</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="data-table"><thead><tr>
|
|
<th>Modèle</th><th>Sessions scorées</th><th>Premier scoring</th><th>Dernier scoring</th>
|
|
</tr></thead><tbody id="scoring-body"></tbody></table>
|
|
</div>
|
|
</div>
|
|
<!-- Model metadata files -->
|
|
<div class="bg-gray-900 rounded-xl border border-gray-800 overflow-hidden">
|
|
<h3 class="text-sm font-medium text-gray-400 px-5 py-3 border-b border-gray-800">Métadonnées des modèles</h3>
|
|
<div id="model-cards" class="p-5 space-y-4">
|
|
<span class="text-sm text-gray-500">Chargement...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block scripts %}
|
|
<script>
|
|
async function loadModels() {
|
|
try {
|
|
const r = await fetch('/api/models'); const d = await r.json();
|
|
// Scoring stats table
|
|
document.getElementById('scoring-body').innerHTML = (d.scoring_stats||[]).map(row => `<tr>
|
|
<td class="font-medium text-gray-200">${row.model_name||''}</td>
|
|
<td>${(row.scored||0).toLocaleString()}</td>
|
|
<td class="text-xs">${row.first_seen||''}</td>
|
|
<td class="text-xs">${row.last_seen||''}</td>
|
|
</tr>`).join('') || '<tr><td colspan="4" class="text-center text-gray-500 py-4">Aucun scoring récent</td></tr>';
|
|
// Model metadata cards
|
|
const cards = document.getElementById('model-cards');
|
|
if (d.models?.length) {
|
|
cards.innerHTML = d.models.map(m => `
|
|
<div class="bg-gray-800 rounded-lg p-4 border border-gray-700">
|
|
<div class="flex items-center gap-3 mb-2">
|
|
<span class="text-sm font-semibold text-white">${m.model_name||'?'} v${m.version_id||'?'}</span>
|
|
<span class="badge badge-low">${m.algorithm||'?'}</span>
|
|
${m.autoencoder ? '<span class="badge badge-medium">+AE</span>' : ''}
|
|
</div>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs text-gray-400">
|
|
<div>Entraîné : <span class="text-gray-300">${m.trained_at||'?'}</span></div>
|
|
<div>Échantillons : <span class="text-gray-300">${m.human_samples||'?'}</span></div>
|
|
<div>Contamination : <span class="text-gray-300">${m.contamination||'?'}</span></div>
|
|
<div>Seuil : <span class="text-gray-300">${m.threshold||'?'}</span></div>
|
|
${m.validation ? `<div>Val anomaly rate : <span class="text-gray-300">${(m.validation.val_anomaly_rate*100).toFixed(1)}%</span></div>
|
|
<div>Val mean score : <span class="text-gray-300">${m.validation.val_mean_score?.toFixed(4)||'?'}</span></div>
|
|
<div>Train size : <span class="text-gray-300">${m.validation.train_size||'?'}</span></div>
|
|
<div>Val size : <span class="text-gray-300">${m.validation.val_size||'?'}</span></div>` : ''}
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
} else {
|
|
cards.innerHTML = '<span class="text-sm text-gray-500">Aucun fichier de métadonnées trouvé (les modèles sont dans /data/models/)</span>';
|
|
}
|
|
} catch(e) { console.error(e); }
|
|
}
|
|
loadModels();
|
|
</script>
|
|
{% endblock %}
|