<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class IdleActivitySubscriber implements EventSubscriberInterface
{
private int $maxIdle;
public function __construct(private TokenStorageInterface $tokens, private RouterInterface $router, int $minutes=20)
{ $this->maxIdle = $minutes*60; }
public static function getSubscribedEvents(): array
{ return [KernelEvents::REQUEST => ['onKernelRequest', 7]]; }
public function onKernelRequest(RequestEvent $event): void
{
if(!$event->isMainRequest()) return;
$req = $event->getRequest();
$session = $req->getSession();
if(!$session || !$this->tokens->getToken()) return;
$path = $req->getPathInfo();
if (preg_match('#^/login$#',$path)) return;
$last = $session->get('last_activity');
$now = time();
if($last && ($now - $last) > $this->maxIdle) {
$session->invalidate();
if(str_starts_with($path, '/api')) {
$event->setResponse(new JsonResponse(['error'=>'session_expired'], 401));
} else {
$event->setResponse(new RedirectResponse($this->router->generate('app_login')));
}
return;
}
$session->set('last_activity', $now);
}
}