mail: Kontakterreichbarkeit - Formpruefung, Domainpruefung, Einlieferer-Bericht
Vier Punkte aus kontakterreichbarkeit-mx-bounces-2026-09-15.md, ohne Veroeffentlichungssperre: - PlausibleEmailDomain: Domain ohne Endung oder mit Tippfehler-TLD ist ein 422 an der Legacy-API, deterministisch ohne DNS. - email_domain_checks + MailDomainChecker ueber DnsResolver-Schnittstelle (RFC 5321 impliziter MX, RFC 7505 Null MX, Unresolved ist kein Befund), Observer an Company/Contact, Job nach Commit, taeglicher Refresh, mail:check-domains fuer den Bestand. Nie eine Adresssperre aus DNS. - Admin > Reports > Einlieferer-Bericht: tote Adressen aus Sperrliste und Domainbefund, automatisch abgelehnte Meldungen mit Grund, Text zum Kopieren und CSV. Versendet nichts. - EmailBounceKind::Policy: 5.7.x wird protokolliert, zaehlt aber nicht auf den Schwellwert. Statuscode entscheidet vor Diagnosetext. Messung vorab: 6 von 877 API-Domains ohne Zustellweg, eine davon fuer 66 der 151 Ruecklaeufer verantwortlich. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fddfd8a61c
commit
ada41cb067
34 changed files with 1985 additions and 25 deletions
|
|
@ -1206,3 +1206,77 @@ ist weg (alle Tabellen neu angelegt 15:21:58–15:22:11). `composer run
|
|||
test` räumt den Cache vorher weg, `php artisan test` direkt nicht.
|
||||
`tests/TestCase.php` bricht jetzt hart ab, wenn die Verbindung nicht
|
||||
SQLite :memory: ist. Die Kopie muss neu eingespielt werden.
|
||||
|
||||
## Kontakterreichbarkeit — Umsetzung der Punkte 1–4 (16.09.2026)
|
||||
|
||||
Grundlage: `dev/2026-09-11-audit/kontakterreichbarkeit-mx-bounces-2026-09-15.md`.
|
||||
Vor der Entscheidung gemessen (Dev-DB, echtes DNS):
|
||||
|
||||
- 877 Firmen-Domains aus sechs Monaten API-Einlieferung: 870 mit MX, 1
|
||||
nur A (zulässiger Fallback), **6 ohne Zustellweg**, 0 Null MX, 0
|
||||
Resolver-Fehler. Die sechs stehen für 75 Meldungen; `sdmediapr.com`
|
||||
allein für 68 Meldungen einer Firma und **66 der 151 Rückläufer**.
|
||||
- Pressekontakte an Meldungen: 401 im Gesamtbestand, 21 in sechs
|
||||
Monaten — Benachrichtigungen gehen praktisch immer an die Firmenadresse.
|
||||
Grenze 2 des Dokuments (erster Kontakt verdeckt zweiten) ist damit ohne
|
||||
praktische Bedeutung und wurde nicht angefasst.
|
||||
- Mit Kontakten dazu (voller Lauf, 1 200 Domains): 10 Domains ohne
|
||||
Zustellweg, darunter `kaeser-stroebelde` (Punkt verloren) und
|
||||
`bester-immobilienmakler-neustadt-am-ruebenber` (bei 45 Zeichen
|
||||
abgeschnitten) — Datenqualität beim Einlieferer, nicht bei uns.
|
||||
|
||||
Entscheidung mit dem Nutzer: Punkte 1–4 jetzt, **keine**
|
||||
Veröffentlichungssperre (Abschnitt 6 / Stufe 3 des Dokuments) — ein bis
|
||||
zwei Fälle im Monat rechtfertigen die Zustandsmaschine nicht, die
|
||||
Sperrliste verhindert Wiederholungsmails bereits. Nach Beobachtung neu
|
||||
entscheiden.
|
||||
|
||||
**1. Formprüfung beim Einreichen** — `App\Rules\PlausibleEmailDomain`,
|
||||
deterministisch, ohne Netz: Domain braucht eine Endung, gültige Labels,
|
||||
alphabetische TLD (oder `xn--`), kurze Liste beweisbarer Tippfehler-TLDs
|
||||
(`.ded`, `.cmo` …). IDN wird per Punycode normalisiert. Greift an
|
||||
`company.email` und `contact.email` der Legacy-API; 422 mit klarem Grund.
|
||||
`info@hoerwelt-basel` bestand Laravels `email`-Regel bisher.
|
||||
|
||||
**2. Domainprüfung als Beobachtung** — `email_domain_checks` je Domain
|
||||
mit Zustand (`EmailDomainState`: Deliverable, ImplicitMx, NullMx,
|
||||
NoMailPath, DomainMissing, BrokenMx, Unresolved), Grund, Prüfzeit,
|
||||
Ablauf. `MailDomainChecker` über die Schnittstelle `DnsResolver`
|
||||
(`SystemDnsResolver` produktiv, `FakeDnsResolver` im Test — kein Test
|
||||
fragt echtes DNS). Regeln aus dem Dokument umgesetzt: A/AAAA-Fallback nur
|
||||
ohne MX, Null MX schlägt A, ein nutzbares MX-Ziel genügt, Timeout/SERVFAIL
|
||||
ist kein Befund und wird mit wachsendem Abstand wiederholt, positive
|
||||
Befunde gelten 30 Tage, negative 3. Anstoß über `EmailDomainObserver` an
|
||||
`Company` und `Contact` (Job `CheckEmailDomain` nach Commit, alle
|
||||
Strecken gleich), Auffrischung täglich 04:20 via `mail:check-domains
|
||||
--stale`, Bestandsaufnahme mit `mail:check-domains --months=`. **Aus
|
||||
einem DNS-Befund entsteht nie eine Adresssperre** — im Test verankert.
|
||||
Firmenseite im Admin zeigt den Befund als Badge.
|
||||
|
||||
**3. Einlieferer-Bericht** — Admin ▸ Reports ▸ Einlieferer-Bericht
|
||||
(`/admin/reports/einlieferer`), Auswahl Einlieferer und Zeitraum. Drei
|
||||
Quellen, in der Anzeige vereint: nicht erreichbare Adressen (Sperrliste
|
||||
und Domainbefund, letzterer als schärfere Aussage bevorzugt) mit Firma und
|
||||
Zahl betroffener Meldungen; automatisch abgelehnte Meldungen mit der
|
||||
Begründung aus dem Status-Log (`source = ki`); Richtlinien-Ablehnungen
|
||||
als Hinweis. Bericht als Text zum Kopieren und zwei CSV-Downloads.
|
||||
Versendet wird nichts — der Nutzer entscheidet, was er an connektar
|
||||
weitergibt. Service `SubmitterContactReport`, Wertobjekte
|
||||
`SubmitterSummary`, `BrokenContact`, `RejectedRelease`.
|
||||
|
||||
**4. Richtlinien-Ablehnungen zählen nicht** — neue Art
|
||||
`EmailBounceKind::Policy` (5.7.x, „Relay access denied", Transportregel,
|
||||
Spamfilter der Gegenseite): protokolliert und im Bericht sichtbar, aber
|
||||
ohne Wirkung auf den Schwellwert. Reihenfolge im Prozessor: Statuscode
|
||||
vor Diagnosetext — 5.1.x bleibt hart, auch wenn im Text „spam" steht
|
||||
(Freitext des eigenen Relays). Volles Postfach bleibt weich.
|
||||
|
||||
Tests: `MailDomainCheckTest` (11), `PlausibleEmailDomainTest` (5),
|
||||
`SubmitterContactReportTest` (8), `BounceMailProcessingTest` (+1, 10);
|
||||
zwei bestehende Tests von `assertNothingPushed` auf
|
||||
`assertNotPushed(ClassifyPressRelease)` verengt, weil der Observer jetzt
|
||||
einen Job anstößt.
|
||||
|
||||
Deploy: `migrate --force`, dann einmal `php artisan mail:check-domains
|
||||
--months=6 --limit=1500` (≈ 2 Minuten, nur DNS-Lesen). Danach ist der
|
||||
Bericht gefüllt.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue