feat: listener redirection par sous-domaine (webmail/status)

SubdomainRedirectListener (priority 256, KernelEvents::REQUEST) :
- webmail.siteconseil.fr → /webmail (app_webmail_login)
- status.siteconseil.fr → /status (app_status)
- Ignore les sub-requests, ne redirige pas si déjà sur la bonne route
- Mapping dans constante SUBDOMAIN_ROUTES (extensible)

Tests (6 tests) :
- testWebmailRedirect : host webmail → redirect /webmail
- testStatusRedirect : host status → redirect /status
- testNoRedirectOnCrmHost : host crm → pas de redirect
- testNoRedirectWhenAlreadyOnTarget : déjà sur /webmail → pas de redirect
- testNoRedirectOnSubPath : /status/api → pas de redirect
- testSubRequestIgnored : sub-request ignorée

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-03 14:54:43 +02:00
parent 8c6b485acd
commit 1449981995
2 changed files with 131 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
<?php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
#[AsEventListener(event: KernelEvents::REQUEST, priority: 256)]
class SubdomainRedirectListener
{
private const SUBDOMAIN_ROUTES = [
'webmail.siteconseil.fr' => 'app_webmail_login',
'status.siteconseil.fr' => 'app_status',
];
public function __construct(
private UrlGeneratorInterface $urlGenerator,
) {
}
public function __invoke(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$host = $request->getHost();
if (!isset(self::SUBDOMAIN_ROUTES[$host])) {
return;
}
$targetPath = $this->urlGenerator->generate(self::SUBDOMAIN_ROUTES[$host]);
if ($request->getPathInfo() === $targetPath || str_starts_with($request->getPathInfo(), $targetPath.'/')) {
return;
}
$event->setResponse(new RedirectResponse($targetPath, 302));
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Tests\EventListener;
use App\EventListener\SubdomainRedirectListener;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class SubdomainRedirectListenerTest extends TestCase
{
private function createListener(): SubdomainRedirectListener
{
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
$urlGenerator->method('generate')->willReturnMap([
['app_webmail_login', [], 1, '/webmail'],
['app_status', [], 1, '/status'],
]);
return new SubdomainRedirectListener($urlGenerator);
}
private function createEvent(string $host, string $path = '/'): RequestEvent
{
$request = Request::create('https://'.$host.$path);
$kernel = $this->createStub(HttpKernelInterface::class);
return new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST);
}
public function testWebmailRedirect(): void
{
$event = $this->createEvent('webmail.siteconseil.fr');
($this->createListener())($event);
$this->assertInstanceOf(RedirectResponse::class, $event->getResponse());
$this->assertSame('/webmail', $event->getResponse()->getTargetUrl());
}
public function testStatusRedirect(): void
{
$event = $this->createEvent('status.siteconseil.fr');
($this->createListener())($event);
$this->assertInstanceOf(RedirectResponse::class, $event->getResponse());
$this->assertSame('/status', $event->getResponse()->getTargetUrl());
}
public function testNoRedirectOnCrmHost(): void
{
$event = $this->createEvent('crm.siteconseil.fr');
($this->createListener())($event);
$this->assertNull($event->getResponse());
}
public function testNoRedirectWhenAlreadyOnTarget(): void
{
$event = $this->createEvent('webmail.siteconseil.fr', '/webmail');
($this->createListener())($event);
$this->assertNull($event->getResponse());
}
public function testNoRedirectOnSubPath(): void
{
$event = $this->createEvent('status.siteconseil.fr', '/status/api');
($this->createListener())($event);
$this->assertNull($event->getResponse());
}
public function testSubRequestIgnored(): void
{
$request = Request::create('https://webmail.siteconseil.fr/');
$kernel = $this->createStub(HttpKernelInterface::class);
$event = new RequestEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST);
($this->createListener())($event);
$this->assertNull($event->getResponse());
}
}