Add CSRF token to all internal session-authenticated API requests.
This commit is contained in:
parent
95a9b8c781
commit
5a2f1a42e5
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -7,8 +7,23 @@ release channel, you can take advantage of these new features and fixes.
|
||||||
|
|
||||||
## Code Quality/Technical Changes
|
## Code Quality/Technical Changes
|
||||||
|
|
||||||
|
- A number of security fixes are being incorporated into the software as of this version. See below for details.
|
||||||
|
|
||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
|
||||||
|
## Security Fixes
|
||||||
|
|
||||||
|
- Session cookies are now marked as HTTP-only, avoiding possible use by custom JavaScript that may be injected into a
|
||||||
|
given page.
|
||||||
|
|
||||||
|
- If the "Always Use HTTPS" setting is enabled, session cookies will be sent as "secure only" as well.
|
||||||
|
|
||||||
|
- API calls will now either require API key authentication _or_ both a current active login session and a unique
|
||||||
|
identifier; if you're calling the API externally, you should _always_ use a generated API key and not count on the
|
||||||
|
user's existing session.
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# AzuraCast 0.14.1 (Aug 22, 2021)
|
# AzuraCast 0.14.1 (Aug 22, 2021)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Middleware\Auth\ApiAuth;
|
||||||
|
use App\Session\Csrf;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,19 +42,29 @@ return [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'vue-translations' => [
|
'vue-base' => [
|
||||||
'order' => 4,
|
'order' => 4,
|
||||||
'files' => [
|
'files' => [
|
||||||
'js' => [
|
'js' => [
|
||||||
[
|
[
|
||||||
'src' => 'dist/VueTranslations.js',
|
'src' => 'dist/VueBase.js',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'inline' => [
|
'inline' => [
|
||||||
'js' => [
|
'js' => [
|
||||||
function (Request $request) {
|
function (Request $request) {
|
||||||
return 'VueTranslations.default(App.locale);';
|
$csrfJson = 'null';
|
||||||
|
|
||||||
|
$csrf = $request->getAttribute(ServerRequest::ATTR_SESSION_CSRF);
|
||||||
|
if ($csrf instanceof Csrf) {
|
||||||
|
$csrfToken = $csrf->generate(ApiAuth::API_CSRF_NAMESPACE);
|
||||||
|
$csrfJson = json_encode($csrfToken, JSON_THROW_ON_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<JS
|
||||||
|
VueBase.default(App.locale, ${csrfJson});
|
||||||
|
JS;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -76,7 +88,7 @@ return [
|
||||||
|
|
||||||
'vue-component-common' => [
|
'vue-component-common' => [
|
||||||
'order' => 3,
|
'order' => 3,
|
||||||
'require' => ['vue', 'vue-translations'],
|
'require' => ['vue', 'vue-base'],
|
||||||
'files' => [
|
'files' => [
|
||||||
'js' => [
|
'js' => [
|
||||||
[
|
[
|
||||||
|
|
|
@ -83,7 +83,6 @@ return function (CallableEventDispatcherInterface $dispatcher) {
|
||||||
$app->add(Middleware\WrapExceptionsWithRequestData::class);
|
$app->add(Middleware\WrapExceptionsWithRequestData::class);
|
||||||
|
|
||||||
$app->add(Middleware\EnforceSecurity::class);
|
$app->add(Middleware\EnforceSecurity::class);
|
||||||
$app->add(Middleware\GetCurrentUser::class);
|
|
||||||
|
|
||||||
// Request injection middlewares.
|
// Request injection middlewares.
|
||||||
$app->add(Middleware\InjectRouter::class);
|
$app->add(Middleware\InjectRouter::class);
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Middleware;
|
||||||
use Slim\App;
|
use Slim\App;
|
||||||
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (App $app) {
|
||||||
|
$app->group(
|
||||||
call_user_func(include(__DIR__ . '/routes/admin.php'), $app);
|
'',
|
||||||
call_user_func(include(__DIR__ . '/routes/api.php'), $app);
|
function (RouteCollectorProxy $group) {
|
||||||
call_user_func(include(__DIR__ . '/routes/base.php'), $app);
|
call_user_func(include(__DIR__ . '/routes/admin.php'), $group);
|
||||||
call_user_func(include(__DIR__ . '/routes/public.php'), $app);
|
call_user_func(include(__DIR__ . '/routes/base.php'), $group);
|
||||||
call_user_func(include(__DIR__ . '/routes/stations.php'), $app);
|
call_user_func(include(__DIR__ . '/routes/public.php'), $group);
|
||||||
|
call_user_func(include(__DIR__ . '/routes/stations.php'), $group);
|
||||||
|
}
|
||||||
|
)->add(Middleware\Auth\StandardAuth::class);
|
||||||
|
|
||||||
|
$app->group(
|
||||||
|
'',
|
||||||
|
function (RouteCollectorProxy $group) {
|
||||||
|
call_user_func(include(__DIR__ . '/routes/api.php'), $group);
|
||||||
|
}
|
||||||
|
)->add(Middleware\Auth\ApiAuth::class);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
use App\Acl;
|
use App\Acl;
|
||||||
use App\Controller;
|
use App\Controller;
|
||||||
use App\Middleware;
|
use App\Middleware;
|
||||||
use Slim\App;
|
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (RouteCollectorProxy $app) {
|
||||||
$app->group(
|
$app->group(
|
||||||
'/admin',
|
'/admin',
|
||||||
function (RouteCollectorProxy $group) {
|
function (RouteCollectorProxy $group) {
|
||||||
|
|
|
@ -5,10 +5,9 @@ use App\Controller;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\Middleware;
|
use App\Middleware;
|
||||||
use Slim\App;
|
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (RouteCollectorProxy $app) {
|
||||||
$app->group(
|
$app->group(
|
||||||
'/api',
|
'/api',
|
||||||
function (RouteCollectorProxy $group) {
|
function (RouteCollectorProxy $group) {
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
use App\Controller;
|
use App\Controller;
|
||||||
use App\Middleware;
|
use App\Middleware;
|
||||||
use Slim\App;
|
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (RouteCollectorProxy $app) {
|
||||||
$app->get('/', Controller\Frontend\IndexAction::class)
|
$app->get('/', Controller\Frontend\IndexAction::class)
|
||||||
->setName('home');
|
->setName('home');
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
use App\Controller;
|
use App\Controller;
|
||||||
use App\Middleware;
|
use App\Middleware;
|
||||||
use Slim\App;
|
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (RouteCollectorProxy $app) {
|
||||||
$app->get('/sw.js', Controller\Frontend\PWA\ServiceWorkerAction::class)
|
$app->get('/sw.js', Controller\Frontend\PWA\ServiceWorkerAction::class)
|
||||||
->setName('public:sw');
|
->setName('public:sw');
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ use App\Controller;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\Middleware;
|
use App\Middleware;
|
||||||
use Slim\App;
|
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
|
||||||
return function (App $app) {
|
return static function (RouteCollectorProxy $app) {
|
||||||
$app->group(
|
$app->group(
|
||||||
'/station/{station_id}',
|
'/station/{station_id}',
|
||||||
function (RouteCollectorProxy $group) {
|
function (RouteCollectorProxy $group) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -64,6 +64,7 @@
|
||||||
"store": "^1.3.20",
|
"store": "^1.3.20",
|
||||||
"sweetalert2": "^10.16.6",
|
"sweetalert2": "^10.16.6",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
|
"vue-axios": "^3.2.5",
|
||||||
"vue-gettext": "^2.1.12",
|
"vue-gettext": "^2.1.12",
|
||||||
"vue-loader": "14.2.2",
|
"vue-loader": "14.2.2",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
|
|
|
@ -135,7 +135,6 @@
|
||||||
<script>
|
<script>
|
||||||
import {validationMixin} from "vuelidate";
|
import {validationMixin} from "vuelidate";
|
||||||
import handleAxiosError from "../../Function/handleAxiosError";
|
import handleAxiosError from "../../Function/handleAxiosError";
|
||||||
import axios from "axios";
|
|
||||||
import CodemirrorTextarea from "../../Common/CodemirrorTextarea";
|
import CodemirrorTextarea from "../../Common/CodemirrorTextarea";
|
||||||
import BWrappedFormGroup from "../../Form/BWrappedFormGroup";
|
import BWrappedFormGroup from "../../Form/BWrappedFormGroup";
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ export default {
|
||||||
this.$v.form.$reset();
|
this.$v.form.$reset();
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
axios.get(this.apiUrl).then((resp) => {
|
this.axios.get(this.apiUrl).then((resp) => {
|
||||||
this.populateForm(resp.data);
|
this.populateForm(resp.data);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -224,7 +223,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios({
|
this.axios({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
url: this.apiUrl,
|
url: this.apiUrl,
|
||||||
data: this.form
|
data: this.form
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from "../../Function/handleAxiosError";
|
import handleAxiosError from "../../Function/handleAxiosError";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -44,7 +43,7 @@ export default {
|
||||||
this.file = null;
|
this.file = null;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
axios.get(this.apiUrl).then((resp) => {
|
this.axios.get(this.apiUrl).then((resp) => {
|
||||||
this.isUploaded = resp.data.is_uploaded;
|
this.isUploaded = resp.data.is_uploaded;
|
||||||
this.url = resp.data.url;
|
this.url = resp.data.url;
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ export default {
|
||||||
|
|
||||||
},
|
},
|
||||||
clear() {
|
clear() {
|
||||||
axios.delete(this.apiUrl).then((resp) => {
|
this.axios.delete(this.apiUrl).then((resp) => {
|
||||||
this.relist();
|
this.relist();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
handleAxiosError(error);
|
handleAxiosError(error);
|
||||||
|
@ -70,7 +69,7 @@ export default {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('file', this.file);
|
formData.append('file', this.file);
|
||||||
|
|
||||||
axios.post(this.apiUrl, formData).then((resp) => {
|
this.axios.post(this.apiUrl, formData).then((resp) => {
|
||||||
this.relist();
|
this.relist();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
handleAxiosError(error);
|
handleAxiosError(error);
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import EditModal from './StorageLocations/EditModal';
|
import EditModal from './StorageLocations/EditModal';
|
||||||
import Icon from '../Common/Icon';
|
import Icon from '../Common/Icon';
|
||||||
import handleAxiosError from '../Function/handleAxiosError';
|
import handleAxiosError from '../Function/handleAxiosError';
|
||||||
|
@ -115,7 +114,7 @@ export default {
|
||||||
delay: 3000
|
delay: 3000
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.put(url).then((resp) => {
|
this.axios.put(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
@ -135,7 +134,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import {validationMixin} from 'vuelidate';
|
||||||
import { validationMixin } from 'vuelidate';
|
|
||||||
import required from 'vuelidate/src/validators/required';
|
import required from 'vuelidate/src/validators/required';
|
||||||
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
||||||
import BaseEditModal from '../../Common/BaseEditModal';
|
import BaseEditModal from '../../Common/BaseEditModal';
|
||||||
|
@ -126,7 +125,7 @@ export default {
|
||||||
let data = this.form;
|
let data = this.form;
|
||||||
data.type = this.type;
|
data.type = this.type;
|
||||||
|
|
||||||
axios({
|
this.axios({
|
||||||
method: (this.isEditMode)
|
method: (this.isEditMode)
|
||||||
? 'PUT'
|
? 'PUT'
|
||||||
: 'POST',
|
: 'POST',
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import {validationMixin} from 'vuelidate';
|
||||||
import { validationMixin } from 'vuelidate';
|
|
||||||
import handleAxiosError from '../Function/handleAxiosError';
|
import handleAxiosError from '../Function/handleAxiosError';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -56,7 +55,7 @@ export default {
|
||||||
this.doLoad(recordUrl);
|
this.doLoad(recordUrl);
|
||||||
},
|
},
|
||||||
doLoad (recordUrl) {
|
doLoad (recordUrl) {
|
||||||
axios.get(recordUrl).then((resp) => {
|
this.axios.get(recordUrl).then((resp) => {
|
||||||
this.populateForm(resp.data);
|
this.populateForm(resp.data);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -88,7 +87,7 @@ export default {
|
||||||
|
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
axios(this.buildSubmitRequest()).then((resp) => {
|
this.axios(this.buildSubmitRequest()).then((resp) => {
|
||||||
let notifyMessage = this.$gettext('Changes saved.');
|
let notifyMessage = this.$gettext('Changes saved.');
|
||||||
notify('<b>' + notifyMessage + '</b>', 'success');
|
notify('<b>' + notifyMessage + '</b>', 'success');
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,6 @@ table.b-table-selectable {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
@ -385,7 +384,7 @@ export default {
|
||||||
requestConfig = this.requestConfig(requestConfig);
|
requestConfig = this.requestConfig(requestConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(ctx.apiUrl, requestConfig).then((resp) => {
|
this.axios.get(ctx.apiUrl, requestConfig).then((resp) => {
|
||||||
this.flushCache = false;
|
this.flushCache = false;
|
||||||
this.totalRows = resp.data.total;
|
this.totalRows = resp.data.total;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<template></template>
|
<template></template>
|
||||||
<script>
|
<script>
|
||||||
import NowPlaying from '../Entity/NowPlaying';
|
import NowPlaying from '../Entity/NowPlaying';
|
||||||
import axios from 'axios';
|
|
||||||
import NchanSubscriber from 'nchan';
|
import NchanSubscriber from 'nchan';
|
||||||
|
|
||||||
export const nowPlayingProps = {
|
export const nowPlayingProps = {
|
||||||
|
@ -48,7 +47,7 @@ export default {
|
||||||
});
|
});
|
||||||
this.nchan_subscriber.start();
|
this.nchan_subscriber.start();
|
||||||
} else {
|
} else {
|
||||||
axios.get(this.nowPlayingUri).then((response) => {
|
this.axios.get(this.nowPlayingUri).then((response) => {
|
||||||
this.setNowPlaying(response.data);
|
this.setNowPlaying(response.data);
|
||||||
|
|
||||||
setTimeout(this.checkNowPlaying, 15000);
|
setTimeout(this.checkNowPlaying, 15000);
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
import WaveSurfer from 'wavesurfer.js';
|
import WaveSurfer from 'wavesurfer.js';
|
||||||
import timeline from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.js';
|
import timeline from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.js';
|
||||||
import regions from 'wavesurfer.js/dist/plugin/wavesurfer.regions.js';
|
import regions from 'wavesurfer.js/dist/plugin/wavesurfer.regions.js';
|
||||||
import axios from 'axios';
|
|
||||||
import getLogarithmicVolume from '../Function/GetLogarithmicVolume.js';
|
import getLogarithmicVolume from '../Function/GetLogarithmicVolume.js';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
@ -92,7 +91,7 @@ export default {
|
||||||
this.$emit('ready');
|
this.$emit('ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.get(this.waveformUrl).then((resp) => {
|
this.axios.get(this.waveformUrl).then((resp) => {
|
||||||
let waveform = resp.data;
|
let waveform = resp.data;
|
||||||
if (waveform.data) {
|
if (waveform.data) {
|
||||||
this.wavesurfer.load(this.audioUrl, waveform.data);
|
this.wavesurfer.load(this.audioUrl, waveform.data);
|
||||||
|
|
|
@ -158,7 +158,6 @@
|
||||||
<script>
|
<script>
|
||||||
import TimeSeriesChart from './Common/TimeSeriesChart';
|
import TimeSeriesChart from './Common/TimeSeriesChart';
|
||||||
import DataTable from './Common/DataTable';
|
import DataTable from './Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import Icon from './Common/Icon';
|
import Icon from './Common/Icon';
|
||||||
import Avatar, {avatarProps} from './Common/Avatar';
|
import Avatar, {avatarProps} from './Common/Avatar';
|
||||||
|
@ -227,7 +226,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.showCharts) {
|
if (this.showCharts) {
|
||||||
axios.get(this.chartsUrl).then((response) => {
|
this.axios.get(this.chartsUrl).then((response) => {
|
||||||
this.chartsData = response.data;
|
this.chartsData = response.data;
|
||||||
this.chartsLoading = false;
|
this.chartsLoading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -235,7 +234,7 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(this.notificationsUrl).then((response) => {
|
this.axios.get(this.notificationsUrl).then((response) => {
|
||||||
this.notifications = response.data;
|
this.notifications = response.data;
|
||||||
this.notificationsLoading = false;
|
this.notificationsLoading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -256,7 +255,7 @@ export default {
|
||||||
this.$eventHub.$emit('player_toggle', url);
|
this.$eventHub.$emit('player_toggle', url);
|
||||||
},
|
},
|
||||||
updateNowPlaying () {
|
updateNowPlaying () {
|
||||||
axios.get(this.stationsUrl).then((response) => {
|
this.axios.get(this.stationsUrl).then((response) => {
|
||||||
this.stationsLoading = false;
|
this.stationsLoading = false;
|
||||||
this.stations = response.data;
|
this.stations = response.data;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ img.album_art {
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import AlbumArt from '../Common/AlbumArt';
|
import AlbumArt from '../Common/AlbumArt';
|
||||||
import handleAxiosError from '../Function/handleAxiosError';
|
import handleAxiosError from '../Function/handleAxiosError';
|
||||||
|
@ -92,7 +91,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
doSubmitRequest (url) {
|
doSubmitRequest (url) {
|
||||||
axios.post(url).then((resp) => {
|
this.axios.post(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
this.$emit('submitted');
|
this.$emit('submitted');
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { validationMixin } from 'vuelidate';
|
import {validationMixin} from 'vuelidate';
|
||||||
import axios from 'axios';
|
|
||||||
import required from 'vuelidate/src/validators/required';
|
import required from 'vuelidate/src/validators/required';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import MediaFormBasicInfo from './Form/BasicInfo';
|
import MediaFormBasicInfo from './Form/BasicInfo';
|
||||||
|
@ -136,7 +135,7 @@ export default {
|
||||||
this.recordUrl = recordUrl;
|
this.recordUrl = recordUrl;
|
||||||
this.audioUrl = audioUrl;
|
this.audioUrl = audioUrl;
|
||||||
|
|
||||||
axios.get(recordUrl).then((resp) => {
|
this.axios.get(recordUrl).then((resp) => {
|
||||||
let d = resp.data;
|
let d = resp.data;
|
||||||
|
|
||||||
this.songLength = d.length_text;
|
this.songLength = d.length_text;
|
||||||
|
@ -189,7 +188,7 @@ export default {
|
||||||
|
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
axios.put(this.recordUrl, this.form).then((resp) => {
|
this.axios.put(this.recordUrl, this.form).then((resp) => {
|
||||||
let notifyMessage = this.$gettext('Changes saved.');
|
let notifyMessage = this.$gettext('Changes saved.');
|
||||||
notify('<b>' + notifyMessage + '</b>', 'success');
|
notify('<b>' + notifyMessage + '</b>', 'success');
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MediaFormAlbumArt',
|
name: 'MediaFormAlbumArt',
|
||||||
|
@ -55,7 +54,7 @@ export default {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('art', this.artFile);
|
formData.append('art', this.artFile);
|
||||||
|
|
||||||
axios.post(this.albumArtUrl, formData).then((resp) => {
|
this.axios.post(this.albumArtUrl, formData).then((resp) => {
|
||||||
this.reloadArt();
|
this.reloadArt();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@ -63,7 +62,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteArt () {
|
deleteArt () {
|
||||||
axios.delete(this.albumArtUrl).then((resp) => {
|
this.axios.delete(this.albumArtUrl).then((resp) => {
|
||||||
this.reloadArt();
|
this.reloadArt();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
@ -162,7 +161,7 @@ export default {
|
||||||
if (this.selectedItems.all.length) {
|
if (this.selectedItems.all.length) {
|
||||||
this.notifyPending();
|
this.notifyPending();
|
||||||
|
|
||||||
axios.put(this.batchUrl, {
|
this.axios.put(this.batchUrl, {
|
||||||
'do': action,
|
'do': action,
|
||||||
'current_directory': this.currentDirectory,
|
'current_directory': this.currentDirectory,
|
||||||
'files': this.selectedItems.files,
|
'files': this.selectedItems.files,
|
||||||
|
@ -195,7 +194,7 @@ export default {
|
||||||
if (this.selectedItems.all.length) {
|
if (this.selectedItems.all.length) {
|
||||||
this.notifyPending();
|
this.notifyPending();
|
||||||
|
|
||||||
axios.put(this.batchUrl, {
|
this.axios.put(this.batchUrl, {
|
||||||
'do': 'playlist',
|
'do': 'playlist',
|
||||||
'playlists': this.checkedPlaylists,
|
'playlists': this.checkedPlaylists,
|
||||||
'new_playlist_name': this.newPlaylist,
|
'new_playlist_name': this.newPlaylist,
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../../Common/DataTable.vue';
|
import DataTable from '../../Common/DataTable.vue';
|
||||||
import axios from 'axios';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
@ -79,7 +78,7 @@ export default {
|
||||||
this.$refs.modal.hide();
|
this.$refs.modal.hide();
|
||||||
},
|
},
|
||||||
doMove () {
|
doMove () {
|
||||||
(this.selectedItems.all.length) && axios.put(this.batchUrl, {
|
(this.selectedItems.all.length) && this.axios.put(this.batchUrl, {
|
||||||
'do': 'move',
|
'do': 'move',
|
||||||
'currentDirectory': this.currentDirectory,
|
'currentDirectory': this.currentDirectory,
|
||||||
'directory': this.destinationDirectory,
|
'directory': this.destinationDirectory,
|
||||||
|
|
|
@ -23,9 +23,8 @@
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { validationMixin } from 'vuelidate';
|
import {validationMixin} from 'vuelidate';
|
||||||
import { required } from 'vuelidate/lib/validators';
|
import {required} from 'vuelidate/lib/validators';
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -35,7 +34,7 @@ export default {
|
||||||
currentDirectory: String,
|
currentDirectory: String,
|
||||||
mkdirUrl: String
|
mkdirUrl: String
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
newDirectory: null
|
newDirectory: null
|
||||||
};
|
};
|
||||||
|
@ -62,7 +61,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.post(this.mkdirUrl, {
|
this.axios.post(this.mkdirUrl, {
|
||||||
'currentDirectory': this.currentDirectory,
|
'currentDirectory': this.currentDirectory,
|
||||||
'name': this.newDirectory
|
'name': this.newDirectory
|
||||||
}).then((resp) => {
|
}).then((resp) => {
|
||||||
|
|
|
@ -23,9 +23,8 @@
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { validationMixin } from 'vuelidate';
|
import {validationMixin} from 'vuelidate';
|
||||||
import { required } from 'vuelidate/lib/validators';
|
import {required} from 'vuelidate/lib/validators';
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -34,7 +33,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
renameUrl: String
|
renameUrl: String
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
file: null,
|
file: null,
|
||||||
|
@ -71,7 +70,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.put(this.renameUrl, this.form).then((resp) => {
|
this.axios.put(this.renameUrl, this.form).then((resp) => {
|
||||||
this.$refs.modal.hide();
|
this.$refs.modal.hide();
|
||||||
this.$emit('relist');
|
this.$emit('relist');
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -59,7 +59,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import EditModal from './Mounts/EditModal';
|
import EditModal from './Mounts/EditModal';
|
||||||
import Icon from '../Common/Icon';
|
import Icon from '../Common/Icon';
|
||||||
import InfoCard from '../Common/InfoCard';
|
import InfoCard from '../Common/InfoCard';
|
||||||
|
@ -114,7 +113,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../../Function/handleAxiosError';
|
import handleAxiosError from '../../../Function/handleAxiosError';
|
||||||
import FlowUpload from '../../../Common/FlowUpload';
|
import FlowUpload from '../../../Common/FlowUpload';
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ export default {
|
||||||
},
|
},
|
||||||
deleteIntro() {
|
deleteIntro() {
|
||||||
if (this.editIntroUrl) {
|
if (this.editIntroUrl) {
|
||||||
axios.delete(this.editIntroUrl).then((resp) => {
|
this.axios.delete(this.editIntroUrl).then((resp) => {
|
||||||
this.hasIntro = false;
|
this.hasIntro = false;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
handleAxiosError(err);
|
handleAxiosError(err);
|
||||||
|
|
|
@ -131,7 +131,6 @@ import EditModal from './Playlists/EditModal';
|
||||||
import ReorderModal from './Playlists/ReorderModal';
|
import ReorderModal from './Playlists/ReorderModal';
|
||||||
import ImportModal from './Playlists/ImportModal';
|
import ImportModal from './Playlists/ImportModal';
|
||||||
import QueueModal from './Playlists/QueueModal';
|
import QueueModal from './Playlists/QueueModal';
|
||||||
import axios from 'axios';
|
|
||||||
import Icon from '../Common/Icon';
|
import Icon from '../Common/Icon';
|
||||||
import handleAxiosError from '../Function/handleAxiosError';
|
import handleAxiosError from '../Function/handleAxiosError';
|
||||||
import CloneModal from './Playlists/CloneModal';
|
import CloneModal from './Playlists/CloneModal';
|
||||||
|
@ -260,7 +259,7 @@ export default {
|
||||||
delay: 3000
|
delay: 3000
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.put(url).then((resp) => {
|
this.axios.put(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
@ -283,7 +282,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -47,9 +47,8 @@
|
||||||
<script>
|
<script>
|
||||||
import required from 'vuelidate/src/validators/required';
|
import required from 'vuelidate/src/validators/required';
|
||||||
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
import { validationMixin } from 'vuelidate';
|
import {validationMixin} from 'vuelidate';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CloneModal',
|
name: 'CloneModal',
|
||||||
|
@ -102,7 +101,7 @@ export default {
|
||||||
|
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
axios({
|
this.axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: this.cloneUrl,
|
url: this.cloneUrl,
|
||||||
data: this.form
|
data: this.form
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
import InvisibleSubmitButton from '../../Common/InvisibleSubmitButton';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -53,7 +52,7 @@ export default {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('playlist_file', this.playlistFile);
|
formData.append('playlist_file', this.playlistFile);
|
||||||
|
|
||||||
axios.post(this.importPlaylistUrl, formData).then((resp) => {
|
this.axios.post(this.importPlaylistUrl, formData).then((resp) => {
|
||||||
if (resp.data.success) {
|
if (resp.data.success) {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -54,7 +53,7 @@ export default {
|
||||||
this.queueUrl = queueUrl;
|
this.queueUrl = queueUrl;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
axios.get(this.queueUrl).then((resp) => {
|
this.axios.get(this.queueUrl).then((resp) => {
|
||||||
this.media = resp.data;
|
this.media = resp.data;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
@ -62,7 +61,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
doClear () {
|
doClear () {
|
||||||
axios.delete(this.queueUrl).then((resp) => {
|
this.axios.delete(this.queueUrl).then((resp) => {
|
||||||
notify('<b>' + this.$gettext('Playlist queue cleared.') + '</b>', 'success');
|
notify('<b>' + this.$gettext('Playlist queue cleared.') + '</b>', 'success');
|
||||||
this.close();
|
this.close();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -41,7 +41,6 @@ table.sortable {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import Draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
@ -76,7 +75,7 @@ export default {
|
||||||
this.reorderUrl = reorderUrl;
|
this.reorderUrl = reorderUrl;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
axios.get(this.reorderUrl).then((resp) => {
|
this.axios.get(this.reorderUrl).then((resp) => {
|
||||||
this.media = resp.data;
|
this.media = resp.data;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
@ -100,7 +99,7 @@ export default {
|
||||||
newOrder[row.id] = i;
|
newOrder[row.id] = i;
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.put(this.reorderUrl, { 'order': newOrder }).then((resp) => {
|
this.axios.put(this.reorderUrl, {'order': newOrder}).then((resp) => {
|
||||||
notify('<b>' + this.$gettext('Playlist order set.') + '</b>', 'success');
|
notify('<b>' + this.$gettext('Playlist order set.') + '</b>', 'success');
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
handleAxiosError(err);
|
handleAxiosError(err);
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../../Function/handleAxiosError';
|
import handleAxiosError from '../../../Function/handleAxiosError';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -66,7 +65,7 @@ export default {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append('art', file);
|
formData.append('art', file);
|
||||||
|
|
||||||
axios.post(url, formData).then((resp) => {
|
this.axios.post(url, formData).then((resp) => {
|
||||||
this.$emit('input', resp.data);
|
this.$emit('input', resp.data);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
handleAxiosError(err);
|
handleAxiosError(err);
|
||||||
|
@ -74,7 +73,7 @@ export default {
|
||||||
},
|
},
|
||||||
deleteArt () {
|
deleteArt () {
|
||||||
if (this.editArtUrl) {
|
if (this.editArtUrl) {
|
||||||
axios.delete(this.editArtUrl).then((resp) => {
|
this.axios.delete(this.editArtUrl).then((resp) => {
|
||||||
this.src = null;
|
this.src = null;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
handleAxiosError(err);
|
handleAxiosError(err);
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
|
||||||
import handleAxiosError from '../../../Function/handleAxiosError';
|
import handleAxiosError from '../../../Function/handleAxiosError';
|
||||||
import FlowUpload from '../../../Common/FlowUpload';
|
import FlowUpload from '../../../Common/FlowUpload';
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ export default {
|
||||||
},
|
},
|
||||||
deleteMedia () {
|
deleteMedia () {
|
||||||
if (this.editMediaUrl) {
|
if (this.editMediaUrl) {
|
||||||
axios.delete(this.editMediaUrl).then((resp) => {
|
this.axios.delete(this.editMediaUrl).then((resp) => {
|
||||||
this.hasMedia = false;
|
this.hasMedia = false;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
handleAxiosError(err);
|
handleAxiosError(err);
|
||||||
|
|
|
@ -70,7 +70,6 @@
|
||||||
<script>
|
<script>
|
||||||
import DataTable from './../../Common/DataTable';
|
import DataTable from './../../Common/DataTable';
|
||||||
import EditModal from './EpisodeEditModal';
|
import EditModal from './EpisodeEditModal';
|
||||||
import axios from 'axios';
|
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
import AlbumArt from '../../Common/AlbumArt';
|
import AlbumArt from '../../Common/AlbumArt';
|
||||||
import EpisodeFormBasicInfo from './EpisodeForm/BasicInfo';
|
import EpisodeFormBasicInfo from './EpisodeForm/BasicInfo';
|
||||||
|
@ -142,7 +141,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -62,7 +62,6 @@
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../../Common/DataTable';
|
import DataTable from '../../Common/DataTable';
|
||||||
import EditModal from './PodcastEditModal';
|
import EditModal from './PodcastEditModal';
|
||||||
import axios from 'axios';
|
|
||||||
import AlbumArt from '../../Common/AlbumArt';
|
import AlbumArt from '../../Common/AlbumArt';
|
||||||
import handleAxiosError from '../../Function/handleAxiosError';
|
import handleAxiosError from '../../Function/handleAxiosError';
|
||||||
|
|
||||||
|
@ -139,7 +138,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -38,19 +38,18 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ProfileStreams from './Profile/StreamsPanel';
|
import ProfileStreams from './Profile/StreamsPanel';
|
||||||
import ProfileHeader, { profileHeaderProps } from './Profile/HeaderPanel';
|
import ProfileHeader, {profileHeaderProps} from './Profile/HeaderPanel';
|
||||||
import ProfileNowPlaying, { profileNowPlayingProps } from './Profile/NowPlayingPanel';
|
import ProfileNowPlaying, {profileNowPlayingProps} from './Profile/NowPlayingPanel';
|
||||||
import ProfileSchedule from './Profile/SchedulePanel';
|
import ProfileSchedule from './Profile/SchedulePanel';
|
||||||
import ProfileRequests, { profileRequestsProps } from './Profile/RequestsPanel';
|
import ProfileRequests, {profileRequestsProps} from './Profile/RequestsPanel';
|
||||||
import ProfileStreamers, { profileStreamersProps } from './Profile/StreamersPanel';
|
import ProfileStreamers, {profileStreamersProps} from './Profile/StreamersPanel';
|
||||||
import ProfilePublicPages, { profilePublicProps } from './Profile/PublicPagesPanel';
|
import ProfilePublicPages, {profilePublicProps} from './Profile/PublicPagesPanel';
|
||||||
import ProfileFrontend, { profileFrontendProps } from './Profile/FrontendPanel';
|
import ProfileFrontend, {profileFrontendProps} from './Profile/FrontendPanel';
|
||||||
import ProfileBackendNone from './Profile/BackendNonePanel';
|
import ProfileBackendNone from './Profile/BackendNonePanel';
|
||||||
import ProfileBackend, { profileBackendProps } from './Profile/BackendPanel';
|
import ProfileBackend, {profileBackendProps} from './Profile/BackendPanel';
|
||||||
import { profileEmbedModalProps } from './Profile/EmbedModal';
|
import {profileEmbedModalProps} from './Profile/EmbedModal';
|
||||||
import { BACKEND_NONE, FRONTEND_REMOTE } from '../Entity/RadioAdapters.js';
|
import {BACKEND_NONE, FRONTEND_REMOTE} from '../Entity/RadioAdapters.js';
|
||||||
import NowPlaying from '../Entity/NowPlaying';
|
import NowPlaying from '../Entity/NowPlaying';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -131,7 +130,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkNowPlaying () {
|
checkNowPlaying () {
|
||||||
axios.get(this.profileApiUri).then((response) => {
|
this.axios.get(this.profileApiUri).then((response) => {
|
||||||
let np = response.data;
|
let np = response.data;
|
||||||
np.loading = false;
|
np.loading = false;
|
||||||
this.np = np;
|
this.np = np;
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import QueueLogsModal from './Queue/LogsModal';
|
import QueueLogsModal from './Queue/LogsModal';
|
||||||
import handleAxiosError from '../Function/handleAxiosError';
|
import handleAxiosError from '../Function/handleAxiosError';
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.$refs.datatable.refresh();
|
this.$refs.datatable.refresh();
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import EditModal from './Mounts/EditModal';
|
import EditModal from './Mounts/EditModal';
|
||||||
import Icon from '../Common/Icon';
|
import Icon from '../Common/Icon';
|
||||||
import InfoCard from '../Common/InfoCard';
|
import InfoCard from '../Common/InfoCard';
|
||||||
|
@ -108,7 +107,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -164,7 +164,6 @@
|
||||||
<script>
|
<script>
|
||||||
import TimeSeriesChart from '../../Common/TimeSeriesChart';
|
import TimeSeriesChart from '../../Common/TimeSeriesChart';
|
||||||
import DataTable from '../../Common/DataTable';
|
import DataTable from '../../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
import Avatar, {avatarProps} from '../../Common/Avatar';
|
import Avatar, {avatarProps} from '../../Common/Avatar';
|
||||||
import DayOfWeekChart from './DayOfWeekChart';
|
import DayOfWeekChart from './DayOfWeekChart';
|
||||||
|
@ -220,21 +219,21 @@ export default {
|
||||||
created () {
|
created () {
|
||||||
moment.tz.setDefault('UTC');
|
moment.tz.setDefault('UTC');
|
||||||
|
|
||||||
axios.get(this.chartsUrl).then((response) => {
|
this.axios.get(this.chartsUrl).then((response) => {
|
||||||
this.chartsData = response.data;
|
this.chartsData = response.data;
|
||||||
this.chartsLoading = false;
|
this.chartsLoading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.get(this.bestAndWorstUrl).then((response) => {
|
this.axios.get(this.bestAndWorstUrl).then((response) => {
|
||||||
this.bestAndWorst = response.data;
|
this.bestAndWorst = response.data;
|
||||||
this.bestAndWorstLoading = false;
|
this.bestAndWorstLoading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.get(this.mostPlayedUrl).then((response) => {
|
this.axios.get(this.mostPlayedUrl).then((response) => {
|
||||||
this.mostPlayed = response.data;
|
this.mostPlayed = response.data;
|
||||||
this.mostPlayedLoading = false;
|
this.mostPlayedLoading = false;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import axios from 'axios';
|
|
||||||
import EditModal from './Streamers/EditModal';
|
import EditModal from './Streamers/EditModal';
|
||||||
import BroadcastsModal from './Streamers/BroadcastsModal';
|
import BroadcastsModal from './Streamers/BroadcastsModal';
|
||||||
import Schedule from '../Common/ScheduleView';
|
import Schedule from '../Common/ScheduleView';
|
||||||
|
@ -129,7 +128,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.relist();
|
this.relist();
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import DataTable from '../../Common/DataTable.vue';
|
import DataTable from '../../Common/DataTable.vue';
|
||||||
import axios from 'axios';
|
|
||||||
import formatFileSize from '../../Function/FormatFileSize.js';
|
import formatFileSize from '../../Function/FormatFileSize.js';
|
||||||
import InlinePlayer from '../../InlinePlayer';
|
import InlinePlayer from '../../InlinePlayer';
|
||||||
import Icon from '../../Common/Icon';
|
import Icon from '../../Common/Icon';
|
||||||
|
@ -137,7 +136,7 @@ export default {
|
||||||
focusCancel: true
|
focusCancel: true
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.value) {
|
if (result.value) {
|
||||||
axios.delete(url).then((resp) => {
|
this.axios.delete(url).then((resp) => {
|
||||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||||
|
|
||||||
this.$refs.datatable.refresh();
|
this.$refs.datatable.refresh();
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
|
import axios
|
||||||
|
from 'axios';
|
||||||
|
import VueAxios
|
||||||
|
from 'vue-axios';
|
||||||
import GetTextPlugin
|
import GetTextPlugin
|
||||||
from 'vue-gettext';
|
from 'vue-gettext';
|
||||||
import translations
|
import translations
|
||||||
from '../../resources/locale/translations';
|
from '../../resources/locale/translations';
|
||||||
|
|
||||||
export default function (lang) {
|
export default function (lang, csrf) {
|
||||||
|
// Configure localization
|
||||||
Vue.use(GetTextPlugin, {
|
Vue.use(GetTextPlugin, {
|
||||||
defaultLanguage: 'en_US',
|
defaultLanguage: 'en_US',
|
||||||
translations: translations,
|
translations: translations,
|
||||||
|
@ -11,4 +16,9 @@ export default function (lang) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.config.language = lang;
|
Vue.config.language = lang;
|
||||||
|
|
||||||
|
// Configure auto-CSRF on requests
|
||||||
|
axios.defaults.headers.common['X-API-CSRF'] = csrf;
|
||||||
|
|
||||||
|
Vue.use(VueAxios, axios);
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ const WebpackAssetsManifest = require('webpack-assets-manifest');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: {
|
entry: {
|
||||||
VueTranslations: './vue/VueTranslations.js',
|
VueBase: './vue/VueBase.js',
|
||||||
InlinePlayer: './vue/InlinePlayer.vue',
|
InlinePlayer: './vue/InlinePlayer.vue',
|
||||||
Dashboard: './vue/Dashboard.vue',
|
Dashboard: './vue/Dashboard.vue',
|
||||||
AdminBranding: './vue/Admin/Branding.vue',
|
AdminBranding: './vue/Admin/Branding.vue',
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Middleware\Auth;
|
||||||
|
|
||||||
|
use App\Acl;
|
||||||
|
use App\Customization;
|
||||||
|
use App\Entity;
|
||||||
|
use App\Environment;
|
||||||
|
use App\Http\ServerRequest;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
|
||||||
|
abstract class AbstractAuth implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Entity\Repository\SettingsRepository $settingsRepo,
|
||||||
|
protected Environment $environment,
|
||||||
|
protected Acl $acl
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
// Initialize Customization (timezones, locales, etc) based on the current logged in user.
|
||||||
|
$customization = new Customization(
|
||||||
|
environment: $this->environment,
|
||||||
|
settingsRepo: $this->settingsRepo,
|
||||||
|
request: $request
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize ACL (can only be initialized after Customization as it contains localizations).
|
||||||
|
$acl = $this->acl->withRequest($request);
|
||||||
|
|
||||||
|
$request = $request
|
||||||
|
->withAttribute(ServerRequest::ATTR_LOCALE, $customization->getLocale())
|
||||||
|
->withAttribute(ServerRequest::ATTR_CUSTOMIZATION, $customization)
|
||||||
|
->withAttribute(ServerRequest::ATTR_ACL, $acl);
|
||||||
|
|
||||||
|
// Set the Audit Log user.
|
||||||
|
Entity\AuditLog::setCurrentUser($request->getAttribute(ServerRequest::ATTR_USER));
|
||||||
|
|
||||||
|
$response = $handler->handle($request);
|
||||||
|
|
||||||
|
Entity\AuditLog::setCurrentUser();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Middleware\Auth;
|
||||||
|
|
||||||
|
use App\Acl;
|
||||||
|
use App\Auth;
|
||||||
|
use App\Entity;
|
||||||
|
use App\Environment;
|
||||||
|
use App\Exception\CsrfValidationException;
|
||||||
|
use App\Http\ServerRequest;
|
||||||
|
use App\Session\Csrf;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
|
||||||
|
class ApiAuth extends AbstractAuth
|
||||||
|
{
|
||||||
|
public const API_CSRF_NAMESPACE = 'api';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected Entity\Repository\UserRepository $userRepo,
|
||||||
|
protected Entity\Repository\ApiKeyRepository $apiKeyRepo,
|
||||||
|
Entity\Repository\SettingsRepository $settingsRepo,
|
||||||
|
Environment $environment,
|
||||||
|
Acl $acl
|
||||||
|
) {
|
||||||
|
parent::__construct($settingsRepo, $environment, $acl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
// Initialize the Auth for this request.
|
||||||
|
$user = $this->getApiUser($request);
|
||||||
|
|
||||||
|
$request = $request->withAttribute(ServerRequest::ATTR_USER, $user);
|
||||||
|
|
||||||
|
return parent::process($request, $handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getApiUser(ServerRequestInterface $request): ?Entity\User
|
||||||
|
{
|
||||||
|
$apiKey = $this->getApiKey($request);
|
||||||
|
|
||||||
|
if (!empty($apiKey)) {
|
||||||
|
$apiUser = $this->apiKeyRepo->authenticate($apiKey);
|
||||||
|
if (null !== $apiUser) {
|
||||||
|
return $apiUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to session login if available.
|
||||||
|
$csrfKey = $request->getHeaderLine('X-API-CSRF');
|
||||||
|
if (empty($csrfKey) && !$this->environment->isTesting()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$auth = new Auth(
|
||||||
|
userRepo: $this->userRepo,
|
||||||
|
session: $request->getAttribute(ServerRequest::ATTR_SESSION),
|
||||||
|
environment: $this->environment,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($auth->isLoggedIn()) {
|
||||||
|
$csrf = $request->getAttribute(ServerRequest::ATTR_SESSION_CSRF);
|
||||||
|
|
||||||
|
if ($csrf instanceof Csrf) {
|
||||||
|
try {
|
||||||
|
$csrf->verify($csrfKey, self::API_CSRF_NAMESPACE);
|
||||||
|
return $auth->getLoggedInUser();
|
||||||
|
} catch (CsrfValidationException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getApiKey(ServerRequestInterface $request): ?string
|
||||||
|
{
|
||||||
|
// Check authorization header
|
||||||
|
$auth_headers = $request->getHeader('Authorization');
|
||||||
|
$auth_header = $auth_headers[0] ?? '';
|
||||||
|
|
||||||
|
if (preg_match("/Bearer\s+(.*)$/i", $auth_header, $matches)) {
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check API key header
|
||||||
|
$api_key_headers = $request->getHeader('X-API-Key');
|
||||||
|
if (!empty($api_key_headers[0])) {
|
||||||
|
return $api_key_headers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cookies
|
||||||
|
$cookieParams = $request->getCookieParams();
|
||||||
|
if (!empty($cookieParams['token'])) {
|
||||||
|
return $cookieParams['token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check URL parameters as last resort
|
||||||
|
$queryParams = $request->getQueryParams();
|
||||||
|
$queryApiKey = $queryParams['api_key'] ?? null;
|
||||||
|
if (!empty($queryApiKey)) {
|
||||||
|
return $queryApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Middleware\Auth;
|
||||||
|
|
||||||
|
use App\Acl;
|
||||||
|
use App\Auth;
|
||||||
|
use App\Entity;
|
||||||
|
use App\Environment;
|
||||||
|
use App\Http\ServerRequest;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
|
||||||
|
class StandardAuth extends AbstractAuth
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Entity\Repository\UserRepository $userRepo,
|
||||||
|
Entity\Repository\SettingsRepository $settingsRepo,
|
||||||
|
Environment $environment,
|
||||||
|
Acl $acl
|
||||||
|
) {
|
||||||
|
parent::__construct($settingsRepo, $environment, $acl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
// Initialize the Auth for this request.
|
||||||
|
$auth = new Auth(
|
||||||
|
userRepo: $this->userRepo,
|
||||||
|
session: $request->getAttribute(ServerRequest::ATTR_SESSION),
|
||||||
|
environment: $this->environment,
|
||||||
|
);
|
||||||
|
$user = ($auth->isLoggedIn()) ? $auth->getLoggedInUser() : null;
|
||||||
|
|
||||||
|
$request = $request
|
||||||
|
->withAttribute(ServerRequest::ATTR_AUTH, $auth)
|
||||||
|
->withAttribute(ServerRequest::ATTR_USER, $user);
|
||||||
|
|
||||||
|
return parent::process($request, $handler);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ use Symfony\Component\VarDumper\VarDumper;
|
||||||
class Api
|
class Api
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Entity\Repository\ApiKeyRepository $api_repo,
|
protected Entity\Repository\ApiKeyRepository $apiKeyRepo,
|
||||||
protected Entity\Repository\SettingsRepository $settingsRepo,
|
protected Entity\Repository\SettingsRepository $settingsRepo,
|
||||||
protected Environment $environment
|
protected Environment $environment
|
||||||
) {
|
) {
|
||||||
|
@ -43,35 +43,26 @@ class Api
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt API key auth if a key exists.
|
// Attempt API key auth if a key exists.
|
||||||
$api_key = $this->getApiKey($request);
|
$apiUser = $request->getAttribute(ServerRequest::ATTR_USER);
|
||||||
$api_user = (!empty($api_key)) ? $this->api_repo->authenticate($api_key) : null;
|
|
||||||
|
|
||||||
// Override the request's "user" variable if API authentication is supplied and valid.
|
|
||||||
if ($api_user instanceof Entity\User) {
|
|
||||||
$request = $request->withAttribute(ServerRequest::ATTR_USER, $api_user);
|
|
||||||
$request->getAcl()->setRequest($request);
|
|
||||||
|
|
||||||
Entity\AuditLog::setCurrentUser($api_user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default cache control for API pages.
|
// Set default cache control for API pages.
|
||||||
$settings = $this->settingsRepo->readSettings();
|
$settings = $this->settingsRepo->readSettings();
|
||||||
|
|
||||||
$prefer_browser_url = $settings->getPreferBrowserUrl();
|
$preferBrowserUrl = $settings->getPreferBrowserUrl();
|
||||||
|
|
||||||
$response = $handler->handle($request);
|
$response = $handler->handle($request);
|
||||||
|
|
||||||
// Check for a user-set CORS header override.
|
// Check for a user-set CORS header override.
|
||||||
$acao_header = trim($settings->getApiAccessControl());
|
$acaoHeader = trim($settings->getApiAccessControl());
|
||||||
if (!empty($acao_header)) {
|
if (!empty($acaoHeader)) {
|
||||||
if ('*' === $acao_header) {
|
if ('*' === $acaoHeader) {
|
||||||
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
|
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
|
||||||
} else {
|
} else {
|
||||||
// Return the proper ACAO header matching the origin (if one exists).
|
// Return the proper ACAO header matching the origin (if one exists).
|
||||||
$origin = $request->getHeaderLine('Origin');
|
$origin = $request->getHeaderLine('Origin');
|
||||||
|
|
||||||
if (!empty($origin)) {
|
if (!empty($origin)) {
|
||||||
$rawOrigins = array_map('trim', explode(',', $acao_header));
|
$rawOrigins = array_map('trim', explode(',', $acaoHeader));
|
||||||
|
|
||||||
$baseUrl = $settings->getBaseUrl();
|
$baseUrl = $settings->getBaseUrl();
|
||||||
if (null !== $baseUrl) {
|
if (null !== $baseUrl) {
|
||||||
|
@ -97,7 +88,7 @@ class Api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($api_user instanceof Entity\User || in_array($request->getMethod(), ['GET', 'OPTIONS'])) {
|
} elseif ($apiUser instanceof Entity\User || in_array($request->getMethod(), ['GET', 'OPTIONS'])) {
|
||||||
// Default behavior:
|
// Default behavior:
|
||||||
// Only set global CORS for GET requests and API-authenticated requests;
|
// Only set global CORS for GET requests and API-authenticated requests;
|
||||||
// Session-authenticated, non-GET requests should only be made in a same-host situation.
|
// Session-authenticated, non-GET requests should only be made in a same-host situation.
|
||||||
|
@ -105,7 +96,7 @@ class Api
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($response instanceof Response && !$response->hasCacheLifetime()) {
|
if ($response instanceof Response && !$response->hasCacheLifetime()) {
|
||||||
if ($prefer_browser_url || $request->getAttribute(ServerRequest::ATTR_USER) instanceof Entity\User) {
|
if ($preferBrowserUrl || $request->getAttribute(ServerRequest::ATTR_USER) instanceof Entity\User) {
|
||||||
$response = $response->withNoCache();
|
$response = $response->withNoCache();
|
||||||
} else {
|
} else {
|
||||||
$response = $response->withCacheLifetime(15);
|
$response = $response->withCacheLifetime(15);
|
||||||
|
@ -114,38 +105,4 @@ class Api
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ServerRequest $request
|
|
||||||
*/
|
|
||||||
protected function getApiKey(ServerRequest $request): ?string
|
|
||||||
{
|
|
||||||
// Check authorization header
|
|
||||||
$auth_headers = $request->getHeader('Authorization');
|
|
||||||
$auth_header = $auth_headers[0] ?? '';
|
|
||||||
|
|
||||||
if (preg_match("/Bearer\s+(.*)$/i", $auth_header, $matches)) {
|
|
||||||
return $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check API key header
|
|
||||||
$api_key_headers = $request->getHeader('X-API-Key');
|
|
||||||
if (!empty($api_key_headers[0])) {
|
|
||||||
return $api_key_headers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cookies
|
|
||||||
$cookieParams = $request->getCookieParams();
|
|
||||||
if (!empty($cookieParams['token'])) {
|
|
||||||
return $cookieParams['token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check URL parameters as last resort
|
|
||||||
$queryApiKey = $request->getQueryParam('api-key');
|
|
||||||
if (!empty($queryApiKey)) {
|
|
||||||
return $queryApiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue