Initial commit

This commit is contained in:
2026-05-16 12:05:36 -03:00
parent 0ce972a361
commit e82cee97a7
65 changed files with 9051 additions and 5 deletions
+250
View File
@@ -0,0 +1,250 @@
{% extends "base.html" %}
{% block content %}
<header class="dw-page-header">
<h1>Operational Overview</h1>
<p>Deterministic detection with LLM-assisted reporting.</p>
</header>
<section class="dw-overview-filter" aria-label="Traffic filters">
<div class="dw-chart-controls">
<select id="traffic-period" aria-label="Traffic period">
<option value="all" selected>All reports</option>
<option value="24h">24h</option>
<option value="7d">7d</option>
<option value="30d">30d</option>
<option value="365d">Year</option>
<option value="custom">Custom</option>
</select>
<input id="traffic-from" type="date" aria-label="Traffic from date">
<input id="traffic-to" type="date" aria-label="Traffic to date">
<select id="traffic-domain" aria-label="Traffic domain">
<option value="">All domains</option>
{% for domain in domains %}
<option value="{{ domain }}">{{ domain }}</option>
{% endfor %}
</select>
</div>
</section>
<section class="dw-metrics-grid" aria-label="Operational metrics">
<a class="dw-metric-card dw-metric-link" id="monitored-domains-card" href="/inboxes">
<span class="dw-kicker">Monitored Domains</span>
<strong id="metric-domains">{{ data.domains }}</strong>
<small id="metric-domain-target">View inboxes</small>
</a>
<article class="dw-metric-card">
<span class="dw-kicker">DMARC Reports</span>
<strong id="metric-reports">{{ data.reports_today }}</strong>
</article>
<article class="dw-metric-card">
<span class="dw-kicker">Reported Emails</span>
<strong id="metric-messages">{{ data.messages_today }}</strong>
</article>
<article class="dw-metric-card">
<span class="dw-kicker">DMARC Pass Rate</span>
<strong id="metric-pass-rate" class="{{ 'dw-success-value' if data.dmarc_pass_rate_value is none or data.dmarc_pass_rate_value >= 95 else ('dw-warning-value' if data.dmarc_pass_rate_value >= 80 else 'dw-danger-value') }}">{{ data.dmarc_pass_rate }}</strong>
</article>
<article class="dw-metric-card">
<span class="dw-kicker">Passing Emails</span>
<strong id="metric-pass-count">{{ data.dmarc_pass_count }}</strong>
</article>
<article class="dw-metric-card dw-metric-card-critical">
<span class="dw-kicker">Failed Emails</span>
<strong id="metric-fail-count">{{ data.dmarc_fail_count }}</strong>
</article>
<article class="dw-metric-card">
<span class="dw-kicker">Unknown Sources</span>
<strong id="metric-unknown">{{ data.unknown_sources }}</strong>
</article>
<article class="dw-metric-card">
<span class="dw-kicker">Last Successful Check</span>
<code>{{ data.last_check | fmt_dt }}</code>
</article>
</section>
<section class="dw-chart-card dw-overview-chart">
<div class="dw-card-head">
<h3>Traffic Distribution <span id="traffic-period-label">{{ traffic_label }}</span></h3>
<div class="dw-legend">
<span><i class="dw-dot-valid"></i>Valid</span>
<span><i class="dw-dot-failed"></i>Failed</span>
</div>
</div>
<div class="dw-bars" id="traffic-bars" aria-label="Traffic distribution for imported reports in the selected period">
{% for bucket in traffic %}
<a href="/alerts?status=&date_from={{ bucket.date_from }}&date_to={{ bucket.date_to }}" title="{{ bucket.label }} · {{ bucket.total }} messages, {{ bucket.failed }} failed" style="height: {{ [bucket.height, 3] | max }}%;" aria-label="Show alerts for {{ bucket.label }}">
<span class="dw-bar-valid" style="flex-grow: {{ bucket.valid }};"></span>
<span class="dw-bar-failed" style="flex-grow: {{ bucket.failed }};"></span>
</a>
{% else %}
<span style="height: 3%;"></span>
{% endfor %}
</div>
</section>
<section class="dw-overview-summary">
<div class="dw-section-heading">
<span class="material-symbols-outlined dw-filled-icon">auto_awesome</span>
<h2 id="summary-title">Portfolio DMARC posture</h2>
<button class="button-secondary dw-summary-run" type="button" id="run-daily-summary">
<span class="material-symbols-outlined text-[18px]">play_arrow</span>
Generate Digest
</button>
</div>
<article class="dw-summary-card">
<span class="dw-ai-label">AI Assisted</span>
<div class="dw-summary-copy">
{% set summary_parts = data.summary.split("Actions:", 1) %}
<div>{{ summary_parts[0] }}</div>
{% if summary_parts | length > 1 %}
<div class="dw-recommendations">
<span>Recommended Actions</span>
<ul>
{% for action in summary_parts[1].replace(".", "").split(";") %}
{% if action.strip() %}
<li><span class="material-symbols-outlined">task_alt</span>{{ action.strip() }}</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</div>
<div class="inbox-action-result" id="daily-summary-result" role="status" aria-live="polite"></div>
</article>
</section>
<script>
(() => {
const period = document.getElementById("traffic-period");
const domain = document.getElementById("traffic-domain");
const bars = document.getElementById("traffic-bars");
const periodLabel = document.getElementById("traffic-period-label");
const summaryButton = document.getElementById("run-daily-summary");
const summaryResult = document.getElementById("daily-summary-result");
const summaryTitle = document.getElementById("summary-title");
const dateFrom = document.getElementById("traffic-from");
const dateTo = document.getElementById("traffic-to");
const summaryCopy = document.querySelector(".dw-summary-copy");
const monitoredDomainsCard = document.getElementById("monitored-domains-card");
const metricDomains = document.getElementById("metric-domains");
const metricDomainTarget = document.getElementById("metric-domain-target");
const metricReports = document.getElementById("metric-reports");
const metricMessages = document.getElementById("metric-messages");
const metricPassRate = document.getElementById("metric-pass-rate");
const metricPassCount = document.getElementById("metric-pass-count");
const metricFailCount = document.getElementById("metric-fail-count");
const metricUnknown = document.getElementById("metric-unknown");
const scopeText = (periodLabelValue) => `${periodLabelValue || period.options[period.selectedIndex].text} · ${domain.value || "All domains"}`;
const renderDomainCard = (totalDomains) => {
if (domain.value) {
monitoredDomainsCard.href = `/domains/${encodeURIComponent(domain.value)}`;
metricDomains.textContent = "1";
metricDomainTarget.textContent = domain.value;
} else {
monitoredDomainsCard.href = "/inboxes";
metricDomains.textContent = totalDomains;
metricDomainTarget.textContent = "View inboxes";
}
};
const render = (buckets) => {
bars.innerHTML = "";
if (!buckets.length) {
const bar = document.createElement("span");
bar.style.height = "3%";
bars.appendChild(bar);
return;
}
buckets.forEach((bucket) => {
const bar = document.createElement("a");
bar.style.height = `${Math.max(bucket.height, 3)}%`;
bar.title = `${bucket.label} · ${bucket.total} messages, ${bucket.failed} failed`;
bar.href = `/alerts?status=&date_from=${encodeURIComponent(bucket.date_from)}&date_to=${encodeURIComponent(bucket.date_to)}`;
bar.setAttribute("aria-label", `Show alerts for ${bucket.label}`);
const valid = document.createElement("span");
valid.className = "dw-bar-valid";
valid.style.flexGrow = bucket.valid || 0;
const failed = document.createElement("span");
failed.className = "dw-bar-failed";
failed.style.flexGrow = bucket.failed || 0;
bar.appendChild(valid);
bar.appendChild(failed);
bars.appendChild(bar);
});
};
const passClass = (value) => value === null || value >= 95 ? "dw-success-value" : (value >= 80 ? "dw-warning-value" : "dw-danger-value");
const formatDate = (value) => {
if (!value) return "";
const parts = value.split("-");
return parts.length === 3 ? `${parts[2]}/${parts[1]}/${parts[0]}` : value;
};
const renderSummary = (plain) => {
const parts = (plain || "").split("Actions:");
summaryCopy.innerHTML = "";
const body = document.createElement("div");
body.textContent = parts[0] || "";
summaryCopy.appendChild(body);
if (parts.length > 1) {
const rec = document.createElement("div");
rec.className = "dw-recommendations";
rec.innerHTML = "<span>Recommended Actions</span>";
const list = document.createElement("ul");
parts[1].replace(/\.$/, "").split(";").map((item) => item.trim()).filter(Boolean).forEach((item) => {
const li = document.createElement("li");
li.innerHTML = '<span class="material-symbols-outlined">task_alt</span>';
li.appendChild(document.createTextNode(item));
list.appendChild(li);
});
rec.appendChild(list);
summaryCopy.appendChild(rec);
}
};
const refresh = async () => {
const params = new URLSearchParams({ period: period.value });
if (domain.value) params.set("domain", domain.value);
if (period.value === "custom") {
if (dateFrom.value) params.set("date_from", dateFrom.value);
if (dateTo.value) params.set("date_to", dateTo.value);
}
const response = await fetch(`/api/overview?${params}`, { credentials: "same-origin" });
if (response.ok) {
const data = await response.json();
const label = scopeText(data.period_label);
periodLabel.textContent = label;
metricReports.textContent = data.metrics.reports_today;
metricMessages.textContent = data.metrics.messages_today;
metricPassCount.textContent = data.metrics.dmarc_pass_count;
metricFailCount.textContent = data.metrics.dmarc_fail_count;
metricUnknown.textContent = data.metrics.unknown_sources;
renderDomainCard(data.metrics.domains);
metricPassRate.textContent = data.metrics.dmarc_pass_rate;
metricPassRate.className = passClass(data.metrics.dmarc_pass_rate_value);
summaryTitle.textContent = domain.value ? `${domain.value} DMARC posture` : "Portfolio DMARC posture";
renderSummary(data.metrics.summary || "");
render(data.buckets || []);
}
};
period.addEventListener("change", refresh);
domain.addEventListener("change", refresh);
dateFrom.addEventListener("change", refresh);
dateTo.addEventListener("change", refresh);
summaryButton.addEventListener("click", async () => {
summaryButton.disabled = true;
summaryResult.className = "inbox-action-result is-running";
summaryResult.textContent = "Generating digest...";
try {
const response = await fetch("/api/admin/scheduler/daily-summary", { method: "POST", headers: window.adminPostHeaders, credentials: "same-origin" });
const data = await response.json();
if (!response.ok) throw new Error(data.detail || "Digest generation failed.");
summaryResult.className = "inbox-action-result is-success";
summaryResult.textContent = "Digest generated.";
await refresh();
} catch (error) {
summaryResult.className = "inbox-action-result is-error";
summaryResult.textContent = error.message || "Digest generation failed.";
} finally {
summaryButton.disabled = false;
}
});
})();
</script>
{% endblock %}