Files
ja4-platform/services/dashboard/backend/templates/classify.html
toto 2d04288e95 feat(dashboard): SOC workflow overhaul — sidebar nav, doc tooltips, full-width layout
- 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>
2026-04-09 00:29:34 +02:00

74 lines
4.3 KiB
HTML

{% extends "base.html" %}
{% block title %}JA4 SOC — Classifier{% endblock %}
{% block page_title %}
Classification SOC
<span class="relative inline-block ml-1"><button onclick="docToggle(this)" class="doc-btn">?</button><div class="doc-panel">
<h4>Feedback analyste SOC</h4>
<p>Classifiez les IPs pour entraîner le modèle XGBoost supervisé. Les labels sont utilisés au prochain cycle ML.</p>
<p><strong>Bot :</strong> Confirme que l'IP est malveillante. <strong>Légitime :</strong> Faux positif. <strong>Suspect :</strong> À surveiller.</p>
<p class="doc-source">Source : soc_feedback → XGBoost training</p>
</div></span>
{% endblock %}
{% block content %}
<div class="space-y-6">
<div class="bg-gray-900 rounded-xl p-6 border border-gray-800 space-y-4">
<div>
<label class="block text-sm text-gray-400 mb-1">Adresse IP</label>
<input type="text" id="cls-ip" placeholder="ex: 192.168.1.100" class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm text-gray-300 focus:border-brand-500 focus:outline-none">
</div>
<div>
<label class="block text-sm text-gray-400 mb-1">Classification</label>
<select id="cls-type" class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm text-gray-300">
<option value="bot">🤖 Bot malveillant</option>
<option value="legitimate">✅ Trafic légitime</option>
<option value="suspicious">⚠️ Suspect (à surveiller)</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-400 mb-1">Commentaire</label>
<textarea id="cls-comment" rows="3" placeholder="Raison de la classification..." class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm text-gray-300 focus:border-brand-500 focus:outline-none resize-none"></textarea>
</div>
<button id="cls-submit" class="px-6 py-2 bg-brand-600 text-white rounded-lg text-sm font-medium hover:bg-brand-700 transition-colors">Envoyer la classification</button>
<div id="cls-result" class="text-sm"></div>
</div>
<!-- Recent classifications -->
<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">Classifications récentes</h3>
<div class="overflow-x-auto">
<table class="data-table"><thead><tr>
<th>Date</th><th>IP</th><th>Classification</th><th>Commentaire</th>
</tr></thead><tbody id="cls-history"></tbody></table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.getElementById('cls-submit').onclick = async () => {
const ip = document.getElementById('cls-ip').value.trim();
if (!ip) { alert('Veuillez saisir une IP'); return; }
try {
const r = await fetch('/api/classify', {method:'POST', headers:{'Content-Type':'application/json'},
body:JSON.stringify({src_ip:ip, classification:document.getElementById('cls-type').value, comment:document.getElementById('cls-comment').value})});
const d = await r.json();
document.getElementById('cls-result').innerHTML = r.ok
? `<span class="text-green-400">✓ ${ip} classifié : ${d.classification}</span>`
: `<span class="text-red-400">✗ Erreur : ${d.detail||'unknown'}</span>`;
if (r.ok) loadHistory();
} catch(e) { document.getElementById('cls-result').innerHTML = `<span class="text-red-400">✗ ${e}</span>`; }
};
async function loadHistory() {
try {
const r = await fetch('/api/classifications'); const d = await r.json();
document.getElementById('cls-history').innerHTML = (d.data||[]).map(row => `<tr>
<td class="text-xs">${row.created_at||''}</td>
<td>${fmtIP(row.src_ip)}</td>
<td><span class="badge ${row.classification==='bot'?'badge-critical':row.classification==='legitimate'?'badge-low':'badge-medium'}">${row.classification}</span></td>
<td class="text-xs max-w-[300px] truncate">${row.comment||''}</td>
</tr>`).join('') || '<tr><td colspan="4" class="text-center text-gray-500 py-4">Aucune classification</td></tr>';
} catch(e) {}
}
loadHistory();
</script>
{% endblock %}