|
|
@@ -0,0 +1,51 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Middlewares;
|
|
|
+
|
|
|
+use Psr\Http\Message\ResponseInterface;
|
|
|
+use Psr\Http\Message\ServerRequestInterface;
|
|
|
+use React\Http\Message\Response;
|
|
|
+use React\Promise\PromiseInterface;
|
|
|
+
|
|
|
+/**
|
|
|
+ * CORS global da API.
|
|
|
+ *
|
|
|
+ * Registrado como middleware GLOBAL (no construtor do App em public/index.php),
|
|
|
+ * portanto roda antes do roteamento. Isso é essencial para responder ao
|
|
|
+ * preflight `OPTIONS` — que não possui rota registrada e, sem este middleware,
|
|
|
+ * cairia em 404 antes do navegador concluir a requisição real.
|
|
|
+ *
|
|
|
+ * Política atual: liberar todas as origens (`*`). Como usamos "*", NÃO enviamos
|
|
|
+ * `Access-Control-Allow-Credentials` (o navegador rejeita a combinação) — a
|
|
|
+ * autenticação é via header `Authorization: Bearer`, não via cookie, então isso
|
|
|
+ * não é um problema.
|
|
|
+ */
|
|
|
+final class CorsMiddleware
|
|
|
+{
|
|
|
+ public function __invoke(ServerRequestInterface $request, callable $next)
|
|
|
+ {
|
|
|
+ // Preflight: responde imediatamente, sem chegar ao roteador/controllers.
|
|
|
+ if (strtoupper($request->getMethod()) === 'OPTIONS') {
|
|
|
+ return $this->withCorsHeaders(new Response(204));
|
|
|
+ }
|
|
|
+
|
|
|
+ $response = $next($request);
|
|
|
+
|
|
|
+ // Controllers respondem de forma síncrona (Response), mas o FrameworkX
|
|
|
+ // também aceita Promise. Tratamos ambos para anexar os headers de CORS.
|
|
|
+ if ($response instanceof PromiseInterface) {
|
|
|
+ return $response->then(fn (ResponseInterface $resolved): ResponseInterface => $this->withCorsHeaders($resolved));
|
|
|
+ }
|
|
|
+
|
|
|
+ return $this->withCorsHeaders($response);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function withCorsHeaders(ResponseInterface $response): ResponseInterface
|
|
|
+ {
|
|
|
+ return $response
|
|
|
+ ->withHeader('Access-Control-Allow-Origin', '*')
|
|
|
+ ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
|
|
|
+ ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
|
|
+ ->withHeader('Access-Control-Max-Age', '86400');
|
|
|
+ }
|
|
|
+}
|