Add weekly organization security reports
This commit is contained in:
@@ -95,6 +95,45 @@ def device(machine_name):
|
||||
return render_template("device.html", report=report, payload=payload, payload_pretty=json.dumps(payload, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
@app.get("/reports")
|
||||
@requires_auth
|
||||
def reports():
|
||||
with db_connection() as connection, connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT id, organization_name, period_start_utc, period_end_utc,
|
||||
generated_at, device_count, warning_count, critical_count,
|
||||
total_events
|
||||
FROM ocsentinel.weekly_organization_report
|
||||
ORDER BY period_end_utc DESC, organization_name
|
||||
"""
|
||||
)
|
||||
weekly_reports = cursor.fetchall()
|
||||
|
||||
return render_template("reports.html", reports=weekly_reports)
|
||||
|
||||
|
||||
@app.get("/reports/<int:report_id>")
|
||||
@requires_auth
|
||||
def weekly_report(report_id):
|
||||
with db_connection() as connection, connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT organization_name, period_start_utc, period_end_utc,
|
||||
generated_at, report_html
|
||||
FROM ocsentinel.weekly_organization_report
|
||||
WHERE id = %s
|
||||
""",
|
||||
(report_id,),
|
||||
)
|
||||
report = cursor.fetchone()
|
||||
|
||||
if report is None:
|
||||
abort(404)
|
||||
|
||||
return render_template("weekly_report.html", report=report)
|
||||
|
||||
|
||||
@app.get("/healthz")
|
||||
def healthz():
|
||||
try:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
:root { --ink:#17201d; --muted:#66736d; --paper:#f5f3eb; --panel:#fffdf7; --line:#d8d4c6; --green:#236342; --lime:#c7ee6b; --amber:#b86613; --red:#a8342b; }
|
||||
* { box-sizing:border-box; }
|
||||
body { margin:0; color:var(--ink); background:radial-gradient(circle at 86% -10%, #d6efad 0, transparent 28rem), var(--paper); font-family:Georgia, 'Times New Roman', serif; }
|
||||
.masthead { height:70px; padding:0 6vw; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--line); background:rgba(255,253,247,.82); backdrop-filter:blur(10px); }
|
||||
.masthead { height:70px; padding:0 6vw; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--line); background:rgba(255,253,247,.82); backdrop-filter:blur(10px); }.header-links { display:flex; gap:14px; align-items:center; }.header-links a { color:var(--green); font:700 12px Arial,sans-serif; text-decoration:none; }
|
||||
.brand { color:var(--ink); font:700 20px/1 Arial,sans-serif; text-decoration:none; letter-spacing:-.04em; }.brand span { display:inline-grid; place-items:center; margin-right:7px; width:28px; height:28px; background:var(--green); color:#fff; border-radius:50%; font-size:11px; letter-spacing:0; }.badge,.eyebrow { color:var(--muted); font:700 10px/1 Arial,sans-serif; text-transform:uppercase; letter-spacing:.12em; }.badge { border:1px solid var(--line); padding:6px 8px; border-radius:20px; }
|
||||
main { max-width:1280px; margin:auto; padding:58px 6vw 80px; }.hero { max-width:650px; margin-bottom:32px; }.hero h1 { font-size:clamp(34px,5vw,64px); line-height:.98; letter-spacing:-.06em; margin:10px 0; }.hero p { color:var(--muted); font-size:18px; }.hero.compact h1 { font-size:48px; }
|
||||
.metrics { display:grid; grid-template-columns:repeat(5,1fr); gap:1px; margin:25px 0 46px; border:1px solid var(--line); background:var(--line); }.metrics article { min-height:130px; padding:20px; background:var(--panel); }.metrics span { display:block; color:var(--muted); font:700 10px Arial,sans-serif; letter-spacing:.09em; text-transform:uppercase; }.metrics strong { display:block; margin-top:16px; font:700 31px Arial,sans-serif; letter-spacing:-.05em; }.metrics .timestamp { font-size:14px; line-height:1.25; letter-spacing:-.02em; }.warning { color:var(--amber); }.critical { color:var(--red); }
|
||||
.panel { margin-top:26px; padding:26px; background:var(--panel); border:1px solid var(--line); }.panel-heading h2 { margin:8px 0 22px; font-size:28px; letter-spacing:-.04em; }.alert-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:12px; }.alert-card { padding:17px; border-left:5px solid var(--amber); background:#fff7e9; color:var(--ink); text-decoration:none; }.alert-card.critical { border-color:var(--red); background:#fff0ed; }.alert-card span,.alert-card small { display:block; font:700 10px Arial,sans-serif; letter-spacing:.08em; text-transform:uppercase; }.alert-card strong { display:block; margin:10px 0; font:700 22px Arial,sans-serif; letter-spacing:-.04em; }
|
||||
table { width:100%; border-collapse:collapse; font-family:Arial,sans-serif; font-size:13px; } th { text-align:left; color:var(--muted); font-size:10px; letter-spacing:.1em; text-transform:uppercase; } th,td { padding:13px 8px; border-bottom:1px solid var(--line); } td a { color:var(--green); font-weight:700; text-decoration:none; }.state { display:inline-block; padding:4px 7px; border-radius:12px; background:#e2efe6; color:var(--green); font:700 10px Arial,sans-serif; text-transform:uppercase; }.state.warning { background:#fff0d7; color:var(--amber); }.state.critical { background:#ffe0db; color:var(--red); } pre { margin:0; padding:18px; overflow:auto; color:#dce7da; background:#13221b; border-radius:4px; font:12px/1.5 'Cascadia Code',Consolas,monospace; }.table-wrap { overflow:auto; }
|
||||
.report-frame { background:#fff; border:1px solid var(--line); box-shadow:0 12px 40px rgba(20,35,27,.1); }
|
||||
@media (max-width:850px) { .metrics { grid-template-columns:repeat(2,1fr); }.metrics article:last-child { grid-column:span 2; }.masthead { padding:0 5vw; }.badge { display:none; } main { padding:38px 5vw; } }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<body>
|
||||
<header class="masthead">
|
||||
<a href="/" class="brand"><span>OC</span> Sentinel Debug</a>
|
||||
<div class="badge">read-only database console</div>
|
||||
<div class="header-links"><a href="{{ url_for('reports') }}">Wochenberichte</a><div class="badge">read-only database console</div></div>
|
||||
</header>
|
||||
<main>{% block content %}{% endblock %}</main>
|
||||
</body>
|
||||
|
||||
8
infra/debug-dashboard/templates/reports.html
Normal file
8
infra/debug-dashboard/templates/reports.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Wochenberichte - OCSentinel Debug{% endblock %}
|
||||
{% block content %}
|
||||
<section class="hero compact"><p class="eyebrow">Archiv</p><h1>Wochenberichte</h1><p>Je Organisation automatisch durch n8n erzeugt.</p></section>
|
||||
<section class="panel"><div class="table-wrap"><table><thead><tr><th>Organisation</th><th>Zeitraum</th><th>Geraete</th><th>Warnung</th><th>Kritisch</th><th>Events</th><th>Erstellt</th></tr></thead><tbody>
|
||||
{% for row in reports %}<tr><td><a href="{{ url_for('weekly_report', report_id=row[0]) }}">{{ row[1] }}</a></td><td>{{ row[2] }} bis {{ row[3] }}</td><td>{{ row[5] }}</td><td>{{ row[6] }}</td><td>{{ row[7] }}</td><td>{{ row[8] }}</td><td>{{ row[4] }}</td></tr>{% else %}<tr><td colspan="7">Noch keine Wochenberichte erzeugt.</td></tr>{% endfor %}
|
||||
</tbody></table></div></section>
|
||||
{% endblock %}
|
||||
5
infra/debug-dashboard/templates/weekly_report.html
Normal file
5
infra/debug-dashboard/templates/weekly_report.html
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ report[0] }} - Wochenbericht{% endblock %}
|
||||
{% block content %}
|
||||
<section class="report-frame">{{ report[4] | safe }}</section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user