email_suppressions/email_bounces als Zustand und Ereignis-Log; ein MessageSending-Listener streicht gesperrte Empfänger aus JEDER ausgehenden Mail und setzt den Return-Path. Zweiter Aufnahmepfad: SMTP-Ablehnungen im Worker — 5.1.x sperrt den Empfänger, Relay-Ablehnungen zählen für den Circuit Breaker, der den Versand anhält statt gesunde Adressen zu sperren. X-Pk-Ref-Header auf den Mails, API-Meldungen fallen auf die Firmen-E-Mail zurück, der Bounce-Import schreibt in die zentrale Liste. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
53 lines
1.7 KiB
PHP
53 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Mail\Concerns;
|
|
|
|
use App\Enums\EmailNotificationType;
|
|
use App\Models\User;
|
|
use Illuminate\Mail\Mailables\Headers;
|
|
use Illuminate\Support\Facades\URL;
|
|
|
|
/**
|
|
* Macht eine Benachrichtigungs-Mail abbestellbar: signierter Abmelde-Link
|
|
* für den Mail-Footer (ohne Login nutzbar, ohne Ablauf — der Link aus alten
|
|
* Mails bleibt gültig) plus die List-Unsubscribe-Header nach RFC 8058, mit
|
|
* denen Mail-Clients (Gmail, Apple Mail …) einen nativen
|
|
* „Abbestellen"-Button anbieten.
|
|
*
|
|
* Der Link wird im Konstruktor der Mailable erzeugt (configureUnsubscribe),
|
|
* damit er beim Queuen mit serialisiert wird.
|
|
*/
|
|
trait Unsubscribable
|
|
{
|
|
public string $unsubscribeUrl;
|
|
|
|
public EmailNotificationType $notificationType;
|
|
|
|
protected function configureUnsubscribe(User $user, EmailNotificationType $type): void
|
|
{
|
|
$this->notificationType = $type;
|
|
$this->unsubscribeUrl = URL::signedRoute('email-preferences.unsubscribe', [
|
|
'user' => $user->id,
|
|
'type' => $type->value,
|
|
]);
|
|
}
|
|
|
|
public function headers(): Headers
|
|
{
|
|
return new Headers(text: array_filter([
|
|
'List-Unsubscribe' => '<'.$this->unsubscribeUrl.'>',
|
|
'List-Unsubscribe-Post' => 'List-Unsubscribe=One-Click',
|
|
// Bounce-Referenz (Roadmap 1b.6), sofern die Mailable eine hat.
|
|
(string) config('bounces.reference_header', 'X-Pk-Ref') => $this->bounceReference(),
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* Ein DSN bringt die Original-Header zurück; hiermit ordnet der
|
|
* Bounce-Parser den Rückläufer dem Datensatz zu. Mailables überschreiben.
|
|
*/
|
|
public function bounceReference(): ?string
|
|
{
|
|
return null;
|
|
}
|
|
}
|