Send a test mail from the admin and log why delivery failed
Form submissions marked as not sent gave no reason: the transport exception was swallowed. Every send now goes through MailCheck, which logs the server's answer and a hint in German to var/log/mail.log (server lines only, no credentials). The system screen shows the mail route, sends a test mail to any address and lists recent attempts (ADR 121). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
090436233d
commit
d313156c73
11 files changed
+501
-13
No files matched your search
@@ -11,6 +11,7 @@ use App\Ops\Backup;
|
||||
use App\Ops\Environment;
|
||||
use App\Ops\HttpsRedirect;
|
||||
use App\Ops\Installation;
|
||||
use App\Ops\MailCheck;
|
||||
use App\Ops\OriginCheck;
|
||||
use App\Ops\SitePackage;
|
||||
use App\Ops\Updater;
|
||||
@@ -19,6 +20,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
/**
|
||||
@@ -30,7 +33,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
*/
|
||||
final class SystemController extends AbstractController
|
||||
{
|
||||
public function __construct(private Updater $updater, private Backup $backup, private ArchiveUpdate $archive, private Installation $installation, private string $adminPath, private string $databaseUrl, private string $mediaDirectory, private string $projectDir, private string $siteOrigin, private OriginCheck $originCheck, private HttpKernelInterface $kernel, private PageRepository $pages, private SiteSettings $settings, private SitePackage $packages, private HttpsRedirect $https)
|
||||
public function __construct(private Updater $updater, private Backup $backup, private ArchiveUpdate $archive, private Installation $installation, private string $adminPath, private string $databaseUrl, private string $mediaDirectory, private string $projectDir, private string $siteOrigin, private OriginCheck $originCheck, private HttpKernelInterface $kernel, private PageRepository $pages, private SiteSettings $settings, private SitePackage $packages, private HttpsRedirect $https, private MailCheck $mail)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,6 +74,9 @@ final class SystemController extends AbstractController
|
||||
// HTTPS erzwingen und HSTS (ADR 120): der Stand in public/.htaccess.
|
||||
'https' => $this->https->state(),
|
||||
'secure' => $request->isSecure(),
|
||||
// E-Mail-Versand (ADR 121): Weg ohne Passwort, Testmail, die letzten Versuche.
|
||||
'mail' => $this->mailFacts(),
|
||||
'mailResult' => $request->getSession()->remove('mail_result'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -520,6 +526,51 @@ final class SystemController extends AbstractController
|
||||
return $this->back($result['message'], $result['ok'] ? 'success' : 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Testmail an eine frei eingetragene Adresse (ADR 121). Das Ergebnis - Meldung, Hinweis,
|
||||
* Antworten des Servers - steht danach auf der Seite und in var/log/mail.log.
|
||||
*/
|
||||
#[Route('/%kant.admin_path%/system/testmail', name: 'admin_system_testmail', methods: ['POST'])]
|
||||
public function testMail(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
if (!$this->isCsrfTokenValid('system', $request->request->getString('_csrf_token'))) {
|
||||
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
|
||||
}
|
||||
$an = trim($request->request->getString('to'));
|
||||
$von = trim($request->request->getString('from')) ?: $an;
|
||||
foreach ([$an, $von] as $adresse) {
|
||||
if (false === filter_var($adresse, FILTER_VALIDATE_EMAIL)) {
|
||||
return $this->back(sprintf('„%s“ ist keine gültige E-Mail-Adresse.', mb_substr($adresse, 0, 100)));
|
||||
}
|
||||
}
|
||||
$website = rtrim($this->siteOrigin, '/') ?: $request->getSchemeAndHttpHost();
|
||||
$email = (new Email())
|
||||
->from(new Address($von))
|
||||
->to(new Address($an))
|
||||
->subject('Testmail von '.(parse_url($website, PHP_URL_HOST) ?: $website))
|
||||
->text("Diese Testmail kommt aus der Verwaltung von {$website} (System → E-Mail-Versand).
|
||||
|
||||
Wenn sie angekommen ist, funktioniert der Versand der Formulare auf demselben Weg.
|
||||
");
|
||||
$request->getSession()->set('mail_result', ['an' => $an, 'von' => $von] + $this->mail->send($email, 'Testmail'));
|
||||
|
||||
return $this->redirect($this->generateUrl('admin_system').'#mail');
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function mailFacts(): array
|
||||
{
|
||||
$route = $this->mail->route();
|
||||
|
||||
return $route + [
|
||||
'quelle' => $this->environmentSource('MAILER_DSN'),
|
||||
// Wer sich mit einer Adresse anmeldet, versendet meist auch nur unter ihr.
|
||||
'absender' => false !== filter_var($route['benutzer'], FILTER_VALIDATE_EMAIL) ? $route['benutzer'] : '',
|
||||
'protokoll' => $this->mail->recent(),
|
||||
];
|
||||
}
|
||||
|
||||
/** Die Gegenstelle zur Prüfung oben: öffentlich, und sie verrät nichts als diese Kennung. */
|
||||
#[Route(OriginCheck::PATH, name: 'site_origin_probe', methods: ['GET'], priority: 10)]
|
||||
public function originProbe(): Response
|
||||
|
||||
Reference in new issue
Block a user