mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-30 00:19:30 +00:00
feat(secubox-toolbox): landing — live KPI auto-refresh + R3 cert autocheck card + visual polish (ref #496)
User wanted realtime metrics + cert autocheck accessible from kbin host. Both now work via kbin.gk2.secubox.in/landing : ## Live KPI (8 cells, 2 rows) Each KPI cell has data-live=<json.path>. JS polls /cumulative-stats.json every 5s (cache:no-store), digs the value, and re-renders with a flash animation when it changes : - sessions.last_7d / 24h / all_time - events.dpi / cookies / ja4 / total_7d - top_hosts_7d.length Header gains 🔴 LIVE badge (pulse animation) + 'maj HH:MM:SS' stamp. ## Cert R3 autocheck card New section between KPI and project pitch. Single 'Tester' button : - emoji + text update : ⏳ → 🟢 OK / 🔴 fail / ❔ timeout - probe technique : <img src=https://example.com/favicon.ico> (same heuristic as splash, works from any network) - displays CA SHA1 fingerprint pulled from /ca/fingerprint - on failure, links straight to /wg/ca.mobileconfig Accessible from anywhere (HAProxy vhost kbin.gk2.secubox.in → toolbox_landing backend → 10.99.0.1:8088). No captive subnet dependency. ## Cosmetic polish - pulse keyframe for LIVE badge - flash keyframe (.tick class) for KPI value change → gold→phos-hot - reflow trick (void el.offsetWidth) to retrigger CSS animation - dual-row KPI grid for more density without breaking mobile ## Live verification curl kbin /landing → HTTP 200 17853b matches : live-badge=1 data-live=9 cert-probe-btn=2 cumulative-stats.json=2 ## Next batch - Polish other section heads + arch ASCII colors - Banner-to-report flow E2E from iPhone with mh= bypass - PR #495 + #496 when iPhone R3 banner sighted in the wild
This commit is contained in:
parent
2638c6201b
commit
b865636aa3
|
|
@ -68,14 +68,42 @@ a:hover{text-decoration:underline}
|
|||
|
||||
<div class=container>
|
||||
|
||||
{# ── KPI live ── #}
|
||||
{# ── KPI live (auto-refresh 5s via /cumulative-stats.json) ── #}
|
||||
<div class=section>
|
||||
<h2>📊 Cabine en chiffres (live, 7 derniers jours)</h2>
|
||||
<h2>📊 Cabine en chiffres
|
||||
<span id=live-badge style="font-size:0.7rem;background:var(--red);color:#fff;padding:0.15rem 0.5rem;border-radius:99px;margin-left:0.5rem;letter-spacing:0.05em;animation:pulse 1.5s infinite">🔴 LIVE</span>
|
||||
<span id=live-stamp style="font-size:0.7rem;color:var(--dim);margin-left:0.4rem">·</span>
|
||||
</h2>
|
||||
<div class="grid grid-4">
|
||||
<div class=kpi><span class=v>{{ stats.sessions.last_7d if stats.sessions else 0 }}</span><span class=l>👥 sessions uniques</span></div>
|
||||
<div class=kpi><span class=v>{{ stats.events.get('dpi', 0) if stats.events else 0 }}</span><span class=l>🌐 connexions analysées</span></div>
|
||||
<div class=kpi><span class=v>{{ (stats.top_hosts_7d | length) if stats.top_hosts_7d else 0 }}</span><span class=l>📡 hôtes uniques top</span></div>
|
||||
<div class=kpi><span class=v>{{ stats.events.get('cookies', 0) if stats.events else 0 }}</span><span class=l>🍪 cookies trackés</span></div>
|
||||
<div class=kpi><span class=v data-live=sessions.last_7d>{{ stats.sessions.last_7d if stats.sessions else 0 }}</span><span class=l>👥 sessions uniques (7j)</span></div>
|
||||
<div class=kpi><span class=v data-live=events.dpi>{{ stats.events.get('dpi', 0) if stats.events else 0 }}</span><span class=l>🌐 connexions analysées</span></div>
|
||||
<div class=kpi><span class=v data-live=top_hosts_7d.length>{{ (stats.top_hosts_7d | length) if stats.top_hosts_7d else 0 }}</span><span class=l>📡 hôtes uniques top</span></div>
|
||||
<div class=kpi><span class=v data-live=events.cookies>{{ stats.events.get('cookies', 0) if stats.events else 0 }}</span><span class=l>🍪 cookies trackés</span></div>
|
||||
</div>
|
||||
<div class="grid grid-4" style="margin-top:1rem">
|
||||
<div class=kpi><span class=v data-live=sessions.last_24h>{{ stats.sessions.last_24h if stats.sessions else 0 }}</span><span class=l>🕐 sessions 24h</span></div>
|
||||
<div class=kpi><span class=v data-live=sessions.all_time>{{ stats.sessions.all_time if stats.sessions else 0 }}</span><span class=l>🌍 total all-time</span></div>
|
||||
<div class=kpi><span class=v data-live=events.ja4>{{ stats.events.get('ja4', 0) if stats.events else 0 }}</span><span class=l>🔬 fingerprints JA4</span></div>
|
||||
<div class=kpi><span class=v data-live=events.total_7d>{{ stats.events.get('total_7d', 0) if stats.events else 0 }}</span><span class=l>⚡ events 7j</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Cert autocheck — verify R3 CA install from anywhere ── #}
|
||||
<div class=section>
|
||||
<h2>🔐 Vérifier mon certificat R3 (test live)</h2>
|
||||
<div class="card purple" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
|
||||
<div style="font-size:2.4rem;flex-shrink:0" id=cert-probe-emoji>❔</div>
|
||||
<div style="flex:1;min-width:220px">
|
||||
<p id=cert-probe-text style="font-size:0.92rem;line-height:1.5">
|
||||
Clique sur <b>Tester</b> pour vérifier que ton CA R3 est bien installé sur ton device
|
||||
(depuis le tunnel WireGuard). Le test télécharge une image HTTPS et vérifie le succès.
|
||||
</p>
|
||||
<p style="font-size:0.75rem;color:var(--dim);margin-top:0.3rem">
|
||||
Empreinte SHA1 CA R3 (à vérifier dans Réglages iPhone) :
|
||||
<code id=cert-fp-r3 style="font-size:0.7rem">…</code>
|
||||
</p>
|
||||
</div>
|
||||
<button id=cert-probe-btn style="background:var(--purple);color:#0a0a0f;border:0;padding:0.7rem 1.2rem;font-weight:bold;border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9rem">🔬 Tester</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -244,4 +272,93 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
|
|||
// DIY · Open Source · Open Audit
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}
|
||||
.v.tick{animation:flash 0.6s}
|
||||
@keyframes flash{0%{color:var(--gold);transform:scale(1.15)}100%{color:var(--phos-hot);transform:scale(1)}}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// ── Live KPI auto-refresh from /cumulative-stats.json ──
|
||||
(function(){
|
||||
function dig(o,path){
|
||||
var parts = path.split('.');
|
||||
var v = o;
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
if (v == null) return null;
|
||||
if (parts[i] === 'length' && Array.isArray(v)) return v.length;
|
||||
v = v[parts[i]];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
function fmtTs(t){
|
||||
if (!t) return '·';
|
||||
var d = new Date(t*1000);
|
||||
var hh = String(d.getHours()).padStart(2,'0');
|
||||
var mm = String(d.getMinutes()).padStart(2,'0');
|
||||
var ss = String(d.getSeconds()).padStart(2,'0');
|
||||
return 'maj ' + hh+':'+mm+':'+ss;
|
||||
}
|
||||
function refresh(){
|
||||
fetch('/cumulative-stats.json', {cache:'no-store'}).then(function(r){return r.json();}).then(function(d){
|
||||
document.querySelectorAll('[data-live]').forEach(function(el){
|
||||
var v = dig(d, el.dataset.live);
|
||||
if (v == null) return;
|
||||
var prev = el.textContent.trim();
|
||||
var next = String(v);
|
||||
if (prev !== next) {
|
||||
el.textContent = next;
|
||||
el.classList.remove('tick');
|
||||
void el.offsetWidth; // force reflow
|
||||
el.classList.add('tick');
|
||||
}
|
||||
});
|
||||
var stamp = document.getElementById('live-stamp');
|
||||
if (stamp) stamp.textContent = fmtTs(d.computed_at);
|
||||
}).catch(function(){});
|
||||
}
|
||||
refresh();
|
||||
setInterval(refresh, 5000);
|
||||
})();
|
||||
|
||||
// ── Cert R3 probe + fingerprint display ──
|
||||
(function(){
|
||||
var btn = document.getElementById('cert-probe-btn');
|
||||
var emj = document.getElementById('cert-probe-emoji');
|
||||
var txt = document.getElementById('cert-probe-text');
|
||||
var fp = document.getElementById('cert-fp-r3');
|
||||
if (!btn) return;
|
||||
|
||||
// Display the WG CA fingerprint (use ?ca=wg flag if endpoint supports it,
|
||||
// else fallback to default ca fingerprint).
|
||||
fetch('/ca/fingerprint').then(function(r){return r.json();}).then(function(d){
|
||||
fp.textContent = d.sha1 || d.sha256 || '?';
|
||||
}).catch(function(){fp.textContent='?';});
|
||||
|
||||
function runProbe(){
|
||||
emj.textContent = '⏳';
|
||||
txt.innerHTML = 'Test en cours…';
|
||||
var probe = new Image();
|
||||
var done = false;
|
||||
probe.onload = function(){
|
||||
if (done) return; done = true;
|
||||
emj.textContent = '🟢';
|
||||
txt.innerHTML = '<b>HTTPS OK</b> — si tu es en R3 (tunnel WG actif), ton CA R3 est bien installé. ✓';
|
||||
};
|
||||
probe.onerror = function(){
|
||||
if (done) return; done = true;
|
||||
emj.textContent = '🔴';
|
||||
txt.innerHTML = '<b>HTTPS échoue</b> — soit pas de réseau, soit WG actif mais CA R3 manquant. Installe le <a href=/wg/ca.mobileconfig>profil CA R3</a>.';
|
||||
};
|
||||
probe.src = 'https://example.com/favicon.ico?t=' + Date.now();
|
||||
setTimeout(function(){
|
||||
if (done) return; done = true;
|
||||
emj.textContent = '❔';
|
||||
txt.innerHTML = 'Timeout — relance ou vérifie ton réseau.';
|
||||
}, 4000);
|
||||
}
|
||||
btn.addEventListener('click', runProbe);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body></html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user