withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { $middleware->append(CollectPublicSearchMetrics::class); $middleware->encryptCookies(except: ['appearance', 'sidebar_state', EnsureSimulationRecoveryCookie::NAME]); $middleware->web(append: [ EnsureHomologationAccess::class, PortalProfileSession::class, ProtectPrivilegedIdentity::class, AuditProfileDecision::class, ProtectProfileOpportunity::class, HandleAppearance::class, HandleInertiaRequests::class, PreserveFormFeedback::class, AddLinkHeadersForPreloadedAssets::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->dontReportWhen(fn (Throwable $exception) => $exception instanceof DecryptException && request()->isMethod('POST') && request()->is('two-factor-challenge')); $exceptions->render(function (DecryptException $exception, Request $request) { if (! $request->isMethod('POST') || ! $request->is('two-factor-challenge')) { return null; } $reference = (string) Str::uuid(); Log::warning('Two-factor credential could not be decrypted; authentication refused.', [ 'reference' => $reference, 'pending_user_id' => $request->session()->get('login.id'), ]); $request->session()->invalidate(); $request->session()->regenerateToken(); $message = 'Não foi possível verificar a autenticação desta conta. Inicie sessão novamente ou contacte o suporte.'; if ($request->expectsJson()) { $field = $request->filled('recovery_code') ? 'recovery_code' : 'code'; return response()->json(['message' => $message, 'errors' => [$field => [$message]], 'reference' => $reference], 422) ->header('Cache-Control', 'no-store'); } return redirect()->route('login')->withErrors(['email' => $message])->header('Cache-Control', 'no-store'); }); $exceptions->shouldRenderJsonWhen( fn (Request $request) => $request->is('api/*') || $request->expectsJson(), ); $exceptions->respond(function (Response $response, Throwable $exception, Request $request): Response { if (($request->is('api/*') || $request->expectsJson()) && $response->getStatusCode() >= 400) { $body = json_decode((string) $response->getContent(), true); $genericMessages = [ 'Not Found' => 'O registo não foi encontrado ou não está acessível nesta conta.', 'Not found.' => 'O registo não foi encontrado ou não está acessível nesta conta.', 'Forbidden' => 'Esta conta não tem acesso a esta operação.', 'Forbidden.' => 'Esta conta não tem acesso a esta operação.', 'Unauthenticated.' => 'Entre na sua conta para continuar esta operação.', 'CSRF token mismatch.' => 'A sessão expirou. Entre novamente antes de repetir a operação.', 'Too Many Attempts.' => 'Foram feitas várias tentativas. Aguarde um pouco e tente novamente.', 'Server Error' => 'Não foi possível concluir a operação. Tente novamente.', ]; if (is_array($body) && is_string($body['message'] ?? null) && isset($genericMessages[$body['message']])) { $body['message'] = $genericMessages[$body['message']]; $response->setContent(json_encode($body, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); } elseif (is_array($body) && is_string($body['message'] ?? null) && $response->getStatusCode() === 404 && str_starts_with($body['message'], 'The route ') && str_ends_with($body['message'], ' could not be found.')) { $body['message'] = $genericMessages['Not Found']; $response->setContent(json_encode($body, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); } } if ($request->is('api/*')) { $response->headers->set( 'X-Correlation-ID', (string) ($request->attributes->get('correlation_id') ?? (Str::isUuid($request->header('X-Correlation-ID')) ? $request->header('X-Correlation-ID') : Str::uuid())), ); } if (($exception instanceof QueryException || $exception instanceof PDOException) && ! $request->is('api/v1/simulation-drafts*')) { $reference = (string) ($response->headers->get('X-Correlation-ID') ?? $request->attributes->get('correlation_id') ?? Str::uuid()); $response->setStatusCode(503); $response->headers->set('Cache-Control', 'no-store'); if ($request->is('api/*') || $request->expectsJson()) { $response->setContent(json_encode([ 'message' => 'Serviço temporariamente indisponível. Tente novamente mais tarde.', 'reference' => $reference, ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); $response->headers->set('Content-Type', 'application/json; charset=utf-8'); } else { $response->setContent(view('errors.database-unavailable', ['reference' => $reference])->render()); $response->headers->set('Content-Type', 'text/html; charset=utf-8'); } } if ($request->is('api/v1/simulation-drafts*') && $response->isServerError()) { $response->setContent(json_encode([ 'message' => 'Não foi possível guardar ou recuperar o rascunho. Os dados existentes não foram apagados. Tente novamente mais tarde.', 'reference' => $response->headers->get('X-Correlation-ID'), ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); $response->headers->set('Content-Type', 'application/json; charset=utf-8'); } return $response; }); })->create();