fix(qualys/dashboard): vire flag in-memory + safety net thread + flex layout 6 KPI

This commit is contained in:
Pierre & Lumière 2026-04-25 00:13:22 +00:00
parent 34cca6f77b
commit 0ab4f2d8fa
3 changed files with 24 additions and 14 deletions

View File

@ -1229,7 +1229,22 @@ async def qualys_dashboard_refresh(request: Request, db=Depends(get_db)):
from app.database import SessionLocal
s = SessionLocal()
try:
compute_vuln_dashboard(s, triggered_by=f"manual:{user.get('sub','?')}", run_id=rid)
res = compute_vuln_dashboard(s, triggered_by=f"manual:{user.get('sub','?')}", run_id=rid)
# Filet : si compute n'a pas update le run (exception silenced), on le force
current = s.execute(text("SELECT status FROM qualys_vuln_snapshot_run WHERE id=:rid"),
{"rid": rid}).scalar()
if current == "pending":
msg = res.get("msg", "compute n'a pas update le run") if isinstance(res, dict) else "?"
s.execute(text("UPDATE qualys_vuln_snapshot_run SET status='error', msg=:m WHERE id=:rid"),
{"m": msg[:500], "rid": rid})
s.commit()
except Exception as ex:
try:
s.execute(text("UPDATE qualys_vuln_snapshot_run SET status='error', msg=:m WHERE id=:rid"),
{"m": str(ex)[:500], "rid": rid})
s.commit()
except Exception:
pass
finally:
s.close()
threading.Thread(target=_runner, args=(run_id,), daemon=True).start()

View File

@ -892,11 +892,6 @@ def _is_scanned(asset_row, has_vuln_data):
def compute_vuln_dashboard(db, triggered_by="manual", run_id=None):
"""Calcule un nouveau snapshot. Si run_id fourni, l'utilise (sinon en cree un).
Retourne dict {ok, msg, run_id, asset_count, duration_sec}."""
global _dashboard_running
if _dashboard_running:
return {"ok": False, "msg": "Calcul deja en cours", "run_id": None,
"asset_count": 0, "duration_sec": 0}
_dashboard_running = True
import time
t0 = time.time()
try:
@ -1002,7 +997,7 @@ def compute_vuln_dashboard(db, triggered_by="manual", run_id=None):
return {"ok": False, "msg": str(ex), "run_id": run_id,
"asset_count": 0, "duration_sec": int(time.time() - t0)}
finally:
_dashboard_running = False
pass
def load_vuln_dashboard(db):

View File

@ -66,33 +66,33 @@ setTimeout(function() { window.location.href = '/qualys/dashboard'; }, 15000);
<!-- KPI bandeau (6 cards) -->
{% set g = data.global %}
<div class="grid grid-cols-6 gap-2 mb-4">
<div class="card p-3 border-cyber-accent">
<div class="flex gap-2 mb-4 flex-wrap">
<div class="card p-3 border-cyber-accent flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">Total</div>
<div class="text-2xl font-bold text-cyber-accent">{{ g.total if g else 0 }}</div>
<div class="text-xs text-gray-500">serveurs</div>
</div>
<a href="/qualys/search?vuln_filter=critical" class="card p-3 border-red-700 hover:border-red-500 transition">
<a href="/qualys/search?vuln_filter=critical" class="card p-3 border-red-700 hover:border-red-500 transition flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">🔴 Critique (sev 5)</div>
<div class="text-2xl font-bold text-red-500">{{ g.critical if g else 0 }}</div>
<div class="text-xs text-gray-500">{{ ((g.critical * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%</div>
</a>
<a href="/qualys/search?vuln_filter=high" class="card p-3 border-orange-700 hover:border-orange-500 transition">
<a href="/qualys/search?vuln_filter=high" class="card p-3 border-orange-700 hover:border-orange-500 transition flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">🟠 High (sev 4)</div>
<div class="text-2xl font-bold text-orange-500">{{ g.high if g else 0 }}</div>
<div class="text-xs text-gray-500">{{ ((g.high * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%</div>
</a>
<a href="/qualys/search?vuln_filter=medium" class="card p-3 border-yellow-700 hover:border-yellow-500 transition">
<a href="/qualys/search?vuln_filter=medium" class="card p-3 border-yellow-700 hover:border-yellow-500 transition flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">🟡 Medium (sev 3)</div>
<div class="text-2xl font-bold text-yellow-500">{{ g.medium if g else 0 }}</div>
<div class="text-xs text-gray-500">{{ ((g.medium * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%</div>
</a>
<a href="/qualys/search?vuln_filter=zero" class="card p-3 border-green-700 hover:border-green-500 transition">
<a href="/qualys/search?vuln_filter=zero" class="card p-3 border-green-700 hover:border-green-500 transition flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">🟢 Sans vuln</div>
<div class="text-2xl font-bold text-green-500">{{ g.sain if g else 0 }}</div>
<div class="text-xs text-gray-500">{{ ((g.sain * 100 / g.scanned) | round(1)) if g and g.scanned > 0 else 0 }}%</div>
</a>
<div class="card p-3 border-gray-700">
<div class="card p-3 border-gray-700 flex-1" style="min-width:140px">
<div class="text-xs text-gray-500 uppercase">⚫ Non scanné</div>
<div class="text-2xl font-bold text-gray-400">{{ g.non_scanne if g else 0 }}</div>
<div class="text-xs text-gray-500">{{ ((g.non_scanne * 100 / g.total) | round(1)) if g and g.total > 0 else 0 }}% du total</div>