Compile the WebUI and response with it if webui is enabled.
This commit is contained in:
@@ -4,3 +4,5 @@
|
||||
./var/*
|
||||
!./var/.gitignore
|
||||
.phpunit.result.cache
|
||||
frontend/.nuxt
|
||||
frontend/node_modules
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -1,3 +1,9 @@
|
||||
FROM node:lts-alpine as npm_builder
|
||||
|
||||
WORKDIR /frontend
|
||||
COPY frontend ./
|
||||
RUN yarn install --frozen-lockfile && npx nuxi@latest generate
|
||||
|
||||
FROM alpine:edge
|
||||
|
||||
COPY --from=composer:2 /usr/bin/composer /opt/bin/composer
|
||||
@@ -35,9 +41,11 @@ RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezo
|
||||
useradd -u ${USER_ID:-1000} -U -d /config -s /bin/bash user
|
||||
|
||||
# Copy source code to container.
|
||||
#
|
||||
COPY ./ /opt/app
|
||||
|
||||
# Copy frontend to public directory.
|
||||
COPY --chown=app:app --from=npm_builder /frontend/exported/ /opt/app/public/exported/
|
||||
|
||||
# Link PHP if needed.
|
||||
RUN if [ ! -f /usr/bin/php ]; then ln -s /usr/bin/php${PHP_V:3} /usr/bin/php; fi
|
||||
|
||||
|
||||
28
FAQ.md
28
FAQ.md
@@ -315,6 +315,7 @@ These environment variables relates to the tool itself, you can load them via th
|
||||
| WS_TRUST_HEADER | string | Which header contain user true IP. | `X-Forwarded-For` |
|
||||
| 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` |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> for environment variables that has `{TASK}` tag, you **MUST** replace it with one
|
||||
@@ -342,7 +343,8 @@ $ docker exec -ti watchstate console system:tasks
|
||||
|
||||
The Webhook URL is backend specific, the request path is `/v1/api/backends/[BACKEND_NAME]/webhook?apikey=[APIKEY]`,
|
||||
Where `[BACKEND_NAME]` is the name of the backend you want to add webhook for, and `[APIKEY]` is the global api key
|
||||
which you can get via the `system:apikey` command. Typically, the full path is `http://localhost:8080/v1/api/backends/[BACKEND_NAME]/webhook?apikey=[APIKEY]`. if the tool
|
||||
which you can get via the `system:apikey` command. Typically, the full path
|
||||
is `http://localhost:8080/v1/api/backends/[BACKEND_NAME]/webhook?apikey=[APIKEY]`. if the tool
|
||||
port is directly exposed or via the reverse proxy you have setup.
|
||||
|
||||
If your media backend support sending headers then remove query parameter `?apikey=[APIKEY]`, and add this header
|
||||
@@ -540,7 +542,8 @@ https://watchstate.example.org {
|
||||
Set this environment variable in your `docker-compose.yaml` file `WS_DISABLE_CACHE` with value of `1`.
|
||||
to use external redis server you need to alter the value of `WS_CACHE_URL` environment variable. the format for this
|
||||
variable is `redis://host:port?password=auth&db=db_num`, for example to use redis from another container you could use
|
||||
something like `redis://172.23.1.10:6379?password=my_secert_password&db=8`. We only support `redis` and API compatible alternative.
|
||||
something like `redis://172.23.1.10:6379?password=my_secert_password&db=8`. We only support `redis` and API compatible
|
||||
alternative.
|
||||
|
||||
Once that done, restart the container.
|
||||
|
||||
@@ -624,7 +627,8 @@ If everything is working correctly you should see something like this previous j
|
||||
|
||||
### I keep receiving this warning in log `INFO: Ignoring [xxx] Episode range, and treating it as single episode. Backend says it covers [00-00]`?
|
||||
|
||||
We recently added guard clause to prevent backends from sending possibly invalid episode ranges, as such if you see this,
|
||||
We recently added guard clause to prevent backends from sending possibly invalid episode ranges, as such if you see
|
||||
this,
|
||||
this likely means your backend mis-identified episodes range. By default, we allow an episode to cover up to 4 episodes.
|
||||
|
||||
If this is not enough for your library content. fear not we have you covered you can increase the limit by running the
|
||||
@@ -644,17 +648,20 @@ to inform you about the issue.
|
||||
|
||||
### I Keep receiving [jellyfin] item [id: name] is marked as [played] vs local state [unplayed], However due to the remote item date [date] being older than the last backend sync date [date]. it was not considered as valid state.
|
||||
|
||||
Sadly, this is due to bug in jellyfin, where it marks the item as played without updating the LastPlayedDate, and as such, watchstate doesn't really know the item has changed since last sync.
|
||||
Sadly, this is due to bug in jellyfin, where it marks the item as played without updating the LastPlayedDate, and as
|
||||
such, watchstate doesn't really know the item has changed since last sync.
|
||||
Unfortunately, there is no way to fix this issue from our side for the `state:import` task as it working as intended.
|
||||
|
||||
However, we managed to somewhat implement a workaround for this issue using the webhooks feature as temporary fix. Until jellyfin devs fixes the issue. Please take look at
|
||||
However, we managed to somewhat implement a workaround for this issue using the webhooks feature as temporary fix. Until
|
||||
jellyfin devs fixes the issue. Please take look at
|
||||
the webhooks section to enable it.
|
||||
|
||||
---
|
||||
|
||||
### Bare metal installation
|
||||
|
||||
We officially only support the docker container, however for the brave souls who want to install the tool directly on their server,
|
||||
We officially only support the docker container, however for the brave souls who want to install the tool directly on
|
||||
their server,
|
||||
You can follow these steps.
|
||||
|
||||
#### Requirements
|
||||
@@ -662,8 +669,10 @@ You can follow these steps.
|
||||
* [PHP 8.3](http://https://www.php.net/downloads.php) with both the `CLI` and `fpm` mode.
|
||||
* PHP Extensions `pdo`, `pdo-sqlite`, `mbstring`, `json`, `ctype`, `curl`, `redis`, `sodium` and `simplexml`.
|
||||
* [Composer](https://getcomposer.org/download/) for dependency management.
|
||||
* [Redis-server](https://redis.io/) for caching or a compatible implementation that works with [php-redis](https://github.com/phpredis/phpredis).
|
||||
* [Caddy](https://caddyserver.com/) for frontend handling. However, you can use whatever you like. As long as it has support for fastcgi.
|
||||
* [Redis-server](https://redis.io/) for caching or a compatible implementation that works
|
||||
with [php-redis](https://github.com/phpredis/phpredis).
|
||||
* [Caddy](https://caddyserver.com/) for frontend handling. However, you can use whatever you like. As long as it has
|
||||
support for fastcgi.
|
||||
|
||||
#### Installation
|
||||
|
||||
@@ -680,7 +689,8 @@ $ cd watchstate
|
||||
$ composer install --no-dev
|
||||
```
|
||||
|
||||
3. Create `.env` inside `./var/config/` if you need to change any of the environment variables refer to [Tool specific environment variables](#tool-specific-environment-variables) for more information. For example,
|
||||
3. Create `.env` inside `./var/config/` if you need to change any of the environment variables refer
|
||||
to [Tool specific environment variables](#tool-specific-environment-variables) for more information. For example,
|
||||
if your `redis` server is not on the same server or requires a password you can add the following to the `.env` file.
|
||||
|
||||
```dotenv
|
||||
|
||||
@@ -36,6 +36,9 @@ return (function () {
|
||||
'backend' => '[a-zA-Z0-9_-]+',
|
||||
],
|
||||
],
|
||||
'webui' => [
|
||||
'enabled' => (bool)env('WS_WEBUI_ENABLED', false),
|
||||
],
|
||||
'database' => [
|
||||
'version' => 'v01',
|
||||
],
|
||||
|
||||
@@ -9,18 +9,16 @@ http:// {
|
||||
|
||||
header * ?X-Request-Id "{http.request.uuid}"
|
||||
|
||||
try_files {path} exported/{path} /index.php
|
||||
|
||||
php_fastcgi 127.0.0.1:9000 {
|
||||
trusted_proxies private_ranges
|
||||
env X_REQUEST_ID "{http.request.uuid}"
|
||||
}
|
||||
|
||||
log
|
||||
file_server {
|
||||
hide .*
|
||||
}
|
||||
|
||||
# Disabled as workaround for arm/v7 build.
|
||||
#
|
||||
#log {
|
||||
# format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}" - "{resp_headers>X-Request-Id>[0]}" - "{resp_headers>X-Application-Version>[0]}"` {
|
||||
# time_format "02/Jan/2006:15:04:05 -0700"
|
||||
# }
|
||||
#}
|
||||
log
|
||||
}
|
||||
|
||||
@@ -107,3 +107,7 @@ hr {
|
||||
.is-paddingless {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.is-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
14
frontend/components/NoApi.vue
Normal file
14
frontend/components/NoApi.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="notification is-warning">
|
||||
<h1 class="title is-5">There is no <em class="is-bold">{{ api_var }}</em> configured.</h1>
|
||||
<p>Please configure the API connection using the button <i class="fa fa-cog"></i> in the top right corner of the
|
||||
page.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStorage} from "@vueuse/core";
|
||||
|
||||
const api_url = useStorage('api_url', '')
|
||||
const api_var = computed(() => (!api_url.value) ? 'API URL' : 'API token')
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<nav class="navbar is-dark">
|
||||
<nav class="navbar is-dark mb-4">
|
||||
<div class="navbar-brand pl-5">
|
||||
<NuxtLink class="navbar-item" href="/">
|
||||
<span class="icon-text">
|
||||
@@ -37,27 +37,84 @@
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-end pr-3">
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark has-tooltip-bottom" @click="selectedTheme = 'light'"
|
||||
v-if="'dark' === selectedTheme">
|
||||
<span class="icon is-small is-left has-text-warning">
|
||||
<button class="button is-dark" @click="selectedTheme = 'light'" v-if="'dark' === selectedTheme"
|
||||
v-tooltip="'Switch to light theme'">
|
||||
<span class="icon is-small has-text-warning">
|
||||
<i class="fas fa-sun"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-dark has-tooltip-bottom" @click="selectedTheme = 'dark'"
|
||||
v-if="'light' === selectedTheme">
|
||||
<span class="icon is-small is-left">
|
||||
<button class="button is-dark" @click="selectedTheme = 'dark'" v-if="'light' === selectedTheme"
|
||||
v-tooltip="'Switch to dark theme'">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-moon"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="showConnection = !showConnection" v-tooltip="'Configure connection'">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-cog"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="columns">
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-12 mt-2" v-if="showConnection">
|
||||
<form class="box" @submit.prevent="testApi">
|
||||
<div class="field">
|
||||
<label class="label" for="api_url">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-link"></i></span>
|
||||
<span>API URL</span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="control">
|
||||
<input class="input" id="api_url" type="url" v-model="api_url" required
|
||||
placeholder="API URL... http://localhost:8081"
|
||||
@keyup="api_status = false; api_response = ''">
|
||||
<p class="help">
|
||||
Use <a href="javascript:void(0)" @click="setOrigin">current page URL</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="api_token">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-key"></i></span>
|
||||
<span>API Token</span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="control">
|
||||
<input class="input" id="api_token" type="text" v-model="api_token" required placeholder="API Token..."
|
||||
@keyup="api_status = false; api_response = ''">
|
||||
</div>
|
||||
<p class="help">Can be obtained by using the <code>system:apikey</code> command.</p>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped has-addons-right">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="text" v-model="api_response" readonly disabled
|
||||
:class="{'has-background-success': true===api_status}">
|
||||
<p class="help">These settings are stored locally in your browser.</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary" :disabled="!api_url || !api_token">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-signs-post"></i></span>
|
||||
<span>Check</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<slot/>
|
||||
</div>
|
||||
@@ -80,7 +137,13 @@ import 'assets/css/style.css'
|
||||
import 'assets/css/all.css'
|
||||
import {useStorage} from '@vueuse/core'
|
||||
|
||||
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')());
|
||||
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')())
|
||||
const showConnection = ref(false)
|
||||
|
||||
const api_url = useStorage('api_url', '')
|
||||
const api_token = useStorage('api_token', '')
|
||||
const api_status = ref(false)
|
||||
const api_response = ref('Status: Unknown')
|
||||
|
||||
const Year = ref(new Date().getFullYear())
|
||||
const showMenu = ref(false)
|
||||
@@ -93,34 +156,34 @@ const applyPreferredColorScheme = (scheme) => {
|
||||
if (rule && rule.media && rule.media.mediaText.includes("prefers-color-scheme")) {
|
||||
switch (scheme) {
|
||||
case "light":
|
||||
rule.media.appendMedium("original-prefers-color-scheme");
|
||||
rule.media.appendMedium("original-prefers-color-scheme")
|
||||
if (rule.media.mediaText.includes("light")) {
|
||||
rule.media.deleteMedium("(prefers-color-scheme: light)");
|
||||
rule.media.deleteMedium("(prefers-color-scheme: light)")
|
||||
}
|
||||
if (rule.media.mediaText.includes("dark")) {
|
||||
rule.media.deleteMedium("(prefers-color-scheme: dark)");
|
||||
rule.media.deleteMedium("(prefers-color-scheme: dark)")
|
||||
}
|
||||
break;
|
||||
case "dark":
|
||||
rule.media.appendMedium("(prefers-color-scheme: light)");
|
||||
rule.media.appendMedium("(prefers-color-scheme: dark)");
|
||||
rule.media.appendMedium("(prefers-color-scheme: light)")
|
||||
rule.media.appendMedium("(prefers-color-scheme: dark)")
|
||||
if (rule.media.mediaText.includes("original")) {
|
||||
rule.media.deleteMedium("original-prefers-color-scheme");
|
||||
rule.media.deleteMedium("original-prefers-color-scheme")
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rule.media.appendMedium("(prefers-color-scheme: dark)");
|
||||
rule.media.appendMedium("(prefers-color-scheme: dark)")
|
||||
if (rule.media.mediaText.includes("light")) {
|
||||
rule.media.deleteMedium("(prefers-color-scheme: light)");
|
||||
rule.media.deleteMedium("(prefers-color-scheme: light)")
|
||||
}
|
||||
if (rule.media.mediaText.includes("original")) {
|
||||
rule.media.deleteMedium("original-prefers-color-scheme");
|
||||
rule.media.deleteMedium("original-prefers-color-scheme")
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
console.debug(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,15 +191,44 @@ const applyPreferredColorScheme = (scheme) => {
|
||||
|
||||
onMounted(() => {
|
||||
try {
|
||||
applyPreferredColorScheme(selectedTheme.value);
|
||||
applyPreferredColorScheme(selectedTheme.value)
|
||||
} catch (e) {
|
||||
}
|
||||
})
|
||||
|
||||
watch(selectedTheme, (value) => {
|
||||
try {
|
||||
applyPreferredColorScheme(value);
|
||||
applyPreferredColorScheme(value)
|
||||
} catch (e) {
|
||||
}
|
||||
})
|
||||
|
||||
const testApi = async () => {
|
||||
try {
|
||||
const response = await fetch(api_url.value + '/v1/api/backends', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + api_token.value,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (json.error) {
|
||||
api_status.value = false;
|
||||
api_response.value = `Error ${json.error.code} - ${json.error.message}`
|
||||
return
|
||||
}
|
||||
|
||||
api_status.value = 200 === response.status;
|
||||
api_response.value = 200 === response.status ? `Status: OK` : `Status: ${response.status} - ${response.statusText}`;
|
||||
|
||||
} catch (e) {
|
||||
api_status.value = false;
|
||||
api_response.value = `Error: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
const setOrigin = () => api_url.value = window.location.origin;
|
||||
</script>
|
||||
|
||||
@@ -22,6 +22,7 @@ export default defineNuxtConfig({
|
||||
},
|
||||
modules: [
|
||||
'@vueuse/nuxt',
|
||||
'floating-vue/nuxt'
|
||||
],
|
||||
nitro: {
|
||||
output: {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/nuxt": "^10.9.0",
|
||||
"floating-vue": "^5.2.2",
|
||||
"nuxt": "^3.11.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
<template>
|
||||
<p>foo</p>
|
||||
<template v-if="!api_url || !api_token">
|
||||
<no-api/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>foo</p>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import {useStorage} from "@vueuse/core"
|
||||
import NoApi from "~/components/NoApi.vue"
|
||||
|
||||
const api_url = useStorage('api_url', '')
|
||||
const api_token = useStorage('api_token', '')
|
||||
|
||||
useHead({title: 'Index'})
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,34 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Libs\Extends;
|
||||
|
||||
use App\Libs\HTTP_STATUS;
|
||||
use League\Route\Strategy\ApplicationStrategy;
|
||||
use League\Route\Strategy\OptionsHandlerInterface;
|
||||
use Nyholm\Psr7\Response;
|
||||
use Psr\Http\Message\ResponseInterface as iResponse;
|
||||
|
||||
class RouterStrategy extends ApplicationStrategy
|
||||
class RouterStrategy extends ApplicationStrategy implements OptionsHandlerInterface
|
||||
{
|
||||
public function getOptionsCallable(array $methods): callable
|
||||
{
|
||||
$headers = [
|
||||
'Allow' => implode(', ', $methods),
|
||||
];
|
||||
|
||||
$mode = ag($_SERVER, 'HTTP_SEC_FETCH_MODE');
|
||||
|
||||
if ('cors' !== $mode) {
|
||||
return fn(): iResponse => new Response(status: HTTP_STATUS::HTTP_NO_CONTENT->value, headers: $headers);
|
||||
}
|
||||
|
||||
$headers += [
|
||||
'Access-Control-Max-Age' => 600,
|
||||
'Access-Control-Allow-Headers' => 'X-Apikey, *',
|
||||
'Access-Control-Allow-Methods' => implode(', ', $methods),
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
];
|
||||
|
||||
return fn(): iResponse => new Response(status: HTTP_STATUS::HTTP_NO_CONTENT->value, headers: $headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -262,8 +262,26 @@ final class Initializer
|
||||
$apikey = ag($request->getQueryParams(), 'apikey', $request->getHeaderLine('x-apikey'));
|
||||
|
||||
if (empty($apikey)) {
|
||||
$response = api_response(HTTP_STATUS::HTTP_UNAUTHORIZED);
|
||||
$this->write($request, Level::Info, $this->formatLog($request, $response, 'No webhook token was found.'));
|
||||
if (false === (bool)Config::get('webui.enabled', false)) {
|
||||
$response = api_response(HTTP_STATUS::HTTP_UNAUTHORIZED);
|
||||
$this->write(
|
||||
$request,
|
||||
Level::Info,
|
||||
$this->formatLog($request, $response, 'No webhook token was found.')
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . '/../../public/exported/index.html')) {
|
||||
return new Response(
|
||||
status: HTTP_STATUS::HTTP_OK->value,
|
||||
headers: ['Content-Type' => 'text/html'],
|
||||
body: Stream::create(fopen(__DIR__ . '/../../public/exported/index.html', 'r'))
|
||||
);
|
||||
}
|
||||
|
||||
$response = api_response(HTTP_STATUS::HTTP_NOT_FOUND);
|
||||
$this->write($request, Level::Info, $this->formatLog($request, $response, 'The Frontend is not compiled.'));
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -369,7 +387,9 @@ final class Initializer
|
||||
if (!$response->hasHeader('X-Log-Response')) {
|
||||
$response = $response->withHeader('X-Log-Response', '1');
|
||||
}
|
||||
return $response;
|
||||
|
||||
return $response->withHeader('Access-Control-Allow-Origin', '*')
|
||||
->withHeader('Access-Control-Allow-Credentials', 'true');
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */
|
||||
catch (RouterHttpException $e) {
|
||||
throw new HttpException($e->getMessage(), $e->getStatusCode());
|
||||
|
||||
@@ -26,6 +26,10 @@ final class APIKeyRequiredMiddleware implements MiddlewareInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if ('OPTIONS' === $request->getMethod()) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
foreach (self::OPEN_ROUTES as $route) {
|
||||
$route = parseConfigValue($route);
|
||||
if (true === str_starts_with($request->getUri()->getPath(), parseConfigValue($route))) {
|
||||
|
||||
@@ -22,7 +22,7 @@ trait APITraits
|
||||
*/
|
||||
protected function getClient(string $name, array $config = []): iClient
|
||||
{
|
||||
$configFile = ConfigFile::open(Config::get('backends_file'), 'yaml');
|
||||
$configFile = ConfigFile::open(Config::get('backends_file'), 'yaml', autoCreate: true);
|
||||
|
||||
if (null === $configFile->get("{$name}.type", null)) {
|
||||
throw new RuntimeException(r("Backend '{backend}' doesn't exists.", ['backend' => $name]));
|
||||
@@ -44,7 +44,9 @@ trait APITraits
|
||||
{
|
||||
$backends = [];
|
||||
|
||||
foreach (ConfigFile::open(Config::get('backends_file'), 'yaml')->getAll() as $backendName => $backend) {
|
||||
$list = ConfigFile::open(Config::get('backends_file'), 'yaml', autoCreate: true)->getAll();
|
||||
|
||||
foreach ($list as $backendName => $backend) {
|
||||
$backend = ['name' => $backendName, ...$backend];
|
||||
|
||||
if (null !== ag($backend, 'import.lastSync')) {
|
||||
|
||||
Reference in New Issue
Block a user