feat(ansible/caddy): Ajoute le domaine signature.ludikevent.fr à la configuration Caddy.
 feat(src/Service/Mailer): Améliore l'envoi d'emails avec désinscription et Message-ID.
 feat(templates/mails): Met à jour le logo de l'email pour LudikEvent.
```
This commit is contained in:
Serreau Jovann
2026-01-15 18:54:50 +01:00
parent b1b2687320
commit 2e157e1f83
3 changed files with 76 additions and 33 deletions

View File

@@ -1,4 +1,4 @@
intranet.ludikevent.fr {
intranet.ludikevent.fr, signature.ludikevent.fr {
tls {
dns cloudflare KL6pZ-Z_12_zbnM2TtFDIsKM8A-HLPhU5GJJbKTW
}

View File

@@ -1,5 +1,6 @@
<?php
namespace App\Service\Mailer;
use Doctrine\ORM\EntityManagerInterface;
@@ -7,10 +8,8 @@ use Exception;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\IdentificationHeader;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@@ -21,15 +20,19 @@ class Mailer
private readonly MailerInterface $mailer;
public function __construct(
MailerInterface $mailer,
MailerInterface $mailer,
private readonly EntityManagerInterface $entityManager,
private readonly UrlGeneratorInterface $urlGenerator,
private readonly ?Profiler $profiler,
private readonly Environment $environment,
) {
private readonly UrlGeneratorInterface $urlGenerator,
private readonly ?Profiler $profiler,
private readonly Environment $environment,
)
{
$this->mailer = $mailer;
}
/**
* Convertit le contenu MJML en HTML via le binaire MJML installé sur le serveur.
*/
private function convertMjmlToHtml(string $mjmlContent): string
{
$process = new Process(['mjml', '--stdin']);
@@ -44,57 +47,98 @@ class Mailer
return $process->getOutput();
} catch (ProcessFailedException|Exception) {
return ''; // Retourne vide en cas d'échec
return '';
}
}
/**
* @param string $address
* @param string $addressName
* @param string $subject
* @param string $template
* @param array<string, mixed> $data
* @param array<\Symfony\Component\Mime\Part\DataPart> $files
* Envoie un email avec headers de désinscription et Message-ID personnalisé.
* * @param string $address Email du destinataire
* @param string $addressName Nom du destinataire
* @param string $subject Sujet de l'email
* @param string $template Chemin du template Twig/MJML
* @param array<string, mixed> $data Données à passer au template
* @param array<\Symfony\Component\Mime\Part\DataPart> $files Pièces jointes
*/
public function send(
string $address,
string $addressName,
string $subject,
string $template,
array $data,
array $files = []
): void {
array $data,
array $files = []
): void
{
$domain = "ludikevent.fr";
$dest = new Address($address, $addressName);
$src = new Address("contact@e-page.fr", "E-PAGE");
$src = new Address("contact@" . $domain, "Ludikevent");
// 1. Génération du Message-ID unique
$messageId = sprintf('<%s.%s@%s>',
bin2hex(random_bytes(12)),
time(),
$domain
);
// 2. Préparation de l'URL de désinscription
$unsubscribeUrl = "https://intranet.ludikevent.fr/unscribe/" . urlencode($address);
$mail = (new Email())
->subject($subject)
->to($dest)
->from($src);
// 3. Configuration des Headers techniques
$headers = $mail->getHeaders();
// Identification unique
$headers->addIdHeader('Message-ID', $messageId);
// RFC 2369 & 8058 (Bouton désinscription dans le client mail)
$headers->addTextHeader('List-Unsubscribe', sprintf('<mailto:unscribe@%s>, <%s>', $domain, $unsubscribeUrl));
$headers->addTextHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click');
// 4. Génération du contenu via Twig et MJML
$mjmlGenerator = $this->environment->render($template, [
'system' => [
'subject' => $subject,
'path' => $_ENV['PATH_URL'],
'path' => $_ENV['PATH_URL'] ?? 'https://intranet.ludikevent.fr',
'unsubscribe_url' => $unsubscribeUrl,
'message_id' => $messageId
],
'datas' => $data,
]);
$htmlContent = $this->convertMjmlToHtml($mjmlGenerator);
$txtContent = $this->environment->render('txt-'.$template,[
'system' => [
'subject' => $subject,
'path' => $_ENV['PATH_URL'],
],
'datas' => $data,
]);
// 5. Version Texte Brut (Fallback)
try {
$txtContent = $this->environment->render('txt-' . $template, [
'system' => [
'subject' => $subject,
'path' => $_ENV['PATH_URL'] ?? 'https://ludikevent.fr',
'unsubscribe_url' => $unsubscribeUrl
],
'datas' => $data,
]);
$mail->text($txtContent);
} catch (Exception) {
// Si le template TXT n'existe pas, on laisse le mailer gérer le fallback ou on ignore
}
// 6. Ajout des pièces jointes
foreach ($files as $file) {
$mail->addPart($file);
}
$mail->html($htmlContent);
$mail->text($txtContent);
$this->mailer->send($mail);
}
$mail->html($htmlContent);
// 7. Envoi final
try {
$this->mailer->send($mail);
} catch (TransportExceptionInterface $e) {
// Optionnel : Logger l'erreur ici
throw $e;
}
}
}

View File

@@ -22,7 +22,6 @@
{# Section d'en-tête #}
<mj-section background-color="#ffffff" padding-bottom="0px">
<mj-column>
{# Logo mis à jour pour SARL SITECONSEIL #}
<mj-image src="{{ system.path }}{{ asset('assets/images/logo.png') }}" alt="Logo LudikEvent" align="center" width="150px" padding-bottom="20px"></mj-image>
</mj-column>
</mj-section>