diff --git a/src/API/Backends/Index.php b/src/API/Backends/Index.php index e85a15a1..def312ec 100644 --- a/src/API/Backends/Index.php +++ b/src/API/Backends/Index.php @@ -48,7 +48,7 @@ final class Index $response['backends'][] = $backend; } - return api_response($response, HTTP_STATUS::HTTP_OK, []); + return api_response(HTTP_STATUS::HTTP_OK, $response, []); } public static function getBackends(string|null $name = null, bool $blacklist = false): array diff --git a/src/API/Backends/View.php b/src/API/Backends/View.php index dcb06035..dbee4781 100644 --- a/src/API/Backends/View.php +++ b/src/API/Backends/View.php @@ -35,8 +35,8 @@ final class View 'list' => (string)$apiUrl->withPath(parseConfigValue(self::URL)), ], ]; - - return api_response(['backend' => $response], HTTP_STATUS::HTTP_OK, []); + + return api_response(HTTP_STATUS::HTTP_OK, ['backend' => $response], []); } } diff --git a/src/API/History/Index.php b/src/API/History/Index.php index 18bbb1fe..632c2566 100644 --- a/src/API/History/Index.php +++ b/src/API/History/Index.php @@ -314,6 +314,6 @@ final class Index $response['history'][] = $item; } - return api_response($response, HTTP_STATUS::HTTP_OK, []); + return api_response(HTTP_STATUS::HTTP_OK, $response, []); } } diff --git a/src/API/History/View.php b/src/API/History/View.php index 0dc2cb9c..61ec347c 100644 --- a/src/API/History/View.php +++ b/src/API/History/View.php @@ -46,6 +46,6 @@ final readonly class View ], ]; - return api_response(['history' => $item], HTTP_STATUS::HTTP_OK, []); + return api_response(HTTP_STATUS::HTTP_OK, ['history' => $item], []); } } diff --git a/src/API/System/Env.php b/src/API/System/Env.php index 505ea69c..502fca6f 100644 --- a/src/API/System/Env.php +++ b/src/API/System/Env.php @@ -71,6 +71,6 @@ final class Env ]; } - return api_response($response, HTTP_STATUS::HTTP_OK, []); + return api_response(HTTP_STATUS::HTTP_OK, $response, []); } } diff --git a/src/API/Tasks/Index.php b/src/API/Tasks/Index.php index 47c9f2f9..1cd0e9b6 100644 --- a/src/API/Tasks/Index.php +++ b/src/API/Tasks/Index.php @@ -41,7 +41,7 @@ final class Index $response['tasks'][] = $task; } - return api_response($response, HTTP_STATUS::HTTP_OK, []); + return api_response(HTTP_STATUS::HTTP_OK, $response, []); } public static function formatTask(array $task): array diff --git a/src/API/Tasks/View.php b/src/API/Tasks/View.php index 3a1d762c..930ab520 100644 --- a/src/API/Tasks/View.php +++ b/src/API/Tasks/View.php @@ -35,6 +35,6 @@ final class View ], ]; - return api_response(['task' => $response], HTTP_STATUS::HTTP_OK); + return api_response(HTTP_STATUS::HTTP_OK, ['task' => $response]); } } diff --git a/src/Libs/Initializer.php b/src/Libs/Initializer.php index 7440e5e1..45286964 100644 --- a/src/Libs/Initializer.php +++ b/src/Libs/Initializer.php @@ -243,12 +243,12 @@ final class Initializer // -- health endpoint. if (true === str_starts_with($requestPath, '/healthcheck')) { - return api_response([], HTTP_STATUS::HTTP_OK); + return api_response(HTTP_STATUS::HTTP_OK, []); } // -- favicon endpoint. if (true === str_starts_with($requestPath, '/favicon.ico')) { - return api_response([], HTTP_STATUS::HTTP_NO_CONTENT); + return api_response(HTTP_STATUS::HTTP_NO_CONTENT, []); } // -- Forward requests to API server. @@ -269,7 +269,7 @@ final class Initializer 'headers' => $request->getHeaders(), 'query' => $request->getQueryParams(), ], true); - return api_response([], HTTP_STATUS::HTTP_UNAUTHORIZED); + return api_response(HTTP_STATUS::HTTP_UNAUTHORIZED, []); } $validUser = $validUUid = null; @@ -371,7 +371,7 @@ final class Initializer } $this->write($request, $loglevel ?? Level::Error, $message, ['messages' => $log]); - return api_response([], HTTP_STATUS::HTTP_UNAUTHORIZED); + return api_response(HTTP_STATUS::HTTP_UNAUTHORIZED, []); } // -- sanity check in case user has both import.enabled and options.IMPORT_METADATA_ONLY enabled. @@ -388,7 +388,7 @@ final class Initializer 'backend' => $class->getName() ]); - return api_response([], HTTP_STATUS::HTTP_NOT_ACCEPTABLE); + return api_response(HTTP_STATUS::HTTP_NOT_ACCEPTABLE, []); } $entity = $class->parseWebhook($request); @@ -412,7 +412,7 @@ final class Initializer ] ); - return api_response([], HTTP_STATUS::HTTP_NOT_MODIFIED); + return api_response(HTTP_STATUS::HTTP_NOT_MODIFIED, []); } if ((0 === (int)$entity->episode || null === $entity->season) && $entity->isEpisode()) { @@ -431,7 +431,7 @@ final class Initializer ] ); - return api_response([], HTTP_STATUS::HTTP_NOT_MODIFIED); + return api_response(HTTP_STATUS::HTTP_NOT_MODIFIED, []); } $cache = Container::get(CacheInterface::class); @@ -471,7 +471,7 @@ final class Initializer ] ]); - return api_response([], HTTP_STATUS::HTTP_OK); + return api_response(HTTP_STATUS::HTTP_OK, []); } /** diff --git a/src/Libs/helpers.php b/src/Libs/helpers.php index 1ecd3ed8..f81a2db6 100644 --- a/src/Libs/helpers.php +++ b/src/Libs/helpers.php @@ -383,16 +383,16 @@ if (!function_exists('api_response')) { /** * Create a API response. * - * @param array|null $body The JSON data to include in the response body. * @param HTTP_STATUS $status Optional. The HTTP status code. Default is {@see HTTP_STATUS::HTTP_OK}. + * @param array|null $body The JSON data to include in the response body. * @param array $headers Optional. Additional headers to include in the response. * @param string|null $reason Optional. The reason phrase to include in the response. Default is null. * * @return ResponseInterface A PSR-7 compatible response object. */ function api_response( - array|null $body, HTTP_STATUS $status = HTTP_STATUS::HTTP_OK, + array|null $body = null, array $headers = [], string|null $reason = null ): ResponseInterface { @@ -428,13 +428,14 @@ if (!function_exists('api_error')) { array $opts = [] ): ResponseInterface { $response = api_response( + status: $httpCode, body: array_replace_recursive($body, [ 'error' => [ 'code' => $httpCode->value, 'message' => $message ] ]), - status: $httpCode, + headers: $headers, reason: $reason ); @@ -1058,6 +1059,7 @@ if (false === function_exists('isValidURL')) { function isValidURL(string $url): bool { // RFC 3987 For absolute IRIs (internationalized): + /** @noinspection PhpArgumentWithoutNamedIdentifierInspection */ return (bool)@preg_match( '/^[a-z](?:[-a-z0-9\+\.])*:(?:\/\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:])*@)?(?:\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4}:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+[-a-z0-9\._~!\$&\'\(\)\*\+,;=:]+)\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=@])*)(?::[0-9]*)?(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])))(?:\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\x{E000}-\x{F8FF}\x{F0000}-\x{FFFFD}|\x{100000}-\x{10FFFD}\/\?])*)?(?:\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\/\?])*)?$/iu', $url diff --git a/tests/Libs/HelpersTest.php b/tests/Libs/HelpersTest.php index ef741dfb..ec35f264 100644 --- a/tests/Libs/HelpersTest.php +++ b/tests/Libs/HelpersTest.php @@ -320,7 +320,7 @@ class HelpersTest extends TestCase public function test_api_response(): void { $data = ['foo' => 'bar']; - $response = api_response($data, HTTP_STATUS::HTTP_OK); + $response = api_response(HTTP_STATUS::HTTP_OK, $data); $this->assertSame(HTTP_STATUS::HTTP_OK->value, $response->getStatusCode()); $this->assertSame('application/json', $response->getHeaderLine('Content-Type')); $this->assertSame(getAppVersion(), $response->getHeaderLine('X-Application-Version'));