feat: page web de rapport DNS detaille + simplification du mail
src/Controller/DnsReportController.php (nouveau):
- Route /email/configuration/{token} accessible via le lien dans le mail
- Utilise le messageId de l'EmailTracking comme token d'acces
(seuls les destinataires du mail ont le lien)
- Execute tous les checks en temps reel: DnsCheckService (SPF, DMARC, MX,
Bounce via dig @1.1.1.1), AwsSesService (domaine, 3 DKIM CNAME,
MAIL FROM MX/TXT, bounce notif), CloudflareService (zone, records),
MailcowService (domaine, DKIM, MX, autodiscover, autoconfig, SRV, MTA-STS)
- Enrichit chaque check avec la colonne Cloudflare
- Passe les resultats au template Twig pour affichage complet
templates/dns_report/index.html.twig (nouveau):
- Page glassmorphism avec header glass
- Resume en haut: 3 cards (verifications OK, erreurs, avertissements)
avec bordures laterales colorees vert/rouge/jaune
- Tableau par domaine avec 6 colonnes: Source (badge colore par type:
orange AWS, violet Mailcow, bleu Cloudflare, gris DNS), Verification,
Attendu, Dig (actuel), Cloudflare, Statut (rond colore)
- Section erreurs detaillees avec liste
- Section avertissements avec liste
- Footer "Esy-Infra - Service de monitoring d'infra"
templates/emails/dns_report.html.twig (simplifie):
- Mail ne contient plus les details: seulement un tableau avec
chaque domaine et son statut (OK vert / WARN jaune / KO rouge)
- Bouton "Voir le rapport complet" avec lien vers la page web
(VML fallback pour Outlook)
- Le lien utilise le placeholder __DNS_REPORT_URL__ remplace par
le MailerService avec le messageId du mail
src/Service/MailerService.php:
- Ajout du remplacement de __DNS_REPORT_URL__ par l'URL absolue
/email/configuration/{messageId} dans sendEmail(), au meme
endroit que __VIEW_URL__
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,90 +15,54 @@
|
||||
|
||||
<h1 style="font-size: 18px; font-weight: 700; margin-top: 16px; margin-right: 0; margin-bottom: 8px; margin-left: 0;">Rapport DNS</h1>
|
||||
<p style="font-size: 12px; color: #888888; margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
{{ date|date('d/m/Y H:i:s') }} • Domaines : <strong>{{ domains|join(', ') }}</strong>
|
||||
{{ date|date('d/m/Y H:i:s') }}
|
||||
</p>
|
||||
|
||||
{# ─── Resume ─── #}
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
{# ─── Statut par domaine ─── #}
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="border: 1px solid #e5e5e5; margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
<tr>
|
||||
<td style="padding-top: 8px; padding-bottom: 8px; padding-left: 12px; padding-right: 12px; background-color: #f0fdf4; border-left: 3px solid #16a34a; font-size: 12px; font-weight: 700; color: #16a34a;">
|
||||
✓ {{ successes|length }} verification(s) OK
|
||||
</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 12px; padding-right: 12px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Domaine</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 12px; padding-right: 12px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; text-align: center; width: 80px;">Statut</td>
|
||||
</tr>
|
||||
{% if errors|length > 0 %}
|
||||
<tr>
|
||||
<td style="padding-top: 8px; padding-bottom: 8px; padding-left: 12px; padding-right: 12px; background-color: #fef2f2; border-left: 3px solid #dc2626; font-size: 12px; font-weight: 700; color: #dc2626;">
|
||||
✗ {{ errors|length }} erreur(s)
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if warnings|length > 0 %}
|
||||
<tr>
|
||||
<td style="padding-top: 8px; padding-bottom: 8px; padding-left: 12px; padding-right: 12px; background-color: #fffbeb; border-left: 3px solid #f59e0b; font-size: 12px; font-weight: 700; color: #f59e0b;">
|
||||
⚠ {{ warnings|length }} avertissement(s)
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for domainData in domainResults %}
|
||||
{% set hasError = false %}
|
||||
{% set hasWarning = false %}
|
||||
{% for check in domainData.checks %}
|
||||
{% if check.status == 'error' %}{% set hasError = true %}{% endif %}
|
||||
{% if check.status == 'warning' %}{% set hasWarning = true %}{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td style="padding-top: 10px; padding-bottom: 10px; padding-left: 12px; padding-right: 12px; font-size: 14px; font-weight: 700; border-bottom: 1px solid #eeeeee;">
|
||||
{{ domainData.domain }}
|
||||
</td>
|
||||
<td style="padding-top: 10px; padding-bottom: 10px; padding-left: 12px; padding-right: 12px; font-size: 14px; font-weight: 700; text-align: center; border-bottom: 1px solid #eeeeee; color: {{ hasError ? '#dc2626' : (hasWarning ? '#f59e0b' : '#16a34a') }};">
|
||||
{% if hasError %}
|
||||
✗ KO
|
||||
{% elseif hasWarning %}
|
||||
⚠ WARN
|
||||
{% else %}
|
||||
✓ OK
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{# ─── Detail par domaine avec valeurs attendue / dig / cloudflare ─── #}
|
||||
{% for domainData in domainResults %}
|
||||
<h2 style="font-size: 16px; font-weight: 700; margin-top: 24px; margin-right: 0; margin-bottom: 8px; margin-left: 0; border-bottom: 2px solid #fabf04; padding-bottom: 4px;">
|
||||
{{ domainData.domain }}
|
||||
</h2>
|
||||
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="border: 1px solid #e5e5e5; margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
{# ─── Lien vers le detail ─── #}
|
||||
{% if reportUrl is defined and reportUrl %}
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin-top: 16px; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
<tr>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; width: 60px;">Type</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Check</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Attendu</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Dig (actuel)</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Cloudflare</td>
|
||||
<td style="background-color: #111827; color: #ffffff; padding-top: 8px; padding-bottom: 8px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; text-align: center; width: 30px;">OK</td>
|
||||
<td align="center" style="padding-top: 8px; padding-bottom: 8px;">
|
||||
<!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" href="{{ reportUrl }}" style="height:44px;v-text-anchor:middle;width:320px;" arcsize="0%" fillcolor="#fabf04" stroke="f">
|
||||
<center style="color:#111827;font-family:Arial,sans-serif;font-size:13px;font-weight:bold;text-transform:uppercase;letter-spacing:1px;">Voir le rapport complet</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<a href="{{ reportUrl }}" style="display: inline-block; background-color: #fabf04; border: 1px solid #e5a800; padding-top: 12px; padding-bottom: 12px; padding-left: 32px; padding-right: 32px; color: #111827; font-weight: 700; text-transform: uppercase; font-size: 13px; text-decoration: none; letter-spacing: 1px;">Voir le rapport complet</a>
|
||||
<!--<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
{% for check in domainData.checks %}
|
||||
<tr>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; border-bottom: 1px solid #eeeeee; color: #666666; vertical-align: top;">{{ check.type }}</td>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 9px; font-weight: 700; border-bottom: 1px solid #eeeeee; vertical-align: top;">{{ check.label }}</td>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 9px; border-bottom: 1px solid #eeeeee; color: #444444; vertical-align: top; word-break: break-all;">{{ check.expected|default('—') }}</td>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 9px; border-bottom: 1px solid #eeeeee; color: {{ check.status == 'ok' ? '#16a34a' : (check.status == 'error' ? '#dc2626' : '#92400e') }}; vertical-align: top; word-break: break-all;">{{ check.dig|default('—') }}</td>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 9px; border-bottom: 1px solid #eeeeee; color: {{ check.cf_status|default('') == 'ok' ? '#16a34a' : (check.cf_status|default('') == 'error' ? '#dc2626' : '#888888') }}; vertical-align: top; word-break: break-all;">{{ check.cloudflare|default('—') }}</td>
|
||||
<td style="padding-top: 5px; padding-bottom: 5px; padding-left: 8px; padding-right: 8px; font-size: 12px; text-align: center; border-bottom: 1px solid #eeeeee; font-weight: 700; color: {{ check.status == 'ok' ? '#16a34a' : (check.status == 'error' ? '#dc2626' : '#f59e0b') }}; vertical-align: top;">
|
||||
{{ check.status == 'ok' ? '✓' : (check.status == 'error' ? '✗' : '⚠') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endfor %}
|
||||
|
||||
{# ─── Erreurs detaillees ─── #}
|
||||
{% if errors|length > 0 %}
|
||||
<h2 style="font-size: 14px; font-weight: 700; color: #dc2626; margin-top: 16px; margin-right: 0; margin-bottom: 8px; margin-left: 0;">
|
||||
Erreurs a corriger ({{ errors|length }})
|
||||
</h2>
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="border: 1px solid #fca5a5; margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
{% for error in errors %}
|
||||
<tr>
|
||||
<td style="padding-top: 6px; padding-bottom: 6px; padding-left: 12px; padding-right: 12px; font-size: 11px; mso-line-height-rule: exactly; line-height: 16px; background-color: #fef2f2; color: #991b1b; border-bottom: 1px solid #fca5a5;">
|
||||
✗ {{ error }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if warnings|length > 0 %}
|
||||
<h2 style="font-size: 14px; font-weight: 700; color: #f59e0b; margin-top: 16px; margin-right: 0; margin-bottom: 8px; margin-left: 0;">
|
||||
Avertissements ({{ warnings|length }})
|
||||
</h2>
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="border: 1px solid #fde68a; margin-top: 0; margin-right: 0; margin-bottom: 16px; margin-left: 0;">
|
||||
{% for warning in warnings %}
|
||||
<tr>
|
||||
<td style="padding-top: 6px; padding-bottom: 6px; padding-left: 12px; padding-right: 12px; font-size: 11px; mso-line-height-rule: exactly; line-height: 16px; background-color: #fffbeb; color: #92400e; border-bottom: 1px solid #fde68a;">
|
||||
⚠ {{ warning }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user