mail: zentrale Bounce-Suppression mit Versand-Gate und Circuit Breaker (Roadmap 1b.6, Schritte 1–2)
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>
This commit is contained in:
parent
87f95fb7a7
commit
0a658bb34b
25 changed files with 1312 additions and 4 deletions
|
|
@ -4,9 +4,11 @@ namespace App\Console\Commands;
|
|||
|
||||
use App\Enums\ActivityType;
|
||||
use App\Enums\EmailNotificationType;
|
||||
use App\Enums\EmailSuppressionReason;
|
||||
use App\Models\NewsletterSubscription;
|
||||
use App\Models\User;
|
||||
use App\Services\ActivityLog;
|
||||
use App\Services\Mail\EmailSuppressionService;
|
||||
use App\Services\Newsletter\NewsletterSyncService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -42,6 +44,7 @@ class ImportNewsletterBounces extends Command
|
|||
public function __construct(
|
||||
private readonly ActivityLog $activityLog,
|
||||
private readonly NewsletterSyncService $newsletterSync,
|
||||
private readonly EmailSuppressionService $suppressions,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
|
@ -73,6 +76,13 @@ class ImportNewsletterBounces extends Command
|
|||
$unknown = [];
|
||||
|
||||
foreach ($emails as $email) {
|
||||
// Seit 1b.6 zuerst die zentrale Sperrliste: Sie wirkt auf JEDE
|
||||
// Mailstrecke, nicht nur auf Newsletter — und auch auf Adressen,
|
||||
// die weder Konto noch Anmeldung haben (Pressekontakte, Firmen).
|
||||
if (! $dryRun) {
|
||||
$this->suppressions->suppress($email, EmailSuppressionReason::HardBounce, 'import', 'Bounce-Liste '.basename($path));
|
||||
}
|
||||
|
||||
$touchedUsers = $this->suppressUsers($email, $dryRun, $optedOut, $alreadyOptedOut);
|
||||
$touchedSubscriptions = $this->suppressSubscriptions($email, $dryRun, $unsubscribed, $alreadyUnsubscribed);
|
||||
|
||||
|
|
@ -85,6 +95,7 @@ class ImportNewsletterBounces extends Command
|
|||
}
|
||||
|
||||
$prefix = $dryRun ? '[Dry-Run] ' : '';
|
||||
$this->info(sprintf('%s%d Adressen in die Sperrliste (email_suppressions) übernommen.', $prefix, count($emails)));
|
||||
$this->info(sprintf(
|
||||
'%s%d Adressen gelesen: %d abgemeldet, %d waren bereits abgemeldet, %d ohne User-Konto.',
|
||||
$prefix,
|
||||
|
|
|
|||
36
app/Console/Commands/MailCircuitCommand.php
Normal file
36
app/Console/Commands/MailCircuitCommand.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Mail\MailCircuitBreaker;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Zustand des Mail-Circuit-Breakers anzeigen oder die Pause aufheben.
|
||||
*/
|
||||
class MailCircuitCommand extends Command
|
||||
{
|
||||
protected $signature = 'mail:circuit {--reset : Pause aufheben und Zähler zurücksetzen}';
|
||||
|
||||
protected $description = 'Zeigt, ob der Mailversand angehalten ist (Circuit Breaker), und hebt die Pause auf Wunsch auf.';
|
||||
|
||||
public function handle(MailCircuitBreaker $breaker): int
|
||||
{
|
||||
if ($this->option('reset')) {
|
||||
$breaker->reset();
|
||||
$this->info('Circuit Breaker zurückgesetzt — der Versand läuft wieder. Liegengebliebene Mails: php artisan queue:retry all');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$until = $breaker->openUntil();
|
||||
|
||||
if ($until !== null) {
|
||||
$this->warn(sprintf('Versand ANGEHALTEN bis %s.', $until->format('d.m.Y H:i')));
|
||||
} else {
|
||||
$this->info(sprintf('Versand läuft. Relay-Ablehnungen im aktuellen Fenster: %d.', $breaker->pendingRejections()));
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue