mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
Merge branch 'master' into feature/adds-new-parser-data
This commit is contained in:
commit
188d053741
@ -121,12 +121,12 @@ INSIGHTS_MAX_STORE_TIME=172800
|
||||
###
|
||||
# Error reporting
|
||||
###
|
||||
REPORTING=true
|
||||
REPORTING=false
|
||||
REPORTING_DRIVER=sentry
|
||||
SENTRY_LARAVEL_DSN="https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||
SENTRY_TRACES_SAMPLE_RATE=1
|
||||
SENTRY_TRACES_SAMPLE_RATE=0.5
|
||||
|
||||
###
|
||||
# Endpoints
|
||||
###
|
||||
DISABLE_USER_LISTS=false
|
||||
DISABLE_USER_LISTS=false
|
||||
|
@ -15,54 +15,54 @@ class GithubReport
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
private string $name;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $code;
|
||||
private string $code;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $error;
|
||||
private string $error;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $requestUri;
|
||||
private string $requestUri;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $requestMethod;
|
||||
private string $requestMethod;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $repo;
|
||||
private string $repo;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $trace;
|
||||
private string $trace;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $jikanVersion;
|
||||
private string $jikanVersion;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $redisRunning;
|
||||
private string $redisRunning;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $instanceType;
|
||||
private string $instanceType;
|
||||
|
||||
/**
|
||||
* @var
|
||||
* @var string
|
||||
*/
|
||||
private $phpVersion;
|
||||
private string $phpVersion;
|
||||
|
||||
/**
|
||||
* @param \Exception $exception
|
||||
|
@ -3,10 +3,7 @@
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Events\SourceHeartbeatEvent;
|
||||
use App\Http\HttpHelper;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@ -16,11 +13,9 @@ use Jikan\Exception\BadResponseException;
|
||||
use Jikan\Exception\ParserException;
|
||||
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
|
||||
use Predis\Connection\ConnectionException;
|
||||
use Symfony\Component\Debug\Exception\FlattenException;
|
||||
use Symfony\Component\HttpClient\Exception\TimeoutException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
@ -41,6 +36,13 @@ class Handler extends ExceptionHandler
|
||||
BadResponseException::class
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $acceptableForReportingDriver = [
|
||||
ParserException::class
|
||||
];
|
||||
|
||||
/**
|
||||
* @param \Throwable $e
|
||||
* @throws Exception
|
||||
@ -55,13 +57,10 @@ class Handler extends ExceptionHandler
|
||||
* @param \Throwable $e
|
||||
* @return JsonResponse|Response
|
||||
*/
|
||||
public function render($request, \Throwable $e)
|
||||
public function render($request, \Throwable $e): JsonResponse|Response
|
||||
{
|
||||
$githubReport = GithubReport::make($e, $request);
|
||||
|
||||
if (app()->bound('sentry') && $this->shouldReport($e)) {
|
||||
app('sentry')->captureException($e);
|
||||
}
|
||||
$this->reportToSentry($e);
|
||||
|
||||
// ConnectionException from Redis server
|
||||
if ($e instanceof ConnectionException) {
|
||||
@ -83,18 +82,6 @@ class Handler extends ExceptionHandler
|
||||
], 500);
|
||||
}
|
||||
|
||||
if ($e instanceof ConnectException) {
|
||||
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
|
||||
|
||||
return response()
|
||||
->json([
|
||||
'status' => $e->getCode(),
|
||||
'type' => 'BadResponseException',
|
||||
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond.',
|
||||
'error' => $e->getMessage()
|
||||
], 503);
|
||||
}
|
||||
|
||||
// ParserException from Jikan PHP API
|
||||
if ($e instanceof ParserException) {
|
||||
$githubReport->setRepo(env('GITHUB_API', 'jikan-me/jikan'));
|
||||
@ -108,9 +95,9 @@ class Handler extends ExceptionHandler
|
||||
], 500);
|
||||
}
|
||||
|
||||
// BadResponseException from Guzzle dep via Jikan PHP API
|
||||
// BadResponseException from Jikan PHP API
|
||||
// This is basically the response MyAnimeList returns to Jikan
|
||||
if ($e instanceof BadResponseException || $e instanceof ClientException) {
|
||||
if ($e instanceof BadResponseException) {
|
||||
switch ($e->getCode()) {
|
||||
case 404:
|
||||
// $this->set404Cache($request, $e);
|
||||
@ -157,6 +144,28 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
}
|
||||
|
||||
if ($e instanceof TimeoutException) {
|
||||
return response()
|
||||
->json([
|
||||
'status' => 408,
|
||||
'type' => 'TimeoutException',
|
||||
'message' => 'Request to MyAnimeList.net timed out (' .env('SOURCE_TIMEOUT', 5) . ' seconds)',
|
||||
'error' => $e->getMessage()
|
||||
], 408);
|
||||
}
|
||||
|
||||
if ($e instanceof Exception && $e->getMessage() === "Undefined index: url") {
|
||||
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
|
||||
|
||||
return response()
|
||||
->json([
|
||||
'status' => $e->getCode(),
|
||||
'type' => 'BadResponseException',
|
||||
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond. Retry the request!',
|
||||
'error' => $e->getMessage()
|
||||
], 503);
|
||||
}
|
||||
|
||||
// Bad REST API requests
|
||||
if ($e instanceof HttpException) {
|
||||
return response()
|
||||
@ -168,71 +177,32 @@ class Handler extends ExceptionHandler
|
||||
], $e->getStatusCode());
|
||||
}
|
||||
|
||||
if ($e instanceof TimeoutException) {
|
||||
return response()
|
||||
->json([
|
||||
'status' => 408,
|
||||
'type' => 'TimeoutException',
|
||||
'message' => 'Request to MyAnimeList.net timed out (' .env('SOURCE_TIMEOUT', 5) . ' seconds)',
|
||||
'error' => $e->getMessage()
|
||||
], 408);
|
||||
}
|
||||
|
||||
if ($e instanceof Exception) {
|
||||
if ($e->getMessage() === "Undefined index: url") {
|
||||
event(new SourceHeartbeatEvent(SourceHeartbeatEvent::BAD_HEALTH, $e->getCode()));
|
||||
|
||||
return response()
|
||||
->json([
|
||||
'status' => $e->getCode(),
|
||||
'type' => 'BadResponseException',
|
||||
'message' => 'Jikan failed to connect to MyAnimeList.net. MyAnimeList.net may be down/unavailable, refuses to connect or took too long to respond. Retry the request!',
|
||||
'error' => $e->getMessage()
|
||||
], 503);
|
||||
}
|
||||
|
||||
|
||||
return response()
|
||||
->json([
|
||||
'status' => 500,
|
||||
'type' => "Exception",
|
||||
'message' => 'Unhandled Exception. Please follow report_url to generate an issue on GitHub',
|
||||
'trace' => "{$e->getFile()} at line {$e->getLine()}",
|
||||
'error' => $e->getMessage(),
|
||||
'report_url' => env('GITHUB_REPORTING', true) ? (string) $githubReport : null
|
||||
], 500);
|
||||
}
|
||||
return response()
|
||||
->json([
|
||||
'status' => 500,
|
||||
'type' => "Exception",
|
||||
'message' => 'Unhandled Exception. Please follow report_url to generate an issue on GitHub',
|
||||
'trace' => "{$e->getFile()} at line {$e->getLine()}",
|
||||
'error' => $e->getMessage(),
|
||||
'report_url' => env('GITHUB_REPORTING', true) ? (string) $githubReport : null
|
||||
], 500);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param BadResponseException $e
|
||||
* @param Exception|\Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
private function set404Cache(Request $request, BadResponseException $e)
|
||||
private function reportToSentry(\Exception|\Throwable $e): void
|
||||
{
|
||||
if (!env('CACHING') || env('MICROCACHING')) {
|
||||
if (!app()->bound('sentry')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fingerprint = "request:404:".sha1(env('APP_URL') . $request->getRequestUri());
|
||||
|
||||
if (Cache::has($fingerprint)) {
|
||||
return;
|
||||
foreach ($this->acceptableForReportingDriver as $type) {
|
||||
if ($e instanceof $type) {
|
||||
app('sentry')->captureException($e);
|
||||
}
|
||||
}
|
||||
|
||||
$routeController = HttpHelper::requestControllerName($request);
|
||||
$cacheTtl = env('CACHE_DEFAULT_EXPIRE', 86400);
|
||||
|
||||
if (\in_array($routeController, [
|
||||
'AnimeController',
|
||||
'MangaController',
|
||||
'CharacterController',
|
||||
'PersonController'
|
||||
])) {
|
||||
$cacheTtl = env('CACHE_404_EXPIRE', 604800);
|
||||
}
|
||||
|
||||
|
||||
Cache::put($fingerprint, $e->getMessage(), $cacheTtl);
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Http\QueryBuilder;
|
||||
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Jenssegers\Mongodb\Eloquent\Builder;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class SearchQueryBuilderAnime
|
||||
* @package App\Http\QueryBuilder
|
||||
*/
|
||||
class TopQueryBuilderManga implements SearchQueryBuilderInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const MAP_TYPES = [
|
||||
'manga' => 'Manga',
|
||||
'novels' => 'Novel',
|
||||
'lightnovels' => 'Light Novel',
|
||||
'oneshots' => 'One-shot',
|
||||
'doujin' => 'Doujinshi',
|
||||
'manhwa' => 'Manhwa',
|
||||
'manhua' => 'Manhua'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const MAP_FILTER = [
|
||||
'publishing', 'upcoming', 'bypopularity', 'favorite'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Builder $builder
|
||||
@ -42,15 +43,15 @@
|
||||
{
|
||||
$mangaType = self::mapType($request->get('type'));
|
||||
$filterType = self::mapFilter($request->get('filter'));
|
||||
|
||||
|
||||
$results = $results
|
||||
->where('type', '!=', 'Doujinshi');
|
||||
|
||||
|
||||
if (!is_null($mangaType)) {
|
||||
$results = $results
|
||||
->where('type', $mangaType);
|
||||
}
|
||||
|
||||
|
||||
if (!is_null($filterType) && $filterType === 'publishing') {
|
||||
$results = $results
|
||||
->where('publishing', true)
|
||||
@ -60,14 +61,14 @@
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
if (!is_null($filterType) && $filterType === 'bypopularity') {
|
||||
$results = $results
|
||||
->orderBy('members', 'desc');
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
if (!is_null($filterType) && $filterType === 'favorite') {
|
||||
$results = $results
|
||||
->orderBy('favorites', 'desc');
|
||||
@ -82,7 +83,7 @@
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|null $type
|
||||
* @return string|null
|
||||
@ -92,12 +93,12 @@
|
||||
if (is_null($type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
$type = strtolower($type);
|
||||
|
||||
|
||||
return self::MAP_TYPES[$type] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|null $filter
|
||||
* @return string|null
|
||||
@ -105,11 +106,11 @@
|
||||
public static function mapFilter(?string $filter = null) : ?string
|
||||
{
|
||||
$filter = strtolower($filter);
|
||||
|
||||
|
||||
if (!\in_array($filter, self::MAP_FILTER)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class MangaFullResource extends JsonResource
|
||||
* @OA\Property(
|
||||
* property="type",
|
||||
* type="string",
|
||||
* enum={"Manga", "Novel", "One-shot", "Doujinshi", "Manhua", "Manhwa", "OEL"},
|
||||
* enum={"Manga", "Novel", "Light Novel", "One-shot", "Doujinshi", "Manhua", "Manhwa", "OEL"},
|
||||
* description="Manga Type",
|
||||
* nullable=true
|
||||
* ),
|
||||
@ -105,12 +105,14 @@ class MangaFullResource extends JsonResource
|
||||
* property="score",
|
||||
* type="number",
|
||||
* format="float",
|
||||
* description="Score"
|
||||
* description="Score",
|
||||
* nullable=true
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="scored_by",
|
||||
* type="integer",
|
||||
* description="Number of users"
|
||||
* description="Number of users",
|
||||
* nullable=true
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="rank",
|
||||
|
@ -61,7 +61,7 @@ class MangaResource extends JsonResource
|
||||
* @OA\Property(
|
||||
* property="type",
|
||||
* type="string",
|
||||
* enum={"Manga", "Novel", "One-shot", "Doujinshi", "Manhua", "Manhwa", "OEL"},
|
||||
* enum={"Manga", "Novel", "Light Novel", "One-shot", "Doujinshi", "Manhua", "Manhwa", "OEL"},
|
||||
* description="Manga Type",
|
||||
* nullable=true
|
||||
* ),
|
||||
@ -96,12 +96,14 @@ class MangaResource extends JsonResource
|
||||
* property="score",
|
||||
* type="number",
|
||||
* format="float",
|
||||
* description="Score"
|
||||
* description="Score",
|
||||
* nullable=true
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="scored_by",
|
||||
* type="integer",
|
||||
* description="Number of users"
|
||||
* description="Number of users",
|
||||
* nullable=true
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="rank",
|
||||
|
@ -85,7 +85,8 @@ class UserUpdatesResource extends JsonResource
|
||||
* @OA\Property(
|
||||
* property="score",
|
||||
* type="integer",
|
||||
* description="User Score"
|
||||
* description="User Score",
|
||||
* nullable=true
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="status",
|
||||
@ -127,4 +128,4 @@ class UserUpdatesResource extends JsonResource
|
||||
{
|
||||
return $this['users'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,11 @@ if (env('REPORTING') && env('REPORTING_DRIVER') === 'sentry') {
|
||||
$app->register(\Sentry\Laravel\ServiceProvider::class);
|
||||
// Sentry Performance Monitoring (optional)
|
||||
$app->register(\Sentry\Laravel\Tracing\ServiceProvider::class);
|
||||
|
||||
\Sentry\configureScope(function (\Sentry\State\Scope $scope): void {
|
||||
$scope->setTag('rest.jikan.version', env('APP_VERSION'));
|
||||
$scope->setTag('parser.jikan.version', JIKAN_PARSER_VERSION);
|
||||
});
|
||||
}
|
||||
|
||||
// Guzzle removed as of lumen 8.x
|
||||
|
@ -8,7 +8,7 @@ return [
|
||||
// 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')),
|
||||
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => env('SENTRY_ENVIRONMENT'),
|
||||
'environment' => env('APP_ENV'),
|
||||
|
||||
'breadcrumbs' => [
|
||||
// Capture Laravel logs in breadcrumbs
|
||||
@ -54,4 +54,6 @@ return [
|
||||
|
||||
'controllers_base_namespace' => env('SENTRY_CONTROLLERS_BASE_NAMESPACE', 'App\\Http\\Controllers'),
|
||||
|
||||
'release' => env('APP_VERSION')
|
||||
|
||||
];
|
||||
|
@ -6774,6 +6774,7 @@
|
||||
"enum": [
|
||||
"Manga",
|
||||
"Novel",
|
||||
"Light Novel",
|
||||
"One-shot",
|
||||
"Doujinshi",
|
||||
"Manhua",
|
||||
@ -6813,11 +6814,13 @@
|
||||
"score": {
|
||||
"description": "Score",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
"format": "float",
|
||||
"nullable": true
|
||||
},
|
||||
"scored_by": {
|
||||
"description": "Number of users",
|
||||
"type": "integer"
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"rank": {
|
||||
"description": "Ranking",
|
||||
@ -6968,6 +6971,7 @@
|
||||
"enum": [
|
||||
"Manga",
|
||||
"Novel",
|
||||
"Light Novel",
|
||||
"One-shot",
|
||||
"Doujinshi",
|
||||
"Manhua",
|
||||
@ -7007,11 +7011,13 @@
|
||||
"score": {
|
||||
"description": "Score",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
"format": "float",
|
||||
"nullable": true
|
||||
},
|
||||
"scored_by": {
|
||||
"description": "Number of users",
|
||||
"type": "integer"
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"rank": {
|
||||
"description": "Ranking",
|
||||
@ -8708,7 +8714,8 @@
|
||||
},
|
||||
"score": {
|
||||
"description": "User Score",
|
||||
"type": "integer"
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"status": {
|
||||
"description": "User list status",
|
||||
|
Loading…
x
Reference in New Issue
Block a user