withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', apiPrefix: 'api/v1', ) ->withMiddleware(function (Middleware $middleware): void { // API uses token-based auth, no CSRF needed $middleware->append(\App\Http\Middleware\SecurityHeaders::class); $middleware->alias([ 'portal.token' => \App\Http\Middleware\PortalTokenMiddleware::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { // Database connection / query errors → 503 $exceptions->render(function (QueryException|PDOException $e, Request $request) { if ($request->expectsJson() || $request->is('api/*')) { Log::error('Database error', [ 'exception' => get_class($e), 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $response = ['message' => 'Service temporarily unavailable. Please try again later.']; if (config('app.debug')) { $response['debug'] = [ 'exception' => get_class($e), 'message' => $e->getMessage(), ]; } return response()->json($response, 503); } }); // 404 Not Found → friendly message $exceptions->render(function (NotFoundHttpException $e, Request $request) { if ($request->expectsJson() || $request->is('api/*')) { return response()->json([ 'message' => 'Resource not found.', ], 404); } }); // Authorization failures → log with user context $exceptions->render(function (AuthorizationException $e, Request $request) { if ($request->expectsJson() || $request->is('api/*')) { Log::warning('Authorization denied', [ 'user_id' => auth()->id(), 'ip' => $request->ip(), 'path' => $request->path(), 'method' => $request->method(), ]); } return null; // Let Laravel handle the 403 response normally }); // All other unhandled exceptions → 500 // (ValidationException, AuthenticationException, and HttpException are handled by Laravel) $exceptions->render(function (Throwable $e, Request $request) { if ($request->expectsJson() || $request->is('api/*')) { if ($e instanceof ValidationException || $e instanceof AuthenticationException || $e instanceof HttpException) { return null; // Let Laravel handle these normally } Log::error('Unhandled exception', [ 'exception' => get_class($e), 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $response = ['message' => 'An unexpected error occurred.']; if (config('app.debug')) { $response['debug'] = [ 'exception' => get_class($e), 'message' => $e->getMessage(), ]; } return response()->json($response, 500); } }); })->create();