<?php
namespace App\EventSubscriber;
use App\Repository\Bus\PassengerAppSettingRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Security;
/**
* Gates the public passenger app out of /bus_api when it's been switched
* off (App\Entity\Bus\PassengerAppSetting), without touching clerk/staff
* traffic on the same routes:
*
* - the Courier Android app's counter-booking flow always sends the
* custom `Auth` JWT header (see BusApi\BookingController::resolveAuthenticatedUser())
* - the web admin (e.g. managing fare rules) is authenticated via the
* session-based `main` firewall, i.e. Security::getUser() is set
* - the public app's checkout flow sends neither - that's the only
* traffic this blocks
*
* Safaricom's M-Pesa callback is exempted by route name since it's neither
* of the above and must always be processed to keep payment/booking state
* consistent regardless of the switch.
*/
class PassengerAppAvailabilitySubscriber implements EventSubscriberInterface
{
private const EXEMPT_ROUTES = ['bookingMpesaCallback'];
private PassengerAppSettingRepository $settingRepository;
private Security $security;
public function __construct(PassengerAppSettingRepository $settingRepository, Security $security)
{
$this->settingRepository = $settingRepository;
$this->security = $security;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if (strpos($request->getPathInfo(), '/bus_api') !== 0) {
return;
}
if (in_array($request->attributes->get('_route'), self::EXEMPT_ROUTES, true)) {
return;
}
if ($request->headers->get('Auth') || $this->security->getUser()) {
return;
}
$setting = $this->settingRepository->getCurrent();
if ($setting->isEnabled()) {
return;
}
$event->setResponse(new JsonResponse([
'error' => 'BOOKING_UNAVAILABLE',
'message' => $setting->getDisabledMessage()
?: 'Online booking is temporarily unavailable. Please visit a station counter to book your seat.',
], Response::HTTP_SERVICE_UNAVAILABLE));
}
}