Update ApiV1Controller, implement better limit logic to gracefully handle requests with limits that exceed the max

This commit is contained in:
Daniel Supernault 2024-02-19 01:36:09 -07:00
parent 8cb7ebdd8b
commit 1f74a95d0c
No known key found for this signature in database
GPG key ID: 23740873EE6F76A1

View file

@ -496,9 +496,12 @@ class ApiV1Controller extends Controller
abort_if(!$account, 404); abort_if(!$account, 404);
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$this->validate($request, [ $this->validate($request, [
'limit' => 'sometimes|integer|min:1|max:80' 'limit' => 'sometimes|integer|min:1'
]); ]);
$limit = $request->input('limit', 10); $limit = $request->input('limit', 10);
if($limit > 80) {
$limit = 80;
}
$napi = $request->has(self::PF_API_ENTITY_KEY); $napi = $request->has(self::PF_API_ENTITY_KEY);
if($account && strpos($account['acct'], '@') != -1) { if($account && strpos($account['acct'], '@') != -1) {
@ -594,9 +597,12 @@ class ApiV1Controller extends Controller
abort_if(!$account, 404); abort_if(!$account, 404);
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$this->validate($request, [ $this->validate($request, [
'limit' => 'sometimes|integer|min:1|max:80' 'limit' => 'sometimes|integer|min:1'
]); ]);
$limit = $request->input('limit', 10); $limit = $request->input('limit', 10);
if($limit > 80) {
$limit = 80;
}
$napi = $request->has(self::PF_API_ENTITY_KEY); $napi = $request->has(self::PF_API_ENTITY_KEY);
if($account && strpos($account['acct'], '@') != -1) { if($account && strpos($account['acct'], '@') != -1) {
@ -698,7 +704,7 @@ class ApiV1Controller extends Controller
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'since_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'since_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|min:1|max:100' 'limit' => 'nullable|integer|min:1'
]); ]);
$napi = $request->has(self::PF_API_ENTITY_KEY); $napi = $request->has(self::PF_API_ENTITY_KEY);
@ -713,7 +719,10 @@ class ApiV1Controller extends Controller
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404); abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
} }
$limit = $request->limit ?? 20; $limit = $request->input('limit') ?? 20;
if($limit > 40) {
$limit = 40;
}
$max_id = $request->max_id; $max_id = $request->max_id;
$min_id = $request->min_id; $min_id = $request->min_id;
@ -959,12 +968,16 @@ class ApiV1Controller extends Controller
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$this->validate($request, [ $this->validate($request, [
'id' => 'required|array|min:1|max:20', 'id' => 'required|array|min:1',
'id.*' => 'required|integer|min:1|max:' . PHP_INT_MAX 'id.*' => 'required|integer|min:1|max:' . PHP_INT_MAX
]); ]);
$ids = $request->input('id');
if(count($ids) > 20) {
$ids = collect($ids)->take(20)->toArray();
}
$napi = $request->has(self::PF_API_ENTITY_KEY); $napi = $request->has(self::PF_API_ENTITY_KEY);
$pid = $request->user()->profile_id ?? $request->user()->profile->id; $pid = $request->user()->profile_id ?? $request->user()->profile->id;
$res = collect($request->input('id')) $res = collect($ids)
->filter(function($id) use($pid) { ->filter(function($id) use($pid) {
return intval($id) !== intval($pid); return intval($id) !== intval($pid);
}) })
@ -989,8 +1002,8 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'q' => 'required|string|min:1|max:255', 'q' => 'required|string|min:1|max:30',
'limit' => 'nullable|integer|min:1|max:40', 'limit' => 'nullable|integer|min:1',
'resolve' => 'nullable' 'resolve' => 'nullable'
]); ]);
@ -1000,22 +1013,23 @@ class ApiV1Controller extends Controller
AccountService::setLastActive($user->id); AccountService::setLastActive($user->id);
$query = $request->input('q'); $query = $request->input('q');
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
$resolve = (bool) $request->input('resolve', false); if($limit > 20) {
$q = '%' . $query . '%'; $limit = 20;
}
$resolve = $request->boolean('resolve', false);
$q = $query . '%';
$profiles = Cache::remember('api:v1:accounts:search:' . sha1($query) . ':limit:' . $limit, 86400, function() use($q, $limit) { $profiles = Profile::where('username', 'like', $q)
return Profile::whereNull('status') ->orderByDesc('followers_count')
->where('username', 'like', $q) ->limit($limit)
->orWhere('name', 'like', $q) ->pluck('id')
->limit($limit) ->map(function($id) {
->pluck('id') return AccountService::getMastodon($id);
->map(function($id) { })
return AccountService::getMastodon($id); ->filter(function($account) {
}) return $account && isset($account['id']);
->filter(function($account) { })
return $account && isset($account['id']); ->values();
});
});
return $this->json($profiles); return $this->json($profiles);
} }
@ -1033,20 +1047,25 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'nullable|integer|min:1|max:40', 'limit' => 'sometimes|integer|min:1',
'page' => 'nullable|integer|min:1|max:10' 'page' => 'sometimes|integer|min:1'
]); ]);
$user = $request->user(); $user = $request->user();
$limit = $request->input('limit') ?? 40; $limit = $request->input('limit') ?? 40;
if($limit > 80) {
$limit = 80;
}
$blocked = UserFilter::select('filterable_id','filterable_type','filter_type','user_id') $blocks = UserFilter::select('filterable_id','filterable_type','filter_type','user_id')
->whereUserId($user->profile_id) ->whereUserId($user->profile_id)
->whereFilterableType('App\Profile') ->whereFilterableType('App\Profile')
->whereFilterType('block') ->whereFilterType('block')
->orderByDesc('id') ->orderByDesc('id')
->simplePaginate($limit) ->simplePaginate($limit)
->pluck('filterable_id') ->withQueryString();
$res = $blocks->pluck('filterable_id')
->map(function($id) { ->map(function($id) {
return AccountService::get($id, true); return AccountService::get($id, true);
}) })
@ -1055,7 +1074,23 @@ class ApiV1Controller extends Controller
}) })
->values(); ->values();
return $this->json($blocked); $baseUrl = config('app.url') . '/api/v1/blocks?limit=' . $limit . '&';
$next = $blocks->nextPageUrl();
$prev = $blocks->previousPageUrl();
if($next && !$prev) {
$link = '<'.$next.'>; rel="next"';
}
if(!$next && $prev) {
$link = '<'.$prev.'>; rel="prev"';
}
if($next && $prev) {
$link = '<'.$next.'>; rel="next",<'.$prev.'>; rel="prev"';
}
$headers = isset($link) ? ['Link' => $link] : [];
return $this->json($res, 200, $headers);
} }
/** /**
@ -1247,13 +1282,16 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'sometimes|integer|min:1|max:40' 'limit' => 'sometimes|integer|min:1'
]); ]);
$user = $request->user(); $user = $request->user();
$maxId = $request->input('max_id'); $maxId = $request->input('max_id');
$minId = $request->input('min_id'); $minId = $request->input('min_id');
$limit = $request->input('limit') ?? 10; $limit = $request->input('limit') ?? 10;
if($limit > 40) {
$limit = 40;
}
$res = Like::whereProfileId($user->profile_id) $res = Like::whereProfileId($user->profile_id)
->when($maxId, function($q, $maxId) { ->when($maxId, function($q, $maxId) {
@ -1620,7 +1658,7 @@ class ApiV1Controller extends Controller
'thumbnail' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')), 'thumbnail' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'languages' => [config('app.locale')], 'languages' => [config('app.locale')],
'registrations' => (bool) config_cache('pixelfed.open_registration'), 'registrations' => (bool) config_cache('pixelfed.open_registration'),
'approval_required' => false, 'approval_required' => false, // (bool) config_cache('instance.curated_registration.enabled'),
'contact_account' => $contact, 'contact_account' => $contact,
'rules' => $rules, 'rules' => $rules,
'configuration' => [ 'configuration' => [
@ -2049,18 +2087,23 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'nullable|integer|min:1|max:40' 'limit' => 'sometimes|integer|min:1'
]); ]);
$user = $request->user(); $user = $request->user();
$limit = $request->input('limit', 40); $limit = $request->input('limit', 40);
if($limit > 80) {
$limit = 80;
}
$mutes = UserFilter::whereUserId($user->profile_id) $mutes = UserFilter::whereUserId($user->profile_id)
->whereFilterableType('App\Profile') ->whereFilterableType('App\Profile')
->whereFilterType('mute') ->whereFilterType('mute')
->orderByDesc('id') ->orderByDesc('id')
->simplePaginate($limit) ->simplePaginate($limit)
->pluck('filterable_id') ->withQueryString();
$res = $mutes->pluck('filterable_id')
->map(function($id) { ->map(function($id) {
return AccountService::get($id, true); return AccountService::get($id, true);
}) })
@ -2069,7 +2112,23 @@ class ApiV1Controller extends Controller
}) })
->values(); ->values();
return $this->json($mutes); $baseUrl = config('app.url') . '/api/v1/mutes?limit=' . $limit . '&';
$next = $mutes->nextPageUrl();
$prev = $mutes->previousPageUrl();
if($next && !$prev) {
$link = '<'.$next.'>; rel="next"';
}
if(!$next && $prev) {
$link = '<'.$prev.'>; rel="prev"';
}
if($next && $prev) {
$link = '<'.$next.'>; rel="next",<'.$prev.'>; rel="prev"';
}
$headers = isset($link) ? ['Link' => $link] : [];
return $this->json($res, 200, $headers);
} }
/** /**
@ -2181,7 +2240,7 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'nullable|integer|min:1|max:100', 'limit' => 'sometimes|integer|min:1',
'min_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX, 'min_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX, 'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
'since_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX, 'since_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
@ -2191,6 +2250,9 @@ class ApiV1Controller extends Controller
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$limit = $request->input('limit', 20); $limit = $request->input('limit', 20);
if($limit > 40) {
$limit = 40;
}
$since = $request->input('since_id'); $since = $request->input('since_id');
$min = $request->input('min_id'); $min = $request->input('min_id');
@ -2200,6 +2262,10 @@ class ApiV1Controller extends Controller
$min = 1; $min = 1;
} }
if($since) {
$min = $since + 1;
}
$types = $request->input('types'); $types = $request->input('types');
$maxId = null; $maxId = null;
@ -2261,7 +2327,7 @@ class ApiV1Controller extends Controller
'page' => 'sometimes|integer|max:40', 'page' => 'sometimes|integer|max:40',
'min_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX, 'min_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX, 'max_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'sometimes|integer|min:1|max:40', 'limit' => 'sometimes|integer|min:1',
'include_reblogs' => 'sometimes', 'include_reblogs' => 'sometimes',
]); ]);
@ -2270,6 +2336,9 @@ class ApiV1Controller extends Controller
$min = $request->input('min_id'); $min = $request->input('min_id');
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
if($limit > 40) {
$limit = 40;
}
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$includeReblogs = $request->filled('include_reblogs') ? $request->boolean('include_reblogs') : false; $includeReblogs = $request->filled('include_reblogs') ? $request->boolean('include_reblogs') : false;
$nullFields = $includeReblogs ? $nullFields = $includeReblogs ?
@ -2515,7 +2584,7 @@ class ApiV1Controller extends Controller
$this->validate($request,[ $this->validate($request,[
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|max:100', 'limit' => 'sometimes|integer|min:1',
'remote' => 'sometimes', 'remote' => 'sometimes',
'local' => 'sometimes' 'local' => 'sometimes'
]); ]);
@ -2525,6 +2594,9 @@ class ApiV1Controller extends Controller
$max = $request->input('max_id'); $max = $request->input('max_id');
$minOrMax = $request->anyFilled(['max_id', 'min_id']); $minOrMax = $request->anyFilled(['max_id', 'min_id']);
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
if($limit > 40) {
$limit = 40;
}
$user = $request->user(); $user = $request->user();
$remote = $request->has('remote'); $remote = $request->has('remote');
@ -3043,10 +3115,13 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'nullable|integer|min:1|max:80' 'limit' => 'sometimes|integer|min:1'
]); ]);
$limit = $request->input('limit', 10); $limit = $request->input('limit', 40);
if($limit > 80) {
$limit = 80;
}
$user = $request->user(); $user = $request->user();
$pid = $user->profile_id; $pid = $user->profile_id;
$status = Status::findOrFail($id); $status = Status::findOrFail($id);
@ -3485,7 +3560,7 @@ class ApiV1Controller extends Controller
'page' => 'nullable|integer|max:40', 'page' => 'nullable|integer|max:40',
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|max:100', 'limit' => 'sometimes|integer|min:1',
'only_media' => 'sometimes|boolean', 'only_media' => 'sometimes|boolean',
'_pe' => 'sometimes' '_pe' => 'sometimes'
]); ]);
@ -3518,6 +3593,9 @@ class ApiV1Controller extends Controller
$min = $request->input('min_id'); $min = $request->input('min_id');
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit', 20); $limit = $request->input('limit', 20);
if($limit > 40) {
$limit = 40;
}
$onlyMedia = $request->input('only_media', true); $onlyMedia = $request->input('only_media', true);
$pe = $request->has(self::PF_API_ENTITY_KEY); $pe = $request->has(self::PF_API_ENTITY_KEY);
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
@ -3547,7 +3625,7 @@ class ApiV1Controller extends Controller
->whereStatusVisibility('public') ->whereStatusVisibility('public')
->where('status_id', $dir, $id) ->where('status_id', $dir, $id)
->orderBy('status_id', 'desc') ->orderBy('status_id', 'desc')
->limit($limit) ->limit(100)
->pluck('status_id') ->pluck('status_id')
->map(function ($i) use($pe) { ->map(function ($i) use($pe) {
return $pe ? StatusService::get($i) : StatusService::getMastodon($i); return $pe ? StatusService::get($i) : StatusService::getMastodon($i);
@ -3565,6 +3643,7 @@ class ApiV1Controller extends Controller
$domain = strtolower(parse_url($i['url'], PHP_URL_HOST)); $domain = strtolower(parse_url($i['url'], PHP_URL_HOST));
return !in_array($i['account']['id'], $filters) && !in_array($domain, $domainBlocks); return !in_array($i['account']['id'], $filters) && !in_array($domain, $domainBlocks);
}) })
->take($limit)
->values() ->values()
->toArray(); ->toArray();
@ -3584,7 +3663,7 @@ class ApiV1Controller extends Controller
abort_unless($request->user()->tokenCan('read'), 403); abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'nullable|integer|min:1|max:40', 'limit' => 'sometimes|integer|min:1',
'max_id' => 'nullable|integer|min:0', 'max_id' => 'nullable|integer|min:0',
'since_id' => 'nullable|integer|min:0', 'since_id' => 'nullable|integer|min:0',
'min_id' => 'nullable|integer|min:0' 'min_id' => 'nullable|integer|min:0'
@ -3593,6 +3672,9 @@ class ApiV1Controller extends Controller
$pe = $request->has('_pe'); $pe = $request->has('_pe');
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
if($limit > 40) {
$limit = 40;
}
$max_id = $request->input('max_id'); $max_id = $request->input('max_id');
$since_id = $request->input('since_id'); $since_id = $request->input('since_id');
$min_id = $request->input('min_id'); $min_id = $request->input('min_id');
@ -3758,11 +3840,14 @@ class ApiV1Controller extends Controller
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$this->validate($request, [ $this->validate($request, [
'limit' => 'int|min:1|max:10', 'limit' => 'sometimes|integer|min:1',
'sort' => 'in:all,newest,popular' 'sort' => 'in:all,newest,popular'
]); ]);
$limit = $request->input('limit', 3); $limit = $request->input('limit', 3);
if($limit > 10) {
$limit = 10;
}
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$status = StatusService::getMastodon($id, false); $status = StatusService::getMastodon($id, false);