205 lines
6.9 KiB
PHP
Executable File
205 lines
6.9 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Common\Core\Exceptions;
|
|
|
|
use Common\Billing\Models\Product;
|
|
use ErrorException;
|
|
use Illuminate\Auth\Access\AuthorizationException;
|
|
use Illuminate\Foundation\Exceptions\Handler;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Str;
|
|
use Sentry\Laravel\Integration;
|
|
use Sentry\State\Scope;
|
|
use Spatie\Ignition\Ignition;
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
use Throwable;
|
|
use function Sentry\configureScope;
|
|
|
|
class BaseExceptionHandler extends Handler
|
|
{
|
|
public function render($request, Throwable $e)
|
|
{
|
|
$isAuthException =
|
|
$e instanceof AuthorizationException ||
|
|
($e instanceof HttpException && $e->getStatusCode() === 403);
|
|
|
|
if (
|
|
$isAuthException &&
|
|
(requestIsFromFrontend() &&
|
|
!$request->expectsJson() &&
|
|
!Auth::check())
|
|
) {
|
|
return redirect('/login');
|
|
}
|
|
|
|
if (
|
|
$e instanceof AuthorizationException &&
|
|
$e->response() instanceof AccessResponseWithPermission &&
|
|
$e->response()->permission &&
|
|
Auth::check() &&
|
|
settings('billing.enable')
|
|
) {
|
|
$permissionExistsInSubscriptionPlan = Product::with(['permissions'])
|
|
->get()
|
|
->some(function ($product) use ($e) {
|
|
// check if there's a plan that has this permission and if user is not already on this plan
|
|
return $product->permissions->contains(
|
|
'name',
|
|
$e->response()->permission,
|
|
) &&
|
|
Auth::user()->subscriptions->first()?->product_id !==
|
|
$product->id;
|
|
});
|
|
if ($permissionExistsInSubscriptionPlan) {
|
|
return Auth::user()->subscribed()
|
|
? redirect('/billing')
|
|
: redirect('/pricing');
|
|
}
|
|
}
|
|
|
|
return parent::render($request, $e);
|
|
}
|
|
|
|
public function register()
|
|
{
|
|
if (config('app.env') !== 'production') {
|
|
return;
|
|
}
|
|
|
|
$this->renderable(function (ErrorException $e) {
|
|
if (
|
|
Str::contains($e->getMessage(), [
|
|
'failed to open stream: Permission denied',
|
|
'mkdir(): Permission denied',
|
|
])
|
|
) {
|
|
return $this->filePermissionResponse($e);
|
|
}
|
|
});
|
|
|
|
configureScope(function (Scope $scope): void {
|
|
$scope->setContext('app_name', ['value' => config('app.name')]);
|
|
});
|
|
|
|
$this->reportable(function (Throwable $e) {
|
|
Integration::captureUnhandledException($e);
|
|
});
|
|
}
|
|
|
|
protected function convertExceptionToArray(Throwable $e): array
|
|
{
|
|
$previous = $e->getPrevious();
|
|
$isValidationException =
|
|
$e instanceof HttpException && $e->getStatusCode() === 422;
|
|
$isExceptionWithAction =
|
|
$previous &&
|
|
method_exists($previous, 'response') &&
|
|
$previous->response() &&
|
|
property_exists($previous->response(), 'action');
|
|
|
|
if (
|
|
config('app.debug') &&
|
|
!config('common.site.demo') &&
|
|
!$isValidationException
|
|
) {
|
|
$array = $this->ignitionReportFromThrowable($e);
|
|
} else {
|
|
$array = parent::convertExceptionToArray($e);
|
|
}
|
|
|
|
if ($isExceptionWithAction) {
|
|
$array['action'] = $e->getPrevious()->response()->action;
|
|
}
|
|
|
|
if ($array['message'] === 'Server Error') {
|
|
$array['message'] = __(
|
|
'There was an issue. Please try again later.',
|
|
);
|
|
}
|
|
|
|
if ($array['message'] === 'This action is unauthorized.') {
|
|
$array['message'] = __(
|
|
"You don't have required permissions for this action.",
|
|
);
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
|
|
protected function filePermissionResponse(ErrorException $e)
|
|
{
|
|
if (request()->expectsJson()) {
|
|
return response()->json(['message' => 'test']);
|
|
} else {
|
|
preg_match('/\((.+?)\):/', $e->getMessage(), $matches);
|
|
$path = $matches[1] ?? null;
|
|
// should not return a view here, in case laravel views folder is not readable as well
|
|
return response(
|
|
"<div style='text-align:center'><h1>Could not access a file or folder</h1> <br> Location: <b>$path</b><br>" .
|
|
'<p>See the article here for possible solutions: <a target="_blank" href="https://support.vebto.com/hc/articles/21/25/207/changing-file-permissions">https://support.vebto.com/hc/articles/207/changing-file-permissions</a></p></div>',
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function ignitionReportFromThrowable(Throwable $e): array
|
|
{
|
|
$report = app(Ignition::class)
|
|
->shouldDisplayException(false)
|
|
->handleException($e)
|
|
->toArray();
|
|
|
|
$trace = array_map(function ($item) {
|
|
$path = Str::of($item['class'] ?? $item['file'])
|
|
->replace([base_path(), 'vendor/laravel/framework/src/'], '')
|
|
->replace('\\', '/')
|
|
->trim('/')
|
|
->explode('/');
|
|
return [
|
|
'applicationFrame' => $item['application_frame'],
|
|
'codeSnippet' => $item['code_snippet'],
|
|
'path' => $path,
|
|
'lineNumber' => $item['line_number'],
|
|
'method' => $item['method'],
|
|
];
|
|
}, $report['stacktrace']);
|
|
|
|
$flatIndex = 0;
|
|
$totalVendorGroups = 0;
|
|
$groupedTrace = array_reduce(
|
|
$trace,
|
|
function ($carry, $item) use (&$flatIndex, &$totalVendorGroups) {
|
|
$item['flatIndex'] = $flatIndex;
|
|
if ($item['applicationFrame']) {
|
|
$carry[] = $item;
|
|
} else {
|
|
if (Arr::get(Arr::last($carry), 'vendorGroup')) {
|
|
$carry[count($carry) - 1]['items'][] = $item;
|
|
} else {
|
|
$totalVendorGroups++;
|
|
$carry[] = [
|
|
'vendorGroup' => true,
|
|
'items' => [$item],
|
|
];
|
|
}
|
|
}
|
|
$flatIndex++;
|
|
return $carry;
|
|
},
|
|
[],
|
|
);
|
|
|
|
return [
|
|
'ignitionTrace' => true,
|
|
'message' => $report['message'],
|
|
'exception' => $report['exception_class'],
|
|
'file' => $report['stacktrace'][0]['file'],
|
|
'line' => $report['stacktrace'][0]['line_number'],
|
|
'trace' => $groupedTrace,
|
|
'totalVendorGroups' => $totalVendorGroups,
|
|
'phpVersion' => $report['language_version'],
|
|
'appVersion' => config('common.site.version'),
|
|
];
|
|
}
|
|
}
|