- Turnstile für Gäste und unbestätigte Konten (TurnstileVerifier, <x-turnstile>); ist Cloudflare nicht erreichbar, wird durchgelassen und im Verlauf, in der Kontakt-Queue und in der Betriebsüberwachung markiert - Inhaltsfilter des Kontaktformulars (fremde Links, Floskel mit Seitentitel, Zeichensalat, Sperrliste, Absenden unter 3 Sekunden); Treffer landen in der Spam-Quarantäne ohne Mail, Löschung nach 30 Tagen - Admin: Filter „Spam“, Sammelaktion, „Kein Spam“ holt die Mail nach - Registrierung lehnt Zufallsnamen ab; users:prune-bot-registrations (täglicher Lauf erst mit USERS_PRUNE_BOT_REGISTRATIONS=true) - contact-messages:quarantine-spam für den Altbestand - CSP erlaubt challenges.cloudflare.com, Log-Kanal spam - Datenschutzerklärung: Abschnitt 6a Cloudflare Turnstile (Vorschlag) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.1 KiB
PHP
60 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\ContactMessage;
|
|
use App\Services\Spam\ContactSpamFilter;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Einmalig für den Altbestand (Spamschutz, Entscheidung 2): offene
|
|
* Kontakt-Anliegen von Gästen nach denselben Regeln wie beim Eingang in die
|
|
* Spam-Quarantäne legen. Was die Regeln nicht erkennen, markiert der Admin
|
|
* über die Sammelaktion der Kontakt-Queue.
|
|
*/
|
|
class QuarantineContactSpam extends Command
|
|
{
|
|
protected $signature = 'contact-messages:quarantine-spam {--dry-run : Nur auflisten, nichts ändern}';
|
|
|
|
protected $description = 'Legt offene Kontakt-Anliegen von Gästen, die der Spamfilter erkennt, in die Quarantäne.';
|
|
|
|
public function handle(ContactSpamFilter $filter): int
|
|
{
|
|
$hits = ContactMessage::query()
|
|
->open()
|
|
->whereNull('user_id')
|
|
->orderBy('id')
|
|
->get()
|
|
->map(fn (ContactMessage $message): array => [
|
|
'message' => $message,
|
|
// Ohne Zeitstempel: Der Altbestand hatte kein Formular-Feld dafür.
|
|
'reason' => $filter->reasonFor((string) $message->name, (string) $message->message, null),
|
|
])
|
|
->filter(fn (array $hit): bool => $hit['reason'] !== null);
|
|
|
|
$this->table(
|
|
['Vorgang', 'Eingang', 'Grund', 'Nachricht'],
|
|
$hits->map(fn (array $hit): array => [
|
|
$hit['message']->reference(),
|
|
$hit['message']->created_at?->format('d.m.Y'),
|
|
$hit['reason'],
|
|
Str::limit(Str::squish((string) $hit['message']->message), 60),
|
|
])->all(),
|
|
);
|
|
|
|
if ($this->option('dry-run')) {
|
|
$this->info("Probelauf: {$hits->count()} Anliegen würden als Spam markiert.");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
foreach ($hits as $hit) {
|
|
$hit['message']->markAsSpam(null, $hit['reason']);
|
|
}
|
|
|
|
$this->info("{$hits->count()} Anliegen als Spam markiert. Offen bleiben: ".ContactMessage::query()->open()->count().'.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|