]> BookStack Code Mirror - bookstack/blob - app/Exceptions/Handler.php
Drawings: Added class to extract drawio data from png files
[bookstack] / app / Exceptions / Handler.php
1 <?php
2
3 namespace BookStack\Exceptions;
4
5 use Exception;
6 use Illuminate\Auth\AuthenticationException;
7 use Illuminate\Database\Eloquent\ModelNotFoundException;
8 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
9 use Illuminate\Http\Exceptions\PostTooLargeException;
10 use Illuminate\Http\JsonResponse;
11 use Illuminate\Http\Request;
12 use Illuminate\Http\Response;
13 use Illuminate\Validation\ValidationException;
14 use Symfony\Component\ErrorHandler\Error\FatalError;
15 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
16 use Throwable;
17
18 class Handler extends ExceptionHandler
19 {
20     /**
21      * A list of the exception types that are not reported.
22      *
23      * @var array<int, class-string<\Throwable>>
24      */
25     protected $dontReport = [
26         NotFoundException::class,
27         StoppedAuthenticationException::class,
28     ];
29
30     /**
31      * A list of the inputs that are never flashed to the session on validation exceptions.
32      *
33      * @var array<int, string>
34      */
35     protected $dontFlash = [
36         'current_password',
37         'password',
38         'password_confirmation',
39     ];
40
41     /**
42      * A function to run upon out of memory.
43      * If it returns a response, that will be provided back to the request
44      * upon an out of memory event.
45      *
46      * @var ?callable(): ?Response
47      */
48     protected $onOutOfMemory = null;
49
50     /**
51      * Report or log an exception.
52      *
53      * @param \Throwable $exception
54      *
55      * @throws \Throwable
56      *
57      * @return void
58      */
59     public function report(Throwable $exception)
60     {
61         parent::report($exception);
62     }
63
64     /**
65      * Render an exception into an HTTP response.
66      *
67      * @param \Illuminate\Http\Request $request
68      * @param Exception                $e
69      *
70      * @return \Illuminate\Http\Response
71      */
72     public function render($request, Throwable $e)
73     {
74         if ($e instanceof FatalError && str_contains($e->getMessage(), 'bytes exhausted (tried to allocate') && $this->onOutOfMemory) {
75             $response = call_user_func($this->onOutOfMemory);
76             if ($response) {
77                 return $response;
78             }
79         }
80
81         if ($e instanceof PostTooLargeException) {
82             $e = new NotifyException(trans('errors.server_post_limit'), '/', 413);
83         }
84
85         if ($this->isApiRequest($request)) {
86             return $this->renderApiException($e);
87         }
88
89         return parent::render($request, $e);
90     }
91
92     /**
93      * Provide a function to be called when an out of memory event occurs.
94      * If the callable returns a response, this response will be returned
95      * to the request upon error.
96      */
97     public function prepareForOutOfMemory(callable $onOutOfMemory)
98     {
99         $this->onOutOfMemory = $onOutOfMemory;
100     }
101
102     /**
103      * Forget the current out of memory handler, if existing.
104      */
105     public function forgetOutOfMemoryHandler()
106     {
107         $this->onOutOfMemory = null;
108     }
109
110     /**
111      * Check if the given request is an API request.
112      */
113     protected function isApiRequest(Request $request): bool
114     {
115         return str_starts_with($request->path(), 'api/');
116     }
117
118     /**
119      * Render an exception when the API is in use.
120      */
121     protected function renderApiException(Throwable $e): JsonResponse
122     {
123         $code = 500;
124         $headers = [];
125
126         if ($e instanceof HttpExceptionInterface) {
127             $code = $e->getStatusCode();
128             $headers = $e->getHeaders();
129         }
130
131         if ($e instanceof ModelNotFoundException) {
132             $code = 404;
133         }
134
135         $responseData = [
136             'error' => [
137                 'message' => $e->getMessage(),
138             ],
139         ];
140
141         if ($e instanceof ValidationException) {
142             $responseData['error']['message'] = 'The given data was invalid.';
143             $responseData['error']['validation'] = $e->errors();
144             $code = $e->status;
145         }
146
147         $responseData['error']['code'] = $code;
148
149         return new JsonResponse($responseData, $code, $headers);
150     }
151
152     /**
153      * Convert an authentication exception into an unauthenticated response.
154      *
155      * @param \Illuminate\Http\Request                 $request
156      * @param \Illuminate\Auth\AuthenticationException $exception
157      *
158      * @return \Illuminate\Http\Response
159      */
160     protected function unauthenticated($request, AuthenticationException $exception)
161     {
162         if ($request->expectsJson()) {
163             return response()->json(['error' => 'Unauthenticated.'], 401);
164         }
165
166         return redirect()->guest('login');
167     }
168
169     /**
170      * Convert a validation exception into a JSON response.
171      *
172      * @param \Illuminate\Http\Request                   $request
173      * @param \Illuminate\Validation\ValidationException $exception
174      *
175      * @return \Illuminate\Http\JsonResponse
176      */
177     protected function invalidJson($request, ValidationException $exception)
178     {
179         return response()->json($exception->errors(), $exception->status);
180     }
181 }