chore: complete update

This commit is contained in:
2025-07-17 16:24:20 +02:00
parent 899227b0a4
commit 64a7051137
1300 changed files with 85570 additions and 2756 deletions

View File

@@ -0,0 +1,340 @@
<?php
namespace App\Application\Admin;
use App\Framework\Attributes\Route;
use App\Framework\Auth\Auth;
use App\Framework\Config\TypedConfiguration;
use App\Framework\Core\VersionInfo;
use App\Framework\DI\DefaultContainer;
use App\Framework\Http\HttpResponse;
use App\Framework\Http\Method;
use App\Framework\Http\Response;
use App\Framework\Meta\MetaData;
use App\Framework\Meta\OpenGraphTypeWebsite;
use App\Framework\Performance\MemoryUsageTracker;
use App\Framework\Router\Result\ViewResult;
use App\Framework\Http\Session\SessionManager;
use App\Framework\Http\Status;
use Dom\HTMLDocument;
final readonly class Dashboard
{
public function __construct(
private DefaultContainer $container,
private VersionInfo $versionInfo,
private MemoryUsageTracker $memoryTracker,
private TypedConfiguration $config,
) {}
#[Auth]
#[Route(path: '/admin', method: Method::GET)]
public function show(): ViewResult
{
$stats = [
'frameworkVersion' => $this->versionInfo->getVersion(),
'phpVersion' => PHP_VERSION,
'memoryUsage' => $this->formatBytes(memory_get_usage(true)),
'peakMemoryUsage' => $this->formatBytes(memory_get_peak_usage(true)),
'serverInfo' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown',
'serverTime' => date('Y-m-d H:i:s'),
'timezone' => date_default_timezone_get(),
'operatingSystem' => PHP_OS,
'loadedExtensions' => $this->getLoadedExtensions(),
'sessionCount' => $this->getActiveSessionCount(),
'uptime' => $this->getServerUptime(),
'servicesCount' => 4,#count($this->container->getServiceIds()),
];
#debug($stats);
return new ViewResult(
template: 'dashboard',
metaData: new MetaData('Admin Dashboard'),
data: [
'title' => 'Admin Dashboard',
'stats' => $stats
]
);
}
#[Auth]
#[Route(path: '/admin/routes', method: Method::GET)]
public function routes(): ViewResult
{
$routeRegistry = $this->container->get('App\Framework\Router\RouteRegistry');
$routes = $routeRegistry->getRoutes();
// Sort routes by path for better readability
usort($routes, function($a, $b) {
return strcmp($a->path, $b->path);
});
return new ViewResult(
template: 'admin/routes',
metaData: new MetaData('', ''),
data: [
'title' => 'Routen-Übersicht',
'routes' => $routes
]
);
}
#[Auth]
#[Route(path: '/admin/services', method: Method::GET)]
public function services(): ViewResult
{
$services = $this->container->getServiceIds();
sort($services);
return new ViewResult(
template: 'admin/services',
data: [
'title' => 'Registrierte Dienste',
'services' => $services
]
);
}
#[Auth]
#[Route(path: '/admin/environment', method: Method::GET)]
public function environment(): ViewResult
{
$env = [];
foreach ($_ENV as $key => $value) {
// Maskiere sensible Daten
if (str_contains(strtolower($key), 'password') ||
str_contains(strtolower($key), 'secret') ||
str_contains(strtolower($key), 'key')) {
$value = '********';
}
$env[$key] = $value;
}
ksort($env);
dd($env);
return new ViewResult(
template: 'admin/environment',
metaData: new MetaData('', ''),
data: [
'title' => 'Umgebungsvariablen',
'env' => $env
]
);
}
#[Auth]
#[Route('/admin/phpinfo')]
#[Route(path: '/admin/phpinfo/{mode}', method: Method::GET)]
public function phpInfo(int $mode = 1): Response
{
ob_start();
phpinfo($mode);
$phpinfo = ob_get_clean();
// Extraktion des <body> Inhalts, um nur den relevanten Teil anzuzeigen
preg_match('/<body[^>]*>(.*?)<\/body>/si', $phpinfo, $matches);
$body = $matches[1] ?? $phpinfo;
// Entfernen der Navigation-Links am Anfang
#$body = preg_replace('/<div class="center">(.*?)<\/div>/si', '', $body, 1);
#debug($body);
// Hinzufügen von eigenen Styles
$customStyles = '<style>
.phpinfo { font-family: system-ui, sans-serif; line-height: 1.5; }
.phpinfo table { border-collapse: collapse; width: 100%; margin-bottom: 1rem; }
.phpinfo td, .phpinfo th { padding: 0.5rem; border: 1px solid #ddd; }
.phpinfo h1, .phpinfo h2 { margin-bottom: 1rem; }
.phpinfo hr { margin: 2rem 0; }
</style>';
$responseBody = '<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP Info</title>
<link rel="stylesheet" href="/css/admin.css">
' . $customStyles . '
</head>
<body class="admin-page">
<div class="admin-header">
<h1>PHP Info</h1>
<a href="/admin" class="btn">Zurück zum Dashboard</a>
</div>
<div class="admin-content">
<div class="phpinfo">' . $body . '</div>
</div>
</body>
</html>';
echo $responseBody;
die();
return new HttpResponse(status: Status::OK, body: $responseBody);
}
#[Auth]
#[Route(path: '/admin/performance', method: Method::GET)]
public function performance(): ViewResult
{
$performanceData = [
'currentMemoryUsage' => $this->formatBytes(memory_get_usage(true)),
'peakMemoryUsage' => $this->formatBytes(memory_get_peak_usage(true)),
'memoryLimit' => $this->formatBytes($this->getMemoryLimitInBytes()),
'memoryUsagePercentage' => round((memory_get_usage(true) / $this->getMemoryLimitInBytes()) * 100, 2),
'loadAverage' => function_exists('sys_getloadavg') ? sys_getloadavg() : ['N/A', 'N/A', 'N/A'],
'opcacheEnabled' => function_exists('opcache_get_status') ? 'Ja' : 'Nein',
'executionTime' => number_format(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], 4) . ' Sekunden',
'includedFiles' => count(get_included_files()),
];
if (function_exists('opcache_get_status')) {
try {
$opcacheStatus = opcache_get_status(false);
if ($opcacheStatus !== false) {
$performanceData['opcacheMemoryUsage'] = $this->formatBytes($opcacheStatus['memory_usage']['used_memory']);
$performanceData['opcacheCacheHits'] = number_format($opcacheStatus['opcache_statistics']['hits']);
$performanceData['opcacheMissRate'] = number_format($opcacheStatus['opcache_statistics']['misses'] /
($opcacheStatus['opcache_statistics']['hits'] + $opcacheStatus['opcache_statistics']['misses']) * 100, 2) . '%';
}
} catch (\Throwable $e) {
// OPCache Status konnte nicht abgerufen werden
$performanceData['opcacheError'] = $e->getMessage();
}
}
return new ViewResult(
template: 'performance',
data: [
'title' => 'Performance-Daten',
'performance' => $performanceData
]
);
}
#[Auth]
#[Route(path: '/admin/redis', method: Method::GET)]
public function redisInfo(): ViewResult
{
$redisInfo = [];
try {
$redis = $this->container->get('Predis\Client');
$info = $redis->info();
$redisInfo['status'] = 'Verbunden';
$redisInfo['version'] = $info['redis_version'];
$redisInfo['uptime'] = $this->formatUptime((int)$info['uptime_in_seconds']);
$redisInfo['memory'] = $this->formatBytes((int)$info['used_memory']);
$redisInfo['peak_memory'] = $this->formatBytes((int)$info['used_memory_peak']);
$redisInfo['clients'] = $info['connected_clients'];
$redisInfo['keys'] = $redis->dbsize();
// Einige Schlüssel auflisten (begrenzt auf 50)
$keys = $redis->keys('*');
$redisInfo['key_sample'] = array_slice($keys, 0, 50);
} catch (\Throwable $e) {
$redisInfo['status'] = 'Fehler: ' . $e->getMessage();
}
return new ViewResult(
template: 'admin/redis',
data: [
'title' => 'Redis Information',
'redis' => $redisInfo
]
);
}
private function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
private function getMemoryLimitInBytes(): int
{
$memoryLimit = ini_get('memory_limit');
if ($memoryLimit === '-1') {
return PHP_INT_MAX;
}
$value = (int) $memoryLimit;
$unit = strtolower($memoryLimit[strlen($memoryLimit)-1]);
switch($unit) {
case 'g':
$value *= 1024;
case 'm':
$value *= 1024;
case 'k':
$value *= 1024;
break;
}
return $value;
}
private function getLoadedExtensions(): array
{
$extensions = get_loaded_extensions();
sort($extensions);
return $extensions;
}
private function getActiveSessionCount(): int
{
try {
if ($this->container->has(SessionManager::class)) {
$sessionManager = $this->container->get(SessionManager::class);
// Diese Methode müsste implementiert werden
return $sessionManager->getActiveSessionCount();
}
} catch (\Throwable $e) {
// Silent fail
}
return 0;
}
private function getServerUptime(): string
{
// Für Linux-Systeme
if (function_exists('shell_exec') && stripos(PHP_OS, 'Linux') !== false) {
$uptime = shell_exec('uptime -p');
if ($uptime) {
return $uptime;
}
}
// Fallback
return 'Nicht verfügbar';
}
private function formatUptime(int $seconds): string
{
$days = floor($seconds / 86400);
$hours = floor(($seconds % 86400) / 3600);
$minutes = floor(($seconds % 3600) / 60);
$remainingSeconds = $seconds % 60;
$result = '';
if ($days > 0) {
$result .= "$days Tage, ";
}
return $result . sprintf('%02d:%02d:%02d', $hours, $minutes, $remainingSeconds);
}
}