feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Application\Admin;
use App\Application\Admin\Service\AdminLayoutProcessor;
use App\Framework\Attributes\Route;
use App\Framework\Auth\Auth;
use App\Framework\Core\VersionInfo;
@@ -12,8 +11,9 @@ use App\Framework\DateTime\Clock;
use App\Framework\Http\Method;
use App\Framework\Meta\MetaData;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Router\Result\ViewResult;
use App\Framework\Process\Services\SystemInfoService;
use App\Framework\Router\AdminRoutes;
use App\Framework\Router\Result\ViewResult;
final readonly class Dashboard
{
@@ -21,7 +21,7 @@ final readonly class Dashboard
private VersionInfo $versionInfo,
private MemoryMonitor $memoryMonitor,
private Clock $clock,
private AdminLayoutProcessor $layoutProcessor,
private SystemInfoService $systemInfo,
) {
}
@@ -29,61 +29,87 @@ final readonly class Dashboard
#[Route(path: '/admin', method: Method::GET, name: AdminRoutes::DASHBOARD)]
public function show(): ViewResult
{
$data = [
'title' => 'Admin Dashboard',
'framework_version' => $this->versionInfo->getVersion(),
'uptime_formatted' => $this->getServerUptime(),
'memory_usage_formatted' => $this->memoryMonitor->getCurrentMemory()->toHumanReadable(),
'peak_memory_formatted' => $this->memoryMonitor->getPeakMemory()->toHumanReadable(),
'load_average' => $this->getLoadAverage(),
'db_pool_size' => 10,
'db_active_connections' => 3,
'cache_hit_rate' => 85,
'cache_total_operations' => number_format(12547),
'requests_today' => number_format(1247),
'errors_today' => 3,
'last_deployment' => $this->clock->now()->format('Y-m-d H:i'),
'clear_cache_url' => '/admin/infrastructure/cache/reset',
'logs_url' => '/admin/infrastructure/logs',
'migrations_url' => '/admin/infrastructure/migrations',
];
$finalData = $this->layoutProcessor->processLayoutFromArray($data);
// DEBUG: Log template data to see what's being passed
error_log("🎯 Dashboard::show() - Final template data keys: " . implode(', ', array_keys($finalData)));
error_log("🎯 Dashboard::show() - Navigation menu count: " . count($finalData['navigation_menu'] ?? []));
error_log("🎯 Dashboard::show() - Framework version: " . ($finalData['framework_version'] ?? 'MISSING'));
$sysInfo = ($this->systemInfo)();
return new ViewResult(
template: 'dashboard',
metaData: new MetaData('Admin Dashboard', 'Administrative control panel'),
data: $finalData
data: [
'title' => 'Admin Dashboard',
'page_title' => 'Admin Dashboard',
'current_path' => '/admin',
'framework_version' => $this->versionInfo->getVersion(),
'uptime_formatted' => $this->formatUptime($sysInfo->uptime),
'boot_time' => $sysInfo->uptime->bootTime->format('Y-m-d H:i:s'),
'memory_usage_formatted' => $this->memoryMonitor->getCurrentMemory()->toHumanReadable(),
'peak_memory_formatted' => $this->memoryMonitor->getPeakMemory()->toHumanReadable(),
'load_average' => $this->formatLoadAverage($sysInfo->load),
'system_memory_total' => $this->formatBytes($sysInfo->memory->totalBytes),
'system_memory_used' => $this->formatBytes($sysInfo->memory->usedBytes),
'system_memory_free' => $this->formatBytes($sysInfo->memory->freeBytes),
'system_memory_percent' => round(($sysInfo->memory->usedBytes / $sysInfo->memory->totalBytes) * 100, 1),
'cpu_cores' => $sysInfo->cpu->cores,
'cpu_model' => $sysInfo->cpu->model,
'disk_total' => $this->formatBytes($sysInfo->disk->totalBytes),
'disk_used' => $this->formatBytes($sysInfo->disk->usedBytes),
'disk_free' => $this->formatBytes($sysInfo->disk->availableBytes),
'disk_percent' => round(($sysInfo->disk->usedBytes / $sysInfo->disk->totalBytes) * 100, 1),
'processes_total' => $sysInfo->processes->total,
'processes_running' => $sysInfo->processes->running,
'processes_sleeping' => $sysInfo->processes->sleeping,
'db_pool_size' => 10,
'db_active_connections' => 3,
'cache_hit_rate' => '85%',
'cache_total_operations' => number_format(12547),
'requests_today' => number_format(1247),
'errors_today' => 3,
'last_deployment' => $this->clock->now()->format('Y-m-d H:i'),
'clear_cache_url' => '/admin/infrastructure/cache/reset',
'logs_url' => '/admin/infrastructure/logs',
'migrations_url' => '/admin/infrastructure/migrations',
]
);
}
private function getLoadAverage(): string
private function formatLoadAverage(\App\Framework\Process\ValueObjects\SystemInfo\LoadAverage $load): string
{
if (function_exists('sys_getloadavg')) {
$load = sys_getloadavg();
return sprintf('%.2f, %.2f, %.2f', $load[0], $load[1], $load[2]);
}
return 'N/A';
return sprintf('%.2f, %.2f, %.2f', $load->oneMinute, $load->fiveMinutes, $load->fifteenMinutes);
}
private function getServerUptime(): string
private function formatUptime(\App\Framework\Process\ValueObjects\SystemInfo\SystemUptime $uptime): string
{
// Für Linux-Systeme
if (function_exists('shell_exec') && stripos(PHP_OS, 'Linux') !== false) {
$uptime = shell_exec('uptime -p');
if ($uptime) {
return $uptime;
}
$duration = $uptime->uptime;
$totalHours = (int) floor($duration->toHours());
$totalMinutes = (int) floor($duration->toMinutes());
$days = (int) floor($totalHours / 24);
$hours = $totalHours % 24;
$minutes = $totalMinutes % 60;
$parts = [];
if ($days > 0) {
$parts[] = $days . ' ' . ($days === 1 ? 'Tag' : 'Tage');
}
if ($hours > 0) {
$parts[] = $hours . ' ' . ($hours === 1 ? 'Stunde' : 'Stunden');
}
if ($minutes > 0 && $days === 0) {
$parts[] = $minutes . ' ' . ($minutes === 1 ? 'Minute' : 'Minuten');
}
// Fallback
return 'Nicht verfügbar';
return !empty($parts) ? implode(', ', $parts) : 'Weniger als eine Minute';
}
private function formatBytes(int $bytes): 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 /= (1024 ** $pow);
return round($bytes, 2) . ' ' . $units[$pow];
}
}