vitec:ssr-health --test-mail: send a sample alert through the real alert path

Deliverability check (e.g. mail-tester.com) with the same sender, mailer and plain-text format as a real alert. Optional address; without one the configured alert recipients get it. No health sample is taken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
orascheandClaude Opus 5.5 committed 2026-11-12 15:39:53 +01:00
1 parent 4f47382679
commit 6a00926b89
1 file changed
+52 -3
@@ -9,6 +9,7 @@ use Evomedien\Vitec\Service\SsrPurgeService;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Http\RequestFactory; use TYPO3\CMS\Core\Http\RequestFactory;
@@ -33,8 +34,24 @@ final class SsrHealthSampleCommand extends Command
parent::__construct(); parent::__construct();
} }
protected function configure(): void
{
$this->addOption(
'test-mail',
null,
InputOption::VALUE_OPTIONAL,
'Send a sample alert mail through the real alert path and exit (no health sample). Optional address; default: the configured alert recipients',
false
);
}
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$testMail = $input->getOption('test-mail');
if ($testMail !== false) {
return $this->sendTestMail((string)$testMail, $output);
}
$endpoint = $this->purgeService->getHealthEndpoint(); $endpoint = $this->purgeService->getHealthEndpoint();
if ($endpoint === '') { if ($endpoint === '') {
$output->writeln('<error>SSR_HEALTH_ENDPOINT missing in var/vitec-ssr-purge.env</error>'); $output->writeln('<error>SSR_HEALTH_ENDPOINT missing in var/vitec-ssr-purge.env</error>');
@@ -204,6 +221,38 @@ final class SsrHealthSampleCommand extends Command
return Command::SUCCESS; return Command::SUCCESS;
} }
/**
* Deliverability check (e.g. mail-tester.com): same sender, mailer and
* plain-text format as a real alert, clearly marked as a test.
*/
private function sendTestMail(string $address, OutputInterface $output): int
{
$override = [];
if ($address !== '') {
if (!GeneralUtility::validEmail($address)) {
$output->writeln('<error>invalid address: ' . $address . '</error>');
return Command::INVALID;
}
$override = [$address];
}
$this->sendAlert(
'[VITEC SSR] TEST: alert mail check',
"This is a test of the VITEC SSR alert mail - nothing is wrong.\n\n"
. "Real alerts look like this one and are sent when the 5-minute health\n"
. "check sees the SSR server go down or come back, and when the\n"
. "auto-failover switches the site to SPA delivery.\n\n"
. 'Time: ' . date('d.m.Y H:i:s') . "\n"
. 'Current state: switch vitec.ssr.enabled = '
. ($this->flagService->isEnabledInSettings() ? 'ON' : 'OFF')
. ', flag file ' . ($this->flagService->isFlagSet() ? 'present (SSR active)' : 'missing (SPA delivery)') . ".\n\n"
. "Recipients are maintained in the TYPO3 backend:\n"
. "Site Management > Settings > VITEC > SSR alert mail recipients.\n",
$output,
$override
);
return Command::SUCCESS;
}
/** /**
* Mails are best effort: a mail problem must never break the sampler * Mails are best effort: a mail problem must never break the sampler
* (the cron would stop logging history). Recipients come from the site * (the cron would stop logging history). Recipients come from the site
@@ -211,13 +260,13 @@ final class SsrHealthSampleCommand extends Command
* fallback SSR_ALERT_EMAIL in var/vitec-ssr-purge.env - none at all * fallback SSR_ALERT_EMAIL in var/vitec-ssr-purge.env - none at all
* means "alerts off", which the output states so the cron log shows why. * means "alerts off", which the output states so the cron log shows why.
*/ */
private function sendAlert(string $subject, string $body, OutputInterface $output): void private function sendAlert(string $subject, string $body, OutputInterface $output, array $override = []): void
{ {
$alert = $this->purgeService->getAlertRecipients(); $alert = $this->purgeService->getAlertRecipients();
if ($alert['invalid'] !== []) { if ($alert['invalid'] !== [] && $override === []) {
$output->writeln('<comment>skipped invalid alert addresses: ' . implode(', ', $alert['invalid']) . '</comment>'); $output->writeln('<comment>skipped invalid alert addresses: ' . implode(', ', $alert['invalid']) . '</comment>');
} }
$recipients = $alert['recipients']; $recipients = $override !== [] ? $override : $alert['recipients'];
if ($recipients === []) { if ($recipients === []) {
$output->writeln('<comment>transition detected but no alert recipients are configured (Site settings › VITEC or SSR_ALERT_EMAIL) - no mail sent</comment>'); $output->writeln('<comment>transition detected but no alert recipients are configured (Site settings › VITEC or SSR_ALERT_EMAIL) - no mail sent</comment>');
return; return;