Widgets, backend module template and flash messages now match the rest of the backend, which is English. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
89 lines
3.5 KiB
PHP
89 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\Widgets;
|
|
|
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
|
use TYPO3\CMS\Dashboard\Widgets\WidgetConfigurationInterface;
|
|
use TYPO3\CMS\Dashboard\Widgets\WidgetInterface;
|
|
|
|
/**
|
|
* Dashboard tile: purge and health statistics from the two protocol
|
|
* tables. History depth = what vitec:ssr-health sampling (cron, every
|
|
* 5 minutes) and the purge log hold (14 days).
|
|
*/
|
|
final class SsrStatsWidget implements WidgetInterface
|
|
{
|
|
public function __construct(
|
|
private readonly WidgetConfigurationInterface $configuration,
|
|
private readonly ConnectionPool $connectionPool,
|
|
) {}
|
|
|
|
public function renderWidgetContent(): string
|
|
{
|
|
$now = time();
|
|
$d1 = $now - 86400;
|
|
$d7 = $now - 7 * 86400;
|
|
|
|
$purges24h = $this->count('tx_vitec_ssrpurgelog', 'crdate > ' . $d1);
|
|
$purges7d = $this->count('tx_vitec_ssrpurgelog', 'crdate > ' . $d7);
|
|
$failed7d = $this->count('tx_vitec_ssrpurgelog', 'crdate > ' . $d7 . ' AND success = 0');
|
|
$full7d = $this->count('tx_vitec_ssrpurgelog', "crdate > " . $d7 . " AND paths = '*'");
|
|
|
|
$samples24h = $this->count('tx_vitec_ssrhealthlog', 'crdate > ' . $d1);
|
|
$ok24h = $this->count('tx_vitec_ssrhealthlog', 'crdate > ' . $d1 . ' AND ok = 1');
|
|
$uptime = $samples24h > 0 ? round($ok24h / $samples24h * 100, 1) . ' %' : '-';
|
|
$avgMs = $this->scalar('SELECT ROUND(AVG(response_ms)) FROM tx_vitec_ssrhealthlog WHERE crdate > ' . $d1 . ' AND ok = 1');
|
|
$maxCache = $this->scalar('SELECT MAX(cache_mb) FROM tx_vitec_ssrhealthlog WHERE crdate > ' . $d7);
|
|
|
|
$rows = [
|
|
['Purges (24 h / 7 days)', $purges24h . ' / ' . $purges7d],
|
|
['of which full purges (7 d)', (string)$full7d],
|
|
['Failed purges (7 d)', $failed7d > 0
|
|
? '<span class="badge badge-danger">' . $failed7d . '</span>'
|
|
: '<span class="badge badge-success">0</span>'],
|
|
['Availability (24 h)', htmlspecialchars($uptime)
|
|
. ' <small>(' . $ok24h . '/' . $samples24h . ' samples)</small>'],
|
|
['Avg. response time (24 h)', $avgMs !== null ? $avgMs . ' ms' : '-'],
|
|
['Max. cache size (7 d)', $maxCache !== null ? $maxCache . ' MB' : '-'],
|
|
];
|
|
|
|
$html = '<table class="table table-striped table-sm"><tbody>';
|
|
foreach ($rows as [$label, $value]) {
|
|
$html .= '<tr><th style="width:55%">' . $label . '</th><td>' . $value . '</td></tr>';
|
|
}
|
|
$html .= '</tbody></table>';
|
|
if ($samples24h === 0) {
|
|
$html .= '<p class="text-muted" style="font-size:11px">No samples yet: the cron job for <code>vitec:ssr-health</code> fills this every 5 minutes.</p>';
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
public function getOptions(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
private function count(string $table, string $where): int
|
|
{
|
|
try {
|
|
return (int)$this->connectionPool->getConnectionForTable($table)
|
|
->executeQuery('SELECT COUNT(*) FROM ' . $table . ' WHERE ' . $where)->fetchOne();
|
|
} catch (\Throwable) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private function scalar(string $sql): ?string
|
|
{
|
|
try {
|
|
$v = $this->connectionPool->getConnectionForTable('tx_vitec_ssrhealthlog')
|
|
->executeQuery($sql)->fetchOne();
|
|
return $v === null || $v === false ? null : (string)$v;
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|