Client-Error-Endpoint POST /api/vitec/client-error (0072-FE / 0077-BE)
Neue PSR-15-Middleware ClientErrorMiddleware: nimmt Browser-Fehler des
React-Frontends als JSON {url, message, stack, release, userAgent}
entgegen und antwortet 204 ohne Body. Schutz: 30 Meldungen pro Minute
und IP (Sliding Window ueber TYPO3-RateLimiter, 429 + Retry-After),
8-KB-Body-Limit (413, Content-Length und tatsaechliche Bytes), kein
JSON-Objekt oder leere message = 400, GET = 405. Keine Session, keine
Cookies; der /_json-Praefix funktioniert ebenfalls.
Speicherung in der neuen Tabelle tx_vitec_clienterrorlog mit
anonymisierter IP (IPv4 letztes Oktett 0, IPv6 /48). Sichtbar als
Tabelle "Client errors (last 24 h)" im Modul VITEC SSR (mit Stack als
Aufklapper) und als Zeile "Client errors reported (24 h / 7 d)" in der
Dashboard-Kachel VITEC: SSR statistics.
CLI vitec:client-error-cleanup [--days=30] loescht Eintraege aelter als
30 Tage (taeglich per Cron).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff6f86b829
commit
5121eaa000
7 files changed
+360
No files matched your search
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'vitec:client-error-cleanup',
|
||||
description: 'Delete client error reports (tx_vitec_clienterrorlog) older than 30 days; run daily via cron',
|
||||
)]
|
||||
final class ClientErrorCleanupCommand extends Command
|
||||
{
|
||||
private const TABLE = 'tx_vitec_clienterrorlog';
|
||||
private const DEFAULT_DAYS = 30;
|
||||
|
||||
public function __construct(private readonly ConnectionPool $connectionPool)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('days', 'd', InputOption::VALUE_REQUIRED, 'Keep entries younger than this many days', (string)self::DEFAULT_DAYS);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$days = (int)$input->getOption('days');
|
||||
if ($days < 1) {
|
||||
$output->writeln('<error>--days must be at least 1</error>');
|
||||
return Command::INVALID;
|
||||
}
|
||||
$cutoff = time() - $days * 86400;
|
||||
$conn = $this->connectionPool->getConnectionForTable(self::TABLE);
|
||||
$deleted = $conn->executeStatement('DELETE FROM ' . self::TABLE . ' WHERE crdate < ?', [$cutoff]);
|
||||
$remaining = (int)$conn->executeQuery('SELECT COUNT(*) FROM ' . self::TABLE)->fetchOne();
|
||||
$output->writeln(sprintf(
|
||||
'deleted %d client error report(s) older than %d days (before %s), %d remaining',
|
||||
$deleted,
|
||||
$days,
|
||||
date('Y-m-d H:i', $cutoff),
|
||||
$remaining
|
||||
));
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user