feat: add evidence-linked DNS health guidance

This commit is contained in:
Christian Krakau-Louis
2026-05-23 11:56:51 +02:00
parent 0512f8cfa9
commit a6c795ee16
3 changed files with 426 additions and 4 deletions
+96
View File
@@ -112,6 +112,7 @@
</div>
<!-- Compliance Chart -->
<section id="compliance-chart-section">
{% call card() %}
{% call card_header() %}
{% call card_title() %}Compliance Over Time{% endcall %}
@@ -125,8 +126,74 @@
</div>
{% endcall %}
{% endcall %}
</section>
<!-- DNS Health Summary -->
<section id="dns-health-summary">
{% call card() %}
{% call card_header() %}
<div class="flex items-center justify-between">
{% call card_title() %}DNS Health Summary{% endcall %}
<span class="inline-flex items-center rounded px-2 py-1 text-xs font-semibold capitalize"
:class="dnsHealthStatusClass"
x-text="dnsHealth.status || 'checking'"></span>
</div>
{% call card_description() %}
Evidence-linked authentication posture and enforcement readiness
{% endcall %}
{% endcall %}
{% call card_content() %}
<div class="grid gap-4 lg:grid-cols-3">
<template x-for="check in dnsHealth.checks" :key="check.key">
<div class="rounded border border-base-300 p-4">
<div class="flex items-center justify-between">
<h3 class="font-semibold" x-text="check.label"></h3>
<span class="rounded px-2 py-1 text-xs font-semibold capitalize"
:class="check.status === 'pass' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'"
x-text="check.status"></span>
</div>
<p class="mt-2 text-sm text-base-content/70" x-text="check.message"></p>
<div class="mt-3 space-y-1">
<template x-for="item in check.evidence" :key="item.label + item.value">
<a :href="item.href" class="block text-xs link link-primary">
<span x-text="item.label"></span>:
<span class="font-mono break-all" x-text="item.value"></span>
</a>
</template>
</div>
</div>
</template>
</div>
<div class="mt-5 space-y-3">
<template x-for="recommendation in dnsHealth.recommendations" :key="recommendation.type">
<div class="rounded border border-base-300 p-4">
<div class="flex items-start gap-3">
<span class="mt-1 h-2.5 w-2.5 rounded-full"
:class="recommendationSeverityClass(recommendation.severity)"></span>
<div class="min-w-0 flex-1">
<h3 class="font-semibold" x-text="recommendation.title"></h3>
<p class="mt-1 text-sm text-base-content/70" x-text="recommendation.detail"></p>
<p class="mt-2 text-sm font-medium" x-text="recommendation.action"></p>
<div class="mt-3 flex flex-wrap gap-2">
<template x-for="item in recommendation.evidence" :key="item.label + item.value">
<a :href="item.href" class="badge badge-outline gap-1">
<span x-text="item.label"></span>
<span x-text="item.value"></span>
</a>
</template>
</div>
</div>
</div>
</div>
</template>
</div>
{% endcall %}
{% endcall %}
</section>
<!-- DNS Records -->
<section id="dns-records">
{% call card() %}
{% call card_header() %}
{% call card_title() %}DNS Records{% endcall %}
@@ -217,8 +284,10 @@
</div>
{% endcall %}
{% endcall %}
</section>
<!-- Source Report Table -->
<section id="sending-sources">
{% call card() %}
{% call card_header() %}
<div class="flex items-center justify-between">
@@ -405,6 +474,7 @@
{% endcall %}
{% endcall %}
{% endcall %}
</section>
<!-- Recent Reports -->
{% call card() %}
@@ -511,6 +581,11 @@ function domainDetailsApp(domainId) {
dkim: false,
dkimSelectors: []
},
dnsHealth: {
status: '',
checks: [],
recommendations: []
},
selectors: [],
reportSelectors: [],
newSelector: '',
@@ -528,6 +603,7 @@ function domainDetailsApp(domainId) {
init() {
this.fetchDomainStats();
this.fetchDNSRecords();
this.fetchDNSHealth();
this.fetchSelectors();
this.fetchReports();
this.fetchSources();
@@ -563,6 +639,13 @@ function domainDetailsApp(domainId) {
return 'Verified';
},
get dnsHealthStatusClass() {
if (this.dnsHealth.status === 'healthy') return 'bg-green-100 text-green-700';
if (this.dnsHealth.status === 'degraded') return 'bg-yellow-100 text-yellow-800';
if (this.dnsHealth.status === 'critical') return 'bg-red-100 text-red-700';
return 'bg-base-200 text-base-content/70';
},
recommendationSeverityClass(severity) {
if (severity === 'error') return 'bg-red-500';
if (severity === 'warning') return 'bg-yellow-500';
@@ -593,6 +676,17 @@ function domainDetailsApp(domainId) {
}
},
async fetchDNSHealth() {
try {
const response = await fetch(`/api/v1/domains/${this.domainId}/dns/health`);
if (response.ok) {
this.dnsHealth = await response.json();
}
} catch (error) {
console.error('Error fetching DNS health:', error);
}
},
async fetchSelectors() {
try {
const response = await fetch(`/api/v1/domains/${this.domainId}/selectors`);
@@ -621,6 +715,7 @@ function domainDetailsApp(domainId) {
// Refresh selectors (both manual and report) and DNS check
this.fetchSelectors();
this.fetchDNSRecords();
this.fetchDNSHealth();
} else {
const err = await response.json();
this.selectorError = err.detail || 'Failed to add selector';
@@ -641,6 +736,7 @@ function domainDetailsApp(domainId) {
// Refresh selectors (both manual and report) and DNS check
this.fetchSelectors();
this.fetchDNSRecords();
this.fetchDNSHealth();
} else {
console.error('Error deleting selector:', response.status);
}