chore: complete update
This commit is contained in:
340
src/Application/Admin/Dashboard.php
Normal file
340
src/Application/Admin/Dashboard.php
Normal 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);
|
||||
}
|
||||
}
|
||||
37
src/Application/Admin/Images.php
Normal file
37
src/Application/Admin/Images.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Domain\Media\Image;
|
||||
use App\Domain\Media\ImageRepository;
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Auth\Auth;
|
||||
|
||||
final readonly class Images
|
||||
{
|
||||
#[Auth]
|
||||
#[Route('/admin/images')]
|
||||
public function showAll(ImageRepository $imageRepository)
|
||||
{
|
||||
$images = $imageRepository->findAll();
|
||||
|
||||
echo "<div style='background-color: #222; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); grid-gap: 16px; padding: 16px;'>";
|
||||
|
||||
/** @var Image $image */
|
||||
foreach($images as $image) {
|
||||
echo sprintf("<a href='/images/%s'>", $image->filename);
|
||||
|
||||
echo "<img src='/images/" . $image->filename . "' style='width: 400px; aspect-ratio: 1; object-fit: cover; border-radius: 16px;'/>";
|
||||
|
||||
echo "</a>";
|
||||
|
||||
var_dump($image->variants[0]->filename ?? 'no variant');
|
||||
}
|
||||
|
||||
echo "</div>";
|
||||
|
||||
debug($images);
|
||||
die();
|
||||
}
|
||||
}
|
||||
20
src/Application/Admin/RoutesViewModel.php
Normal file
20
src/Application/Admin/RoutesViewModel.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Framework\View\Template;
|
||||
|
||||
#[Template('routes')]
|
||||
class RoutesViewModel
|
||||
{
|
||||
public string $name = 'Michael';
|
||||
public string $title = 'Routes';
|
||||
|
||||
public function __construct(
|
||||
public array $routes = []
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
37
src/Application/Admin/ShowDiscovery.php
Normal file
37
src/Application/Admin/ShowDiscovery.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Auth\Auth;
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Discovery\Results\DiscoveryResults;
|
||||
use App\Framework\View\TemplateDiscoveryVisitor;
|
||||
|
||||
class ShowDiscovery
|
||||
{
|
||||
public function __construct(
|
||||
private DiscoveryResults $results,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/discovery')]
|
||||
public function show(
|
||||
#Cache $cache
|
||||
)
|
||||
{
|
||||
$attributes = $this->results->getAllAttributeResults();
|
||||
|
||||
foreach ($attributes as $name => $attribute) {
|
||||
echo "Attribute: $name <br/>";
|
||||
echo "<ul>";
|
||||
foreach ($attribute as $result) {
|
||||
echo "<li>" . $result['class'] . '::'.($result['method'] ?? '').'()</li>';
|
||||
};
|
||||
echo "</ul>";
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
86
src/Application/Admin/ShowImageSlots.php
Normal file
86
src/Application/Admin/ShowImageSlots.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Domain\Media\ImageSlot;
|
||||
use App\Domain\Media\ImageSlotRepository;
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Auth\Auth;
|
||||
use App\Framework\Http\Method;
|
||||
use App\Framework\Http\Request;
|
||||
use App\Framework\Meta\MetaData;
|
||||
use App\Framework\Router\Result\ViewResult;
|
||||
|
||||
class ShowImageSlots
|
||||
{
|
||||
public function __construct(
|
||||
private ImageSlotRepository $imageSlotRepository,
|
||||
) {}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/imageslots')]
|
||||
public function show()
|
||||
{
|
||||
$slots = $this->imageSlotRepository->getSlots();
|
||||
|
||||
/** @var ImageSlot $slot */
|
||||
foreach($slots as $slot) {
|
||||
#echo $slot->slotName . '<br/>';
|
||||
|
||||
if($slot->image !== null) {
|
||||
# echo $slot->image->filename . '<br/>';
|
||||
}
|
||||
|
||||
$slotName = $slot->slotName;
|
||||
|
||||
}
|
||||
|
||||
return new ViewResult('imageslots', new MetaData('Image Slots', 'Image Slots'), [
|
||||
'slotName' => $slotName,
|
||||
'slots' => $slots,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/imageslots/{slotName}', method: Method::POST)]
|
||||
public function update(string $slotName): ViewResult
|
||||
{
|
||||
$slot = $this->imageSlotRepository->findBySlotName(urldecode($slotName));
|
||||
|
||||
$slotName = $slot->slotName;
|
||||
|
||||
#echo "<input type='text' value='$slotName' />";
|
||||
|
||||
return new ViewResult('imageslot', new MetaData('Image Slot', 'Image Slots'), [
|
||||
'slotName' => $slotName,
|
||||
'id' => $slot->id,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/imageslots/create', method: Method::POST)]
|
||||
public function create(Request $request)
|
||||
{
|
||||
$name = $request->parsedBody->get('slotName');
|
||||
|
||||
$slot = new ImageSlot(0, $name, '');
|
||||
|
||||
$this->imageSlotRepository->save($slot);
|
||||
|
||||
debug($name);
|
||||
}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/imageslots/edit/{id}', method: Method::PUT)]
|
||||
public function edit(Request $request, string $id)
|
||||
{
|
||||
$name = $request->parsedBody->get('slotName');
|
||||
|
||||
$slot = $this->imageSlotRepository->findById((int)$id);
|
||||
|
||||
|
||||
$slot = new ImageSlot($slot->id, $name, $slot->imageId);
|
||||
|
||||
$this->imageSlotRepository->save($slot);
|
||||
}
|
||||
}
|
||||
146
src/Application/Admin/ShowImageUpload.php
Normal file
146
src/Application/Admin/ShowImageUpload.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Domain\Media\Image;
|
||||
use App\Domain\Media\ImageRepository;
|
||||
use App\Domain\Media\ImageResizer;
|
||||
use App\Domain\Media\ImageVariantRepository;
|
||||
use App\Domain\Media\SaveImageFile;
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Auth\Auth;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Database\Transaction;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Http\Method;
|
||||
use App\Framework\Http\Request;
|
||||
use App\Framework\Http\UploadedFile;
|
||||
use App\Framework\Ulid\StringConverter;
|
||||
use App\Framework\Ulid\Ulid;
|
||||
use Media\Services\ImageService;
|
||||
use function imagecopyresampled;
|
||||
use function imagecreatefromjpeg;
|
||||
use function imagecreatetruecolor;
|
||||
use function imagedestroy;
|
||||
use function imagejpeg;
|
||||
|
||||
class ShowImageUpload
|
||||
{
|
||||
public function __construct(
|
||||
private PathProvider $pathProvider,
|
||||
private StringConverter $stringConverter,
|
||||
) {}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/upload')]
|
||||
public function __invoke()
|
||||
{
|
||||
$html = <<<HTML
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<label for="image">Bild hochladen:</label>
|
||||
<input type="file" id="image" name="image" accept="image/*" required/>
|
||||
<input type="submit" value="Upload" />
|
||||
</form>
|
||||
|
||||
HTML;
|
||||
|
||||
echo $html;
|
||||
die();
|
||||
}
|
||||
#[Auth]
|
||||
#[Route('/upload', Method::POST)]
|
||||
public function upload(Request $request, Ulid $ulid, ImageRepository $imageRepository, ImageVariantRepository $imageVariantRepository,)
|
||||
{
|
||||
try {
|
||||
/** @var UploadedFile $file */
|
||||
$file = $request->files->get('image');
|
||||
|
||||
|
||||
$storageFolder = $this->pathProvider->resolvePath('/storage');
|
||||
|
||||
// Todo: Use Clock instead of date();
|
||||
$uploadDirectory = sprintf('uploads/%s/%s/%s', date('Y'), date('m'), date('d'));
|
||||
|
||||
$ulid = (string)$ulid; //$this->stringConverter->encodeBase32($ulid);
|
||||
|
||||
$id = $ulid;
|
||||
|
||||
// Remove Timestamp
|
||||
$id = substr($id, 10);
|
||||
|
||||
$hash = hash_file('sha256', $file->tmpName);
|
||||
|
||||
// Prüfen, ob ein Bild mit diesem Hash bereits existiert
|
||||
$existingImage = $imageRepository->findByHash($hash);
|
||||
if ($existingImage !== null) {
|
||||
echo "<h2>Bild bereits vorhanden</h2>";
|
||||
echo "<p>Dieses Bild wurde bereits hochgeladen.</p>";
|
||||
echo "<p>Bild-ID: " . htmlspecialchars($existingImage->ulid) . "</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
$idStr = str_pad((string)$id, 9, '0', STR_PAD_LEFT);
|
||||
$filePathPattern = sprintf('%s/%s/%s',
|
||||
substr($idStr, 0, 3),
|
||||
substr($idStr, 3, 3),
|
||||
substr($idStr, 6, 3),
|
||||
);
|
||||
|
||||
$path = $storageFolder . '/' . $uploadDirectory . '/' . $filePathPattern . "/";
|
||||
|
||||
$filename = $idStr . '_' . $hash . "_";
|
||||
|
||||
#dd($path . $filename . 'variant.png');
|
||||
|
||||
$smallPath = $path . $filename . 'small.png';
|
||||
|
||||
|
||||
|
||||
|
||||
[$width, $height] = getimagesize($file->tmpName);
|
||||
|
||||
|
||||
|
||||
$image = new Image(
|
||||
ulid : $ulid,
|
||||
filename : $filename . 'original.jpg',
|
||||
originalFilename: $file->name,
|
||||
mimeType : $file->getMimeType(),
|
||||
fileSize : $file->size,
|
||||
width : $width,
|
||||
height : $height,
|
||||
hash : $hash,
|
||||
path : $path,
|
||||
altText : 'Some alt text',
|
||||
);
|
||||
|
||||
|
||||
|
||||
$imageRepository->save($image, $file->tmpName);
|
||||
|
||||
|
||||
|
||||
#$image = $imageRepository->findById("0197B2CD759501F08D60312AE62ACCFC");
|
||||
|
||||
#mkdir($path, 0755, true);
|
||||
$variant = new ImageResizer()($image, 50, 50);
|
||||
|
||||
$imageVariantRepository->save($variant);;
|
||||
|
||||
$href = "/images/".$variant->filename;
|
||||
echo "<a href='$href'>$href</a>";
|
||||
|
||||
|
||||
#new SaveImageFile()($image, $file->tmpName);;
|
||||
debug($variant->filename);
|
||||
dd($image);
|
||||
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
echo "<h2>Fehler beim Upload:</h2>";
|
||||
echo "<p>" . htmlspecialchars($e->getMessage()) . "</p>";
|
||||
debug($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/Application/Admin/ShowRoutes.php
Normal file
56
src/Application/Admin/ShowRoutes.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Admin;
|
||||
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Auth\Auth;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Core\RouteCache;
|
||||
use App\Framework\Discovery\Results\DiscoveryResults;
|
||||
use App\Framework\Meta\MetaData;
|
||||
use App\Framework\Router\Result\ViewResult;
|
||||
|
||||
final readonly class ShowRoutes
|
||||
{
|
||||
public function __construct(
|
||||
private PathProvider $pathProvider,
|
||||
private DiscoveryResults $processedResults
|
||||
){}
|
||||
|
||||
#[Auth]
|
||||
#[Route('/admin/routes')]
|
||||
public function show(): ViewResult
|
||||
{
|
||||
$routes = $this->processedResults->get(Route::class);
|
||||
|
||||
sort($routes);
|
||||
|
||||
/*
|
||||
|
||||
echo '<div style="font-family: monospace; font-size: 12px;">';
|
||||
|
||||
echo str_repeat('-', 50) . "<br/>";
|
||||
|
||||
foreach($routes as $route) {
|
||||
$path = $route['path'];
|
||||
$line = "|-- $path";
|
||||
|
||||
$times = 50 - mb_strlen($line);
|
||||
|
||||
$line .= str_repeat(' .', $times) . "|<br/>";
|
||||
|
||||
echo $line;
|
||||
|
||||
}
|
||||
|
||||
echo str_repeat('-', 50) . "<br/></div>";*/
|
||||
|
||||
return new ViewResult('routes',
|
||||
metaData: new MetaData('Routes', 'Routes'),
|
||||
data: [
|
||||
'name' => 'Michael',
|
||||
'title' => 'Routes',
|
||||
],
|
||||
model: new RoutesViewModel($routes));
|
||||
}
|
||||
}
|
||||
27
src/Application/Admin/templates/admin-main.view.php
Normal file
27
src/Application/Admin/templates/admin-main.view.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Admin</title>
|
||||
<meta name="description" content="Admin">
|
||||
<meta property="og:type" content="Admin">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!--<header>
|
||||
<h2>Admin</h2>
|
||||
</header>-->
|
||||
|
||||
<menu>
|
||||
<li><a href="/admin">Dashboard</a></li>
|
||||
</menu>
|
||||
|
||||
|
||||
<main></main>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
146
src/Application/Admin/templates/dashboard.view.php
Normal file
146
src/Application/Admin/templates/dashboard.view.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<layout src="admin-main"/>
|
||||
|
||||
<a href="/admin/routes">Routes</a>
|
||||
|
||||
<br>
|
||||
|
||||
<a href="/admin/imageslots">Image Slots</a>
|
||||
|
||||
|
||||
<div class="section">
|
||||
<h2>Basis-Cards (nur semantische Selektoren)</h2>
|
||||
<div class="demo-grid">
|
||||
<article class="card">
|
||||
<header>
|
||||
<div>
|
||||
<h3>Projekt Alpha</h3>
|
||||
<small>Erstellt am 10. Juli 2025</small>
|
||||
</div>
|
||||
<span role="status">Aktiv</span>
|
||||
</header>
|
||||
<main>
|
||||
<p>Diese Card nutzt nur semantische HTML-Elemente. Das Styling erfolgt über Selektoren wie <code>.card > header</code> und <code>.card h3</code>.</p>
|
||||
<p>Weniger Klassen, sauberer HTML-Code.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<small>Letztes Update: heute</small>
|
||||
<div>
|
||||
<button>Öffnen</button>
|
||||
<button>Teilen</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<header>
|
||||
<h3>Einfache Card</h3>
|
||||
</header>
|
||||
<main>
|
||||
<p>Minimaler HTML-Code, maximale Semantik.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Action</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Status-Varianten (Klassen für Varianten)</h2>
|
||||
<div class="demo-grid">
|
||||
<article class="card card--success">
|
||||
<header>
|
||||
<h3>Erfolg</h3>
|
||||
<span role="status">Abgeschlossen</span>
|
||||
</header>
|
||||
<main>
|
||||
<p>Success-Variante durch eine einzige Modifier-Klasse.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Details</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<article class="card card--error">
|
||||
<header>
|
||||
<h3>Fehler</h3>
|
||||
<span role="status">Problem</span>
|
||||
</header>
|
||||
<main>
|
||||
<p>Error-Variante mit systematischen Farben.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Beheben</button>
|
||||
<button>Ignorieren</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Größen-Varianten</h2>
|
||||
<div class="demo-grid">
|
||||
<article class="card card--compact">
|
||||
<header>
|
||||
<h3>Kompakt</h3>
|
||||
</header>
|
||||
<main>
|
||||
<p>Weniger Padding durch Modifier-Klasse.</p>
|
||||
</main>
|
||||
</article>
|
||||
|
||||
<article class="card card--spacious">
|
||||
<header>
|
||||
<h3>Großzügig</h3>
|
||||
</header>
|
||||
<main>
|
||||
<p>Mehr Weißraum für wichtige Inhalte.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Hauptaktion</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Layout-Varianten</h2>
|
||||
<div class="demo-grid demo-grid--wide">
|
||||
<article class="card card--horizontal">
|
||||
<header>
|
||||
<h3>Horizontal</h3>
|
||||
</header>
|
||||
<main>
|
||||
<p>Horizontales Layout durch Modifier.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Action</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<article class="card card--media">
|
||||
<img src="https://picsum.photos/400/200?random=2" alt="Demo">
|
||||
<header>
|
||||
<h3>Mit Media</h3>
|
||||
</header>
|
||||
<main>
|
||||
<p>Bild wird durch Selector <code>.card--media img</code> gestylt.</p>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<button>Ansehen</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
11
src/Application/Admin/templates/imageslot.view.php
Normal file
11
src/Application/Admin/templates/imageslot.view.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<layout src="admin-main"/>
|
||||
|
||||
<form action='/admin/imageslots/edit/{{ id }}' method='post'>
|
||||
<input type='hidden' name='_method' value='PUT'/>
|
||||
|
||||
<label>Slot Name:
|
||||
<input type='text' name='slotName' value='{{ slotName }}'/>
|
||||
</label>
|
||||
<input type='submit' value='Update'/>
|
||||
|
||||
</form>
|
||||
21
src/Application/Admin/templates/imageslots.view.php
Normal file
21
src/Application/Admin/templates/imageslots.view.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<layout src="admin-main"/>
|
||||
|
||||
<for var="slot" in="slots">
|
||||
|
||||
<form action='/admin/imageslots/{{ slot.slotName }}' method='post'>
|
||||
|
||||
<h3>{{ slot.slotName }}</h3>
|
||||
<input type='submit' value='Update'/>
|
||||
|
||||
</form>
|
||||
|
||||
</for>
|
||||
|
||||
<form action='/admin/imageslots/create' method='post'>
|
||||
|
||||
<label>Slot Name:
|
||||
<input type='text' name='slotName' value=''/>
|
||||
</label>
|
||||
<input type='submit' value='Create'/>
|
||||
|
||||
</form>
|
||||
11
src/Application/Admin/templates/routes.view.php
Normal file
11
src/Application/Admin/templates/routes.view.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<layout src="admin-main"/>
|
||||
|
||||
<div>Routes Page</div>
|
||||
|
||||
<p>{{ name }}</p>
|
||||
|
||||
<ul>
|
||||
<for var="route" in="routes">
|
||||
<li><a href="{{ route.path }}">{{ route.path }} </a> <i> ({{ route.class }}) {{ route.attributes.0 }} </i> </li>
|
||||
</for>
|
||||
</ul>
|
||||
96
src/Application/Admin/views/admin/dashboard.php
Normal file
96
src/Application/Admin/views/admin/dashboard.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Framework Admin Dashboard</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin" class="active">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Framework Version</h3>
|
||||
<div class="stat-value"><?= $stats['frameworkVersion'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>PHP Version</h3>
|
||||
<div class="stat-value"><?= $stats['phpVersion'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $stats['memoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Max. Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $stats['peakMemoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Server</h3>
|
||||
<div class="stat-value"><?= $stats['serverInfo'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Serverzeit</h3>
|
||||
<div class="stat-value"><?= $stats['serverTime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Zeitzone</h3>
|
||||
<div class="stat-value"><?= $stats['timezone'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Betriebssystem</h3>
|
||||
<div class="stat-value"><?= $stats['operatingSystem'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Server Uptime</h3>
|
||||
<div class="stat-value"><?= $stats['uptime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Aktive Sessions</h3>
|
||||
<div class="stat-value"><?= $stats['sessionCount'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Registrierte Dienste</h3>
|
||||
<div class="stat-value"><?= $stats['servicesCount'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>PHP Erweiterungen</h2>
|
||||
<div class="extensions-list">
|
||||
<?php foreach($stats['loadedExtensions'] as $extension): ?>
|
||||
<span class="extension-badge"><?= $extension ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
86
src/Application/Admin/views/admin/environment.php
Normal file
86
src/Application/Admin/views/admin/environment.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Umgebungsvariablen</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment" class="active">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="envFilter" placeholder="Variablen filtern..." class="search-input">
|
||||
<div class="filter-tags">
|
||||
<button class="filter-tag" data-prefix="APP_">APP_</button>
|
||||
<button class="filter-tag" data-prefix="DB_">DB_</button>
|
||||
<button class="filter-tag" data-prefix="REDIS_">REDIS_</button>
|
||||
<button class="filter-tag" data-prefix="RATE_LIMIT_">RATE_LIMIT_</button>
|
||||
<button class="filter-tag" data-prefix="PHP_">PHP_</button>
|
||||
<button class="filter-tag active" data-prefix="">Alle</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="admin-table" id="envTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Wert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($env as $key => $value): ?>
|
||||
<tr>
|
||||
<td><?= $key ?></td>
|
||||
<td><?= is_array($value) ? json_encode($value) : $value ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Filterung der Umgebungsvariablen
|
||||
document.getElementById('envFilter').addEventListener('input', filterTable);
|
||||
|
||||
// Tag-Filter
|
||||
document.querySelectorAll('.filter-tag').forEach(tag => {
|
||||
tag.addEventListener('click', function() {
|
||||
document.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const prefix = this.getAttribute('data-prefix');
|
||||
document.getElementById('envFilter').value = prefix;
|
||||
filterTable();
|
||||
});
|
||||
});
|
||||
|
||||
function filterTable() {
|
||||
const filterValue = document.getElementById('envFilter').value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#envTable tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const key = row.cells[0].textContent.toLowerCase();
|
||||
row.style.display = key.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
125
src/Application/Admin/views/admin/performance.view.php
Normal file
125
src/Application/Admin/views/admin/performance.view.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Performance-Übersicht</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance" class="active">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Aktueller Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $performance['currentMemoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Maximaler Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $performance['peakMemoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherlimit</h3>
|
||||
<div class="stat-value"><?= $performance['memoryLimit'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherauslastung</h3>
|
||||
<div class="stat-value">
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: <?= $performance['memoryUsagePercentage'] ?>%"></div>
|
||||
</div>
|
||||
<div class="progress-value"><?= $performance['memoryUsagePercentage'] ?>%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Systemlast (1/5/15 min)</h3>
|
||||
<div class="stat-value">
|
||||
<?= $performance['loadAverage'][0] ?> /
|
||||
<?= $performance['loadAverage'][1] ?> /
|
||||
<?= $performance['loadAverage'][2] ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache aktiviert</h3>
|
||||
<div class="stat-value"><?= $performance['opcacheEnabled'] ?></div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($performance['opcacheMemoryUsage'])): ?>
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $performance['opcacheMemoryUsage'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Cache Hits</h3>
|
||||
<div class="stat-value"><?= $performance['opcacheCacheHits'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>OPCache Miss Rate</h3>
|
||||
<div class="stat-value"><?= $performance['opcacheMissRate'] ?></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Ausführungszeit</h3>
|
||||
<div class="stat-value"><?= $performance['executionTime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Geladene Dateien</h3>
|
||||
<div class="stat-value"><?= $performance['includedFiles'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>Geladene Dateien</h2>
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="fileFilter" placeholder="Dateien filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<div class="file-list" id="fileList">
|
||||
<?php foreach(get_included_files() as $file): ?>
|
||||
<div class="file-item">
|
||||
<?= $file ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('fileFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#fileList .file-item');
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
105
src/Application/Admin/views/admin/redis.view.php
Normal file
105
src/Application/Admin/views/admin/redis.view.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Redis-Informationen</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis" class="active">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<?php if (isset($redis['status']) && $redis['status'] === 'Verbunden'): ?>
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<h3>Status</h3>
|
||||
<div class="stat-value status-connected"><?= $redis['status'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Version</h3>
|
||||
<div class="stat-value"><?= $redis['version'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Uptime</h3>
|
||||
<div class="stat-value"><?= $redis['uptime'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $redis['memory'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Max. Speicherverbrauch</h3>
|
||||
<div class="stat-value"><?= $redis['peak_memory'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Verbundene Clients</h3>
|
||||
<div class="stat-value"><?= $redis['clients'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="stat-box">
|
||||
<h3>Anzahl Schlüssel</h3>
|
||||
<div class="stat-value"><?= $redis['keys'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-section">
|
||||
<h2>Schlüssel (max. 50 angezeigt)</h2>
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="keyFilter" placeholder="Schlüssel filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<div class="key-list" id="keyList">
|
||||
<?php if (empty($redis['key_sample'])): ?>
|
||||
<div class="empty-message">Keine Schlüssel vorhanden</div>
|
||||
<?php else: ?>
|
||||
<?php foreach($redis['key_sample'] as $key): ?>
|
||||
<div class="key-item">
|
||||
<?= $key ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="error-message">
|
||||
<h2>Redis-Verbindung fehlgeschlagen</h2>
|
||||
<p><?= $redis['status'] ?? 'Unbekannter Fehler' ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('keyFilter')?.addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#keyList .key-item');
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
69
src/Application/Admin/views/admin/routes.php
Normal file
69
src/Application/Admin/views/admin/routes.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Routen-Übersicht</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes" class="active">Routen</a>
|
||||
<a href="/admin/services">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="routeFilter" placeholder="Routen filtern..." class="search-input">
|
||||
</div>
|
||||
|
||||
<table class="admin-table" id="routesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pfad</th>
|
||||
<th>Methode</th>
|
||||
<th>Controller</th>
|
||||
<th>Aktion</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($routes as $route): ?>
|
||||
<tr>
|
||||
<td><?= $route->path ?></td>
|
||||
<td><?= $route->method ?></td>
|
||||
<td><?= $route->controllerClass ?></td>
|
||||
<td><?= $route->methodName ?></td>
|
||||
<td><?= $route->name ?? '-' ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('routeFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#routesTable tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
row.style.display = text.includes(filterValue) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
72
src/Application/Admin/views/admin/services.php
Normal file
72
src/Application/Admin/views/admin/services.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body class="admin-page">
|
||||
<div class="admin-header">
|
||||
<h1>Registrierte Dienste</h1>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav">
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/routes">Routen</a>
|
||||
<a href="/admin/services" class="active">Dienste</a>
|
||||
<a href="/admin/environment">Umgebung</a>
|
||||
<a href="/admin/performance">Performance</a>
|
||||
<a href="/admin/redis">Redis</a>
|
||||
<a href="/admin/phpinfo">PHP Info</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-content">
|
||||
<div class="admin-tools">
|
||||
<input type="text" id="serviceFilter" placeholder="Dienste filtern..." class="search-input">
|
||||
<span class="services-count"><?= count($services) ?> Dienste insgesamt</span>
|
||||
</div>
|
||||
|
||||
<div class="service-list" id="serviceList">
|
||||
<?php foreach($services as $service): ?>
|
||||
<div class="service-item">
|
||||
<div class="service-name"><?= $service ?></div>
|
||||
<?php
|
||||
$parts = explode('\\', $service);
|
||||
$category = $parts[1] ?? 'Unknown';
|
||||
$subCategory = $parts[2] ?? '';
|
||||
?>
|
||||
<div class="service-category">
|
||||
<span class="category-badge"><?= $category ?></span>
|
||||
<?php if ($subCategory): ?>
|
||||
<span class="subcategory-badge"><?= $subCategory ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-footer">
|
||||
<p>© <?= date('Y') ?> Framework Admin</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('serviceFilter').addEventListener('input', function() {
|
||||
const filterValue = this.value.toLowerCase();
|
||||
const items = document.querySelectorAll('#serviceList .service-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
const isVisible = text.includes(filterValue);
|
||||
item.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
});
|
||||
|
||||
document.querySelector('.services-count').textContent =
|
||||
visibleCount + ' von ' + <?= count($services) ?> + ' Diensten';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user