Export CSV avec filtres (domaine/zone/recherche/KPI), BOM UTF-8
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c42708db75
commit
df03852c86
@ -1,7 +1,7 @@
|
|||||||
"""Router Audit Complet — import JSON, liste, detail, carte flux, carte applicative"""
|
"""Router Audit Complet — import JSON, liste, detail, carte flux, carte applicative"""
|
||||||
import json
|
import json
|
||||||
from fastapi import APIRouter, Request, Depends, UploadFile, File
|
from fastapi import APIRouter, Request, Depends, UploadFile, File
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
from ..dependencies import get_db, get_current_user, get_user_perms, can_view, base_context
|
from ..dependencies import get_db, get_current_user, get_user_perms, can_view, base_context
|
||||||
@ -192,6 +192,80 @@ async def audit_full_import(request: Request, db=Depends(get_db),
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/audit-full/export-csv")
|
||||||
|
async def audit_full_export_csv(request: Request, db=Depends(get_db)):
|
||||||
|
user = get_current_user(request)
|
||||||
|
if not user:
|
||||||
|
return RedirectResponse(url="/login")
|
||||||
|
|
||||||
|
import io, csv
|
||||||
|
filtre = request.query_params.get("filter", "")
|
||||||
|
search = request.query_params.get("q", "").strip()
|
||||||
|
domain = request.query_params.get("domain", "")
|
||||||
|
|
||||||
|
audits = get_latest_audits(db, limit=9999)
|
||||||
|
|
||||||
|
# Memes filtres que la page liste
|
||||||
|
if filtre == "reboot":
|
||||||
|
audits = [a for a in audits if a.reboot_required]
|
||||||
|
elif filtre == "uptime":
|
||||||
|
audits = [a for a in audits if a.uptime and ("month" in a.uptime or "year" in a.uptime)]
|
||||||
|
elif filtre and filtre.startswith("app_"):
|
||||||
|
app_patterns = {
|
||||||
|
"app_postgres": "postgres", "app_mariadb": "mariadb|mysqld",
|
||||||
|
"app_hana": "hdb|sapstart|HANA", "app_oracle": "ora_pmon|oracle",
|
||||||
|
"app_httpd": "httpd", "app_nginx": "nginx", "app_haproxy": "haproxy",
|
||||||
|
"app_tomcat": "tomcat|catalina", "app_nodejs": "node",
|
||||||
|
"app_redis": "redis", "app_mongodb": "mongod",
|
||||||
|
"app_elastic": "elasticsearch|opensearch", "app_container": "docker|podman",
|
||||||
|
"app_java": "java|\\.jar",
|
||||||
|
}
|
||||||
|
pattern = app_patterns.get(filtre, "")
|
||||||
|
if pattern:
|
||||||
|
ids = {r.id for r in db.execute(text("""
|
||||||
|
SELECT id FROM server_audit_full
|
||||||
|
WHERE status = 'ok'
|
||||||
|
AND (services::text ~* :pat OR listen_ports::text ~* :pat OR processes::text ~* :pat)
|
||||||
|
AND id IN (SELECT DISTINCT ON (hostname) id FROM server_audit_full WHERE status = 'ok' ORDER BY hostname, audit_date DESC)
|
||||||
|
"""), {"pat": pattern}).fetchall()}
|
||||||
|
audits = [a for a in audits if a.id in ids]
|
||||||
|
if domain:
|
||||||
|
zone_servers = {r.hostname for r in db.execute(text(
|
||||||
|
"SELECT s.hostname FROM servers s JOIN zones z ON s.zone_id = z.id WHERE z.name = :name"
|
||||||
|
), {"name": domain}).fetchall()}
|
||||||
|
if zone_servers:
|
||||||
|
audits = [a for a in audits if a.hostname in zone_servers]
|
||||||
|
else:
|
||||||
|
domain_servers = {r.hostname for r in db.execute(text("""
|
||||||
|
SELECT s.hostname FROM servers s JOIN domain_environments de ON s.domain_env_id = de.id
|
||||||
|
JOIN domains d ON de.domain_id = d.id WHERE d.code = :dc
|
||||||
|
"""), {"dc": domain}).fetchall()}
|
||||||
|
audits = [a for a in audits if a.hostname in domain_servers]
|
||||||
|
if search:
|
||||||
|
q = search.lower()
|
||||||
|
audits = [a for a in audits if q in a.hostname.lower()]
|
||||||
|
|
||||||
|
# Generer CSV
|
||||||
|
output = io.StringIO()
|
||||||
|
writer = csv.writer(output, delimiter=";")
|
||||||
|
writer.writerow(["Hostname", "OS", "Kernel", "Uptime", "Services", "Processus",
|
||||||
|
"Ports", "Connexions", "Reboot requis", "Date audit"])
|
||||||
|
for a in audits:
|
||||||
|
writer.writerow([
|
||||||
|
a.hostname, a.os_release or "", a.kernel or "", a.uptime or "",
|
||||||
|
a.svc_count, a.proc_count, a.port_count, a.conn_count,
|
||||||
|
"Oui" if a.reboot_required else "Non",
|
||||||
|
a.audit_date.strftime("%Y-%m-%d %H:%M") if a.audit_date else "",
|
||||||
|
])
|
||||||
|
|
||||||
|
output.seek(0)
|
||||||
|
return StreamingResponse(
|
||||||
|
iter(["\ufeff" + output.getvalue()]),
|
||||||
|
media_type="text/csv",
|
||||||
|
headers={"Content-Disposition": "attachment; filename=audit_serveurs.csv"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/audit-full/flow-map", response_class=HTMLResponse)
|
@router.get("/audit-full/flow-map", response_class=HTMLResponse)
|
||||||
async def audit_full_flow_map(request: Request, db=Depends(get_db)):
|
async def audit_full_flow_map(request: Request, db=Depends(get_db)):
|
||||||
user = get_current_user(request)
|
user = get_current_user(request)
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
<input type="file" name="file" accept=".json" class="text-xs" required>
|
<input type="file" name="file" accept=".json" class="text-xs" required>
|
||||||
<button type="submit" class="btn-primary px-4 py-2 text-sm" data-loading="Import en cours...|Insertion des donnees">Importer JSON</button>
|
<button type="submit" class="btn-primary px-4 py-2 text-sm" data-loading="Import en cours...|Insertion des donnees">Importer JSON</button>
|
||||||
</form>
|
</form>
|
||||||
|
<a href="/audit-full/export-csv{% if filter %}?filter={{ filter }}{% endif %}{% if search %}&q={{ search }}{% endif %}{% if domain %}&domain={{ domain }}{% endif %}" class="btn-sm bg-cyber-green text-black px-4 py-2">Exporter CSV</a>
|
||||||
<a href="/audit-full/flow-map" class="btn-sm bg-cyber-border text-cyber-accent px-4 py-2">Carte flux</a>
|
<a href="/audit-full/flow-map" class="btn-sm bg-cyber-border text-cyber-accent px-4 py-2">Carte flux</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user