withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { $middleware->trustProxies(at: '*'); $middleware->prepend(\App\Http\Middleware\SetLocale::class); $middleware->alias([ 'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class, 'not_suspended' => \App\Http\Middleware\EnsureAccountIsNotSuspended::class, 'onboarded' => \App\Http\Middleware\EnsureNutritionOnboardingCompleted::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->render(function (AuthenticationException $exception, Request $request) { if (! $request->expectsJson()) { return null; } return response()->json([ 'code' => 'UNAUTHENTICATED', ], 401); }); $exceptions->render(function (InvalidSignatureException $exception, Request $request) { if (! $request->expectsJson()) { return null; } return response()->json([ 'code' => 'INVALID_VERIFICATION_LINK', ], 403); }); $exceptions->render(function (ValidationException $exception, Request $request) { if (! $request->expectsJson()) { return null; } $failedRules = $exception->validator->failed(); $errors = collect($exception->errors())->mapWithKeys( function (array $messages, string $attribute) use ($failedRules): array { $explicitCodes = collect($messages) ->filter(fn (string $message): bool => preg_match('/^[A-Z][A-Z0-9_]+$/', $message) === 1) ->values(); if ($explicitCodes->isNotEmpty()) { return [$attribute => $explicitCodes->all()]; } $codes = collect(array_keys($failedRules[$attribute] ?? [])) ->map(fn (string $rule): string => 'VALIDATION_'.Str::upper(Str::snake(class_basename($rule)))) ->values() ->all(); return [$attribute => $codes !== [] ? $codes : ['VALIDATION_INVALID']]; } ); return response()->json([ 'code' => 'VALIDATION_ERROR', 'errors' => $errors, ], $exception->status); }); $exceptions->render(function (AuthorizationException $exception, Request $request) { if (! $request->expectsJson()) { return null; } return response()->json(['code' => 'FORBIDDEN'], 403); }); $exceptions->render(function (ModelNotFoundException $exception, Request $request) { if (! $request->expectsJson()) { return null; } return response()->json(['code' => 'NOT_FOUND'], 404); }); $exceptions->render(function (HttpExceptionInterface $exception, Request $request) { if (! $request->expectsJson()) { return null; } $message = $exception->getMessage(); $code = preg_match('/^[A-Z][A-Z0-9_]+$/', $message) === 1 ? $message : match ($exception->getStatusCode()) { 401 => 'UNAUTHENTICATED', 403 => 'FORBIDDEN', 404 => 'NOT_FOUND', 409 => 'CONFLICT', 422 => 'VALIDATION_ERROR', 429 => 'TOO_MANY_REQUESTS', default => $exception->getStatusCode() >= 500 ? 'SERVER_ERROR' : 'REQUEST_FAILED', }; return response()->json(['code' => $code], $exception->getStatusCode()); }); $exceptions->render(function (Throwable $exception, Request $request) { if (! $request->expectsJson()) { return null; } return response()->json(['code' => 'SERVER_ERROR'], 500); }); })->create();