Added the ability to secure the webhook endpoint.
This commit is contained in:
13
FAQ.md
13
FAQ.md
@@ -316,6 +316,7 @@ These environment variables relates to the tool itself, you can load them via th
|
||||
| WS_LIBRARY_SEGMENT | integer | Paginate backend library items request. Per request get total X number. | `1000` |
|
||||
| WS_CACHE_URL | string | Cache server URL. | `redis://127.0.0.1:6379` |
|
||||
| WS_WEBUI_ENABLED | bool | Enable Web UI. | `false` |
|
||||
| WS_SECURE_API_ENDPOINTS | bool | Disregard the open route policy and require API key for all endpoints. | `false` |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> for environment variables that has `{TASK}` tag, you **MUST** replace it with one
|
||||
@@ -404,16 +405,18 @@ Go to your Plex Web UI > Settings > Your Account > Webhooks > (Click ADD WEBHOOK
|
||||
|
||||
* Replace `[BACKEND_NAME]` with the name you have chosen for your backend.
|
||||
|
||||
> [!NOTE]
|
||||
> If you use multiple plex servers and use the same PlexPass account for all of them, You have to add each backend
|
||||
> using the same method above, while enabling `limit webhook events to` `selected user` and `backend unique id`.
|
||||
> Essentially, this method replaced the old unified webhook.token for backends.
|
||||
> [!IMPORTANT]
|
||||
> If you have enabled `WS_SECURE_API_ENDPOINTS`, you have to add `?apikey=yourapikey` to the end of the URL.
|
||||
|
||||
Click `Save Changes`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> [!NOTE]
|
||||
> If you share your plex server with other users, i,e. `Home/managed users`, you have to enable match user id, otherwise
|
||||
> their play state will end up changing your play state.
|
||||
>
|
||||
> If you use multiple plex servers and use the same PlexPass account for all of them, You have to add each backend
|
||||
> using the same method above, while enabling `limit webhook events to` `selected user` and `backend unique id`.
|
||||
> Essentially, this method replaced the old unified webhook.token for backends.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -11,6 +11,13 @@ out of the box, this tool support `Jellyfin`, `Plex` and `Emby` media servers.
|
||||
|
||||
### 2024-05-05
|
||||
|
||||
**Edit** - We received requests that people are exposing watchstate externally, and there was concern that having open webhook
|
||||
endpoints might lead to abuse. As such, we have added a new environment variable `WS_SECURE_API_ENDPOINTS`. Simply set
|
||||
the environment variable to `1` to secure the webhook endpoint. This means you have to add `?apikey=yourapikey` to the end
|
||||
of the webhook endpoint.
|
||||
|
||||
-----
|
||||
|
||||
We are deprecating the use of the following environment variables `WS_DISABLE_HTTP`, `WS_DISABLE_CRON`, `WS_DISABLE_CACHE`,
|
||||
and replacing them with `DISABLE_CACHE`, `DISABLE_CRON`, `DISABLE_HTTP`. The old environment variables will be removed in the future versions.
|
||||
It doesn't make sense to mark them as `WS_` since they are global and do not relate to the tool itself. And they must be set from the `compose.yaml` file itself.
|
||||
|
||||
72
composer.lock
generated
72
composer.lock
generated
@@ -1367,16 +1367,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
"version": "v3.4.2",
|
||||
"version": "v3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache-contracts.git",
|
||||
"reference": "2c9db6509a1b21dad229606897639d3284f54b2a"
|
||||
"reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/2c9db6509a1b21dad229606897639d3284f54b2a",
|
||||
"reference": "2c9db6509a1b21dad229606897639d3284f54b2a",
|
||||
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197",
|
||||
"reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1386,7 +1386,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.4-dev"
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
@@ -1423,7 +1423,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/cache-contracts/tree/v3.4.2"
|
||||
"source": "https://github.com/symfony/cache-contracts/tree/v3.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1439,7 +1439,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-23T14:51:35+00:00"
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
@@ -1537,16 +1537,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.4.0",
|
||||
"version": "v3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
|
||||
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1555,7 +1555,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.4-dev"
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
@@ -1584,7 +1584,7 @@
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1600,7 +1600,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-23T14:45:45+00:00"
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dotenv",
|
||||
@@ -1771,16 +1771,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
"version": "v3.4.2",
|
||||
"version": "v3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client-contracts.git",
|
||||
"reference": "b6b5c876b3a4ed74460e2c5ac53bbce2f12e2a7e"
|
||||
"reference": "20414d96f391677bf80078aa55baece78b82647d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/b6b5c876b3a4ed74460e2c5ac53bbce2f12e2a7e",
|
||||
"reference": "b6b5c876b3a4ed74460e2c5ac53bbce2f12e2a7e",
|
||||
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d",
|
||||
"reference": "20414d96f391677bf80078aa55baece78b82647d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1789,7 +1789,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.4-dev"
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
@@ -1829,7 +1829,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client-contracts/tree/v3.4.2"
|
||||
"source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1845,7 +1845,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-01T18:51:09+00:00"
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/lock",
|
||||
@@ -1989,21 +1989,22 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.4.2",
|
||||
"version": "v3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "11bbf19a0fb7b36345861e85c5768844c552906e"
|
||||
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e",
|
||||
"reference": "11bbf19a0fb7b36345861e85c5768844c552906e",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
|
||||
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"psr/container": "^1.1|^2.0"
|
||||
"psr/container": "^1.1|^2.0",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2"
|
||||
@@ -2011,7 +2012,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.4-dev"
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
@@ -2051,7 +2052,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.4.2"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2067,7 +2068,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-19T21:51:00+00:00"
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
@@ -3185,12 +3186,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "a6cc84fe50abd91fdbfa06fa0e7b93386aa2193c"
|
||||
"reference": "b88a8ff62ebab83b7c8fccae8984fba26d42ed4e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a6cc84fe50abd91fdbfa06fa0e7b93386aa2193c",
|
||||
"reference": "a6cc84fe50abd91fdbfa06fa0e7b93386aa2193c",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/b88a8ff62ebab83b7c8fccae8984fba26d42ed4e",
|
||||
"reference": "b88a8ff62ebab83b7c8fccae8984fba26d42ed4e",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
@@ -3404,7 +3405,7 @@
|
||||
"getkirby/panel": "<2.5.14",
|
||||
"getkirby/starterkit": "<=3.7.0.2",
|
||||
"gilacms/gila": "<=1.15.4",
|
||||
"gleez/cms": "<=1.2|==2",
|
||||
"gleez/cms": "<=1.3|==2",
|
||||
"globalpayments/php-sdk": "<2",
|
||||
"gogentooss/samlbase": "<1.2.7",
|
||||
"google/protobuf": "<3.15",
|
||||
@@ -3565,6 +3566,7 @@
|
||||
"nonfiction/nterchange": "<4.1.1",
|
||||
"notrinos/notrinos-erp": "<=0.7",
|
||||
"noumo/easyii": "<=0.9",
|
||||
"novaksolutions/infusionsoft-php-sdk": "<1",
|
||||
"nukeviet/nukeviet": "<4.5.02",
|
||||
"nyholm/psr7": "<1.6.1",
|
||||
"nystudio107/craft-seomatic": "<3.4.12",
|
||||
@@ -3657,7 +3659,7 @@
|
||||
"processwire/processwire": "<=3.0.210",
|
||||
"propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7",
|
||||
"propel/propel1": ">=1,<=1.7.1",
|
||||
"pterodactyl/panel": "<1.7",
|
||||
"pterodactyl/panel": "<1.11.6",
|
||||
"ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2",
|
||||
"ptrofimov/beanstalk_console": "<1.7.14",
|
||||
"pubnub/pubnub": "<6.1",
|
||||
@@ -3968,7 +3970,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-30T09:04:31+00:00"
|
||||
"time": "2024-05-05T05:04:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -32,6 +32,7 @@ return (function () {
|
||||
'api' => [
|
||||
'prefix' => '/v1/api',
|
||||
'key' => env('WS_API_KEY', null),
|
||||
'secure' => (bool)env('WS_SECURE_API_ENDPOINTS', false),
|
||||
'pattern_match' => [
|
||||
'backend' => '[a-zA-Z0-9_-]+',
|
||||
],
|
||||
|
||||
146
config/env.spec.php
Normal file
146
config/env.spec.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
* Last update: 2024-05-05
|
||||
*
|
||||
* This file contains the environment variables that are supported by the application.
|
||||
* All keys MUST start with WS_ and be in UPPERCASE and use _ as a separator.
|
||||
* Avoid using complex datatypes, the value should be a simple scalar value.
|
||||
*/
|
||||
|
||||
return (function () {
|
||||
// -- Do not forget to update the tasks list if you add a new task.
|
||||
$tasks = ['import', 'export', 'push', 'progress', 'backup', 'prune', 'indexes', 'requests'];
|
||||
$task_env = [
|
||||
'WS_CRON_{task}' => [
|
||||
'desc' => 'Enable the {task} task.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_CRON_{task}_AT' => [
|
||||
'desc' => 'The time to run the {task} task.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_CRON_{task}_ARGS' => [
|
||||
'desc' => 'The arguments to pass to the {task} task.',
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
|
||||
$env = [];
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
foreach ($task_env as $key => $info) {
|
||||
$info['desc'] = r($info['desc'], ['task' => $task]);
|
||||
$env[r($key, ['task' => strtoupper($task)])] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
$env = array_replace_recursive($env, [
|
||||
'WS_DATA_PATH' => [
|
||||
'description' => 'Where to store main data. (config, db).',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_TMP_DIR' => [
|
||||
'description' => 'Where to store temp data. (logs, cache)',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_TZ' => [
|
||||
'description' => 'Set the Tool timezone.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_LOGS_CONTEXT' => [
|
||||
'description' => 'Enable context in logs.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_LOGGER_FILE_ENABLE' => [
|
||||
'description' => 'Enable logging to app.log file',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_LOGGER_FILE_LEVEL' => [
|
||||
'description' => 'Set the log level for the file logger. Default: ERROR',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_WEBHOOK_DUMP_REQUEST' => [
|
||||
'description' => 'Dump all requests to webhook endpoint to a json file.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_TRUST_PROXY' => [
|
||||
'description' => 'Trust the IP from the WS_TRUST_HEADER header.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_TRUST_HEADER' => [
|
||||
'description' => 'The header with the true user IP.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_LIBRARY_SEGMENT' => [
|
||||
'description' => 'How many items to request per a request.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_CACHE_URL' => [
|
||||
'description' => 'The URL to the cache server.',
|
||||
'type' => 'string',
|
||||
'mask' => true,
|
||||
],
|
||||
'WS_CACHE_NULL' => [
|
||||
'description' => 'Enable the null cache. This is useful for testing. Or first time container startup.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_WEBUI_ENABLED' => [
|
||||
'description' => 'Enable the web UI.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_API_KEY' => [
|
||||
'description' => 'The API key to allow access to the API',
|
||||
'type' => 'string',
|
||||
'mask' => true,
|
||||
],
|
||||
'WS_LOGS_PRUNE_AFTER' => [
|
||||
'description' => 'Prune logs after this many days.',
|
||||
'type' => 'int',
|
||||
],
|
||||
'WS_EXPORT_THRESHOLD' => [
|
||||
'description' => 'Trigger full export mode if changes exceed this number.',
|
||||
'type' => 'int',
|
||||
],
|
||||
'WS_EPISODES_DISABLE_GUID' => [
|
||||
'description' => 'Disable the GUID field in the episodes.',
|
||||
'type' => 'bool',
|
||||
'deprecated' => true,
|
||||
],
|
||||
'WS_BACKENDS_FILE' => [
|
||||
'description' => 'The full path to the backends file.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_WEBHOOK_LOG_FILE_FORMAT' => [
|
||||
'description' => 'The name format for the webhook log file. Anything inside {} will be replaced with data from the webhook payload.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_CACHE_PREFIX' => [
|
||||
'description' => 'The prefix for the cache keys.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_CACHE_PATH' => [
|
||||
'description' => 'The path to the cache directory. This is usually if the cache server is not available.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_LOGGER_SYSLOG_FACILITY' => [
|
||||
'description' => 'The syslog facility to use.',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_LOGGER_SYSLOG_ENABLED' => [
|
||||
'description' => 'Enable logging to syslog.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
'WS_LOGGER_SYSLOG_LEVEL' => [
|
||||
'description' => 'Set the log level for the syslog logger. Default: ERROR',
|
||||
'type' => 'string',
|
||||
],
|
||||
'WS_SECURE_API_ENDPOINTS' => [
|
||||
'description' => 'Disregard the open route policy, and require an API key for all routes.',
|
||||
'type' => 'bool',
|
||||
],
|
||||
]);
|
||||
|
||||
ksort($env);
|
||||
|
||||
return $env;
|
||||
})();
|
||||
@@ -17,11 +17,6 @@ final class Env
|
||||
{
|
||||
public const string URL = '%{api.prefix}/system/env';
|
||||
|
||||
private const array MASK = [
|
||||
'WS_API_KEY',
|
||||
'WS_CACHE_URL'
|
||||
];
|
||||
|
||||
private EnvFile $envFile;
|
||||
|
||||
public function __construct()
|
||||
@@ -32,6 +27,8 @@ final class Env
|
||||
#[Get(self::URL . '[/]', name: 'system.env')]
|
||||
public function envList(iRequest $request): iResponse
|
||||
{
|
||||
$spec = require __DIR__ . '/../../../config/env.spec.php';
|
||||
|
||||
$response = [
|
||||
'data' => [],
|
||||
'file' => Config::get('path') . '/config/.env',
|
||||
@@ -45,7 +42,7 @@ final class Env
|
||||
$response['data'][] = [
|
||||
'key' => $key,
|
||||
'value' => $val,
|
||||
'mask' => in_array($key, self::MASK),
|
||||
'mask' => (bool)ag($spec, "{$key}.mask", false),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,17 @@ final class APIKeyRequiredMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public const string KEY_NAME = 'apikey';
|
||||
|
||||
private const array OPEN_ROUTES = [
|
||||
/**
|
||||
* Public routes that are accessible without an API key. and must remain open.
|
||||
*/
|
||||
private const array PUBLIC_ROUTES = [
|
||||
HealthCheck::URL,
|
||||
];
|
||||
|
||||
/**
|
||||
* Routes that follow the open route policy. However, those routes are subject to user configuration.
|
||||
*/
|
||||
private const array OPEN_ROUTES = [
|
||||
'/webhook'
|
||||
];
|
||||
|
||||
@@ -33,7 +42,12 @@ final class APIKeyRequiredMiddleware implements MiddlewareInterface
|
||||
|
||||
$requestPath = rtrim($request->getUri()->getPath(), '/');
|
||||
|
||||
foreach (self::OPEN_ROUTES as $route) {
|
||||
$openRoutes = self::PUBLIC_ROUTES;
|
||||
if (false === (bool)Config::get('api.secure')) {
|
||||
$openRoutes = array_merge($openRoutes, self::OPEN_ROUTES);
|
||||
}
|
||||
|
||||
foreach ($openRoutes as $route) {
|
||||
$route = rtrim(parseConfigValue($route), '/');
|
||||
if (true === str_starts_with($requestPath, $route) || true === str_ends_with($requestPath, $route)) {
|
||||
return $handler->handle($request);
|
||||
@@ -54,7 +68,7 @@ final class APIKeyRequiredMiddleware implements MiddlewareInterface
|
||||
return api_error(
|
||||
'API key is required to access the API.',
|
||||
HTTP_STATUS::HTTP_BAD_REQUEST,
|
||||
reason: 'API key is required to access the API.'
|
||||
reason: 'API key is required to access the API.',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user