Package 1: SsrPurgeService posts to the node /__purge (endpoint+secret in var/vitec-ssr-purge.env, outside git) and logs every attempt to tx_vitec_ssrpurgelog. DataHandler hooks queue per request and flush once: pages with nav-relevant field changes and all tx_news_/tx_vitec_ records purge everything, other page/content edits purge the hosting page path. Gated by the routing flag and the new Site Set switch vitec.ssr.autoPurge. Flipping the SSR switch ON purges everything once. CLI: vitec:ssr-purge, vitec:ssr-health (samples /__health into tx_vitec_ssrhealthlog, 14 days kept, scheduler-ready). Package 2: dashboard widgets vitecSsrStatus (live health, release, switch, last purge) and vitecSsrStats (purge counts, failures, uptime, response time). Package 3: backend module web_vitecssr with manual purge (paths or everything) and both protocols. Tested end to end against the running node (purge single/all 200, health sample logged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
2.8 KiB
PHP
72 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\Command;
|
|
|
|
use Evomedien\Vitec\Service\SsrPurgeService;
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
|
use TYPO3\CMS\Core\Http\RequestFactory;
|
|
|
|
#[AsCommand(
|
|
name: 'vitec:ssr-health',
|
|
description: 'Sample the SSR node /__health into tx_vitec_ssrhealthlog (run via scheduler for history)',
|
|
)]
|
|
final class SsrHealthSampleCommand extends Command
|
|
{
|
|
private const KEEP_DAYS = 14;
|
|
|
|
public function __construct(
|
|
private readonly SsrPurgeService $purgeService,
|
|
private readonly RequestFactory $requestFactory,
|
|
private readonly ConnectionPool $connectionPool,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$endpoint = $this->purgeService->getHealthEndpoint();
|
|
if ($endpoint === '') {
|
|
$output->writeln('<error>SSR_HEALTH_ENDPOINT missing in var/vitec-ssr-purge.env</error>');
|
|
return Command::INVALID;
|
|
}
|
|
$row = ['crdate' => time(), 'ok' => 0, 'release_id' => '', 'cached_pages' => 0, 'cache_mb' => 0, 'response_ms' => 0];
|
|
$start = microtime(true);
|
|
try {
|
|
$response = $this->requestFactory->request($endpoint, 'GET', [
|
|
'timeout' => 4, 'connect_timeout' => 2, 'http_errors' => false,
|
|
]);
|
|
$row['response_ms'] = (int)round((microtime(true) - $start) * 1000);
|
|
if ($response->getStatusCode() === 200) {
|
|
$data = json_decode((string)$response->getBody(), true);
|
|
$row['ok'] = !empty($data['ok']) ? 1 : 0;
|
|
$row['release_id'] = substr((string)($data['release']['release'] ?? ''), 0, 100);
|
|
$row['cached_pages'] = (int)($data['cachedPages'] ?? 0);
|
|
$row['cache_mb'] = (int)($data['cacheMB'] ?? 0);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$row['response_ms'] = (int)round((microtime(true) - $start) * 1000);
|
|
}
|
|
$conn = $this->connectionPool->getConnectionForTable('tx_vitec_ssrhealthlog');
|
|
$conn->insert('tx_vitec_ssrhealthlog', $row);
|
|
$conn->executeStatement(
|
|
'DELETE FROM tx_vitec_ssrhealthlog WHERE crdate < ?',
|
|
[time() - self::KEEP_DAYS * 86400]
|
|
);
|
|
$output->writeln(sprintf(
|
|
'%s release=%s pages=%d cache=%dMB %dms',
|
|
$row['ok'] ? '<info>ok</info>' : '<error>DOWN</error>',
|
|
$row['release_id'] ?: '-',
|
|
$row['cached_pages'],
|
|
$row['cache_mb'],
|
|
$row['response_ms']
|
|
));
|
|
return Command::SUCCESS;
|
|
}
|
|
}
|