fix(qualys/dashboard): vire flag in-memory + safety net thread + flex layout 6 KPI
This commit is contained in:
parent
34cca6f77b
commit
0ab4f2d8fa
@ -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()
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user