Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.82% covered (warning)
56.82%
50 / 88
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Handler
56.82% covered (warning)
56.82%
50 / 88
20.00% covered (danger)
20.00%
1 / 5
44.09
0.00% covered (danger)
0.00%
0 / 1
 report
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 render
81.48% covered (warning)
81.48%
44 / 54
0.00% covered (danger)
0.00%
0 / 1
8.41
 unauthenticated
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 invalidJson
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 convertExceptionToArray
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace App\Exceptions;
4
5use Illuminate\Auth\Access\AuthorizationException;
6use Throwable;
7use Illuminate\Support\Arr;
8use Illuminate\Support\Facades\Log;
9use Illuminate\Auth\AuthenticationException;
10use Illuminate\Validation\ValidationException;
11use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
12use Symfony\Component\ErrorHandler\Error\FatalError;
13use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
14
15class Handler extends ExceptionHandler
16{
17    /**
18     * A list of the inputs that are never flashed for validation exceptions.
19     *
20     * @var array
21     */
22    protected $dontFlash = [
23        'current_password',
24        'password',
25        'password_confirmation',
26    ];
27
28    /**
29     * Report or log an exception.
30     *
31     * @return void
32     *
33     * @throws \Throwable
34     */
35    public function report(Throwable $exception)
36    {
37        // Kill reporting if this is an "access denied" (code 9) OAuthServerException. Laravel Passport. This is blowing up
38        // out logs.
39        if ($exception instanceof \League\OAuth2\Server\Exception\OAuthServerException && $exception->getCode() == 9) {
40            return;
41        }
42        parent::report($exception);
43    }
44
45    /**
46     * Render an exception into an HTTP response.
47     *
48     * @param  \Illuminate\Http\Request  $request
49     * @return \Symfony\Component\HttpFoundation\Response
50     *
51     * @throws \Throwable
52     */
53    public function render($request, Throwable $exception)
54    {
55        if ($request->is('romeo/api/*')) {
56            Log::error($exception->getMessage() . " - " . (auth()->user()->email ?? ''), [
57                'url' => $request->fullUrl(),
58                'exception' => get_class($exception),
59                'method' => $request->method(),
60                'ip' => $request->ip(),
61                'user_agent' => $request->userAgent(),
62                'file' => $exception->getFile(),
63                'line' => $exception->getLine(),
64                'trace' => collect($exception->getTrace())->map(function ($trace) {
65                    return Arr::except($trace, ['args']);
66                })->all(),
67                'user' => auth()->user(),
68            ]);
69
70            if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
71                Log::error('Unauthenticated ATTEMPT', [
72                    'request' => $request->all(),
73                    'headers' => $request->headers->all(),
74                    'endpoint' => $request->path(),
75                    'trace' => collect($exception->getTrace())->map(function ($trace) {
76                        return Arr::except($trace, ['args']);
77                    })->all(),
78                ]);
79                return response()->json(['error' => 'Unauthenticated'], 401);
80            } elseif ($exception instanceof ValidationException) {
81                return $this->invalidJson($request, $exception);
82            } elseif ($exception instanceof ExpectedException) {
83                $statusCode = $exception->getCode() ?? 400;
84                return response()->json(data: [
85                    'success' => false,
86                    'message' => $exception->getMessage(),
87                ], status: $statusCode);
88            } elseif ($exception instanceof FatalError) {
89                return response()->json([
90                    'success' => false,
91                    'message' => $exception->getMessage()
92                ], 400);
93            } elseif ($exception instanceof AuthorizationException) {
94                return response()->json([
95                    'error' => 'Unauthorized',
96                ], 403);
97            }
98
99            return response()->json([
100                'success' => false,
101                'message' => 'Something went wrong. Please try again later.',
102                'details' => config('app.debug') ? [
103                    'exception' => get_class($exception),
104                    'message' => $exception->getMessage(),
105                    'file' => $exception->getFile(),
106                    'line' => $exception->getLine(),
107                    'trace' => collect($exception->getTrace())->map(function ($trace) {
108                        return Arr::except($trace, ['args']);
109                    })->all(),
110                ] : null,
111            ], 500);
112        }
113
114        return parent::render($request, $exception);
115    }
116
117    /**
118     * Convert an authentication exception into a response.
119     *
120     * @param  \Illuminate\Http\Request  $request
121     * @return \Symfony\Component\HttpFoundation\Response
122     */
123    protected function unauthenticated($request, AuthenticationException $exception)
124    {
125        return $request->expectsJson()
126            ? response()->json(['error' => $exception->getMessage()], 401)
127            : redirect()->guest($exception->redirectTo() ?? route('welcome'));
128    }
129
130    /**
131     * Convert a validation exception into a JSON response.
132     *
133     * @param  \Illuminate\Http\Request  $request
134     * @return \Illuminate\Http\JsonResponse
135     */
136    protected function invalidJson($request, ValidationException $exception)
137    {
138        return response()->json([
139            'error' => $exception->getMessage(),
140            'error_list' => $exception->errors(),
141        ], $exception->status);
142    }
143
144    /**
145     * Convert the given exception to an array.
146     *
147     * @return array
148     */
149    protected function convertExceptionToArray(Throwable $e)
150    {
151        $errorMessage = $e->getMessage();
152        if ($e instanceof NotFoundHttpException) {
153            $errorMessage = 'The requested resource was not found';
154            Log::error([
155                'error' => $e->getMessage(),
156                'message' => $errorMessage,
157                'exception' => get_class($e),
158                'file' => $e->getFile(),
159                'line' => $e->getLine(),
160                'trace' => collect($e->getTrace())->map(function ($trace) {
161                    return Arr::except($trace, ['args']);
162                })->all(),
163            ]);
164        }
165
166        return config('app.debug') ? [
167            'error' => $errorMessage,
168            'exception' => get_class($e),
169            'file' => $e->getFile(),
170            'line' => $e->getLine(),
171            'trace' => collect($e->getTrace())->map(function ($trace) {
172                return Arr::except($trace, ['args']);
173            })->all(),
174        ] : [
175            'error' => $this->isHttpException($e) ? $errorMessage : 'Server Error',
176        ];
177    }
178}