. */ declare(strict_types=1); namespace App\EventSubscriber; use App\Services\System\UpdateExecutor; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Twig\Environment; /** * Blocks all web requests when maintenance mode is enabled during updates. */ readonly class MaintenanceModeSubscriber implements EventSubscriberInterface { public function __construct(private UpdateExecutor $updateExecutor) { } public static function getSubscribedEvents(): array { return [ // High priority to run before other listeners KernelEvents::REQUEST => ['onKernelRequest', 512], //High priority to run before other listeners ]; } public function onKernelRequest(RequestEvent $event): void { // Only handle main requests if (!$event->isMainRequest()) { return; } // Skip if not in maintenance mode if (!$this->updateExecutor->isMaintenanceMode()) { return; } // Allow CLI requests if (PHP_SAPI === 'cli') { return; } // Get maintenance info $maintenanceInfo = $this->updateExecutor->getMaintenanceInfo(); // Calculate how long the update has been running $duration = null; if ($maintenanceInfo && isset($maintenanceInfo['enabled_at'])) { try { $startedAt = new \DateTime($maintenanceInfo['enabled_at']); $now = new \DateTime(); $duration = $now->getTimestamp() - $startedAt->getTimestamp(); } catch (\Exception) { // Ignore date parsing errors } } $content = $this->getSimpleMaintenanceHtml($maintenanceInfo, $duration); $response = new Response($content, Response::HTTP_SERVICE_UNAVAILABLE); $response->headers->set('Retry-After', '30'); $response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate'); $event->setResponse($response); } /** * Generate a simple maintenance page HTML without Twig. */ private function getSimpleMaintenanceHtml(?array $maintenanceInfo, ?int $duration): string { $reason = htmlspecialchars($maintenanceInfo['reason'] ?? 'Update in progress'); $durationText = $duration !== null ? sprintf('%d seconds', $duration) : 'a moment'; $startDateStr = $maintenanceInfo['enabled_at'] ?? 'unknown time'; return << Part-DB - Maintenance
⚙️

Part-DB is under maintenance

We're making things better. This should only take a moment.

{$reason}

Maintenance mode active since {$startDateStr}

Started {$durationText} ago
This page will automatically refresh every 15 seconds.

HTML; } }