Vuetify Liquidsoap config; other app cleanup.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-11-05 01:13:32 -05:00
parent 5caa21ba33
commit 87ea336a12
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
16 changed files with 986 additions and 824 deletions

View File

@ -176,7 +176,7 @@ return function (CallableEventDispatcherInterface $dispatcher) {
App\Radio\AutoDJ\Annotations::class,
App\Radio\Backend\Liquidsoap\ConfigWriter::class,
App\Sync\Task\NowPlayingTask::class,
App\Controller\Api\NowplayingController::class,
App\Controller\Api\NowPlayingAction::class,
]
);
};

View File

@ -1,6 +1,5 @@
<?php
use App\Acl;
use App\Controller;
use App\Http\Response;
use App\Http\ServerRequest;
@ -27,7 +26,7 @@ return static function (RouteCollectorProxy $app) {
$group->get('', Controller\Api\IndexController::class . ':indexAction')
->setName('api:index:index');
$group->get('/openapi.yml', Controller\Api\OpenApiController::class)
$group->get('/openapi.yml', Controller\Api\OpenApiAction::class)
->setName('api:openapi');
$group->get('/status', Controller\Api\IndexController::class . ':statusAction')
@ -36,65 +35,6 @@ return static function (RouteCollectorProxy $app) {
$group->get('/time', Controller\Api\IndexController::class . ':timeAction')
->setName('api:index:time');
$group->group(
'/frontend',
function (RouteCollectorProxy $group) {
$group->group(
'/account',
function (RouteCollectorProxy $group) {
$group->get('/me', Controller\Api\Frontend\Account\GetMeAction::class)
->setName('api:frontend:account:me');
$group->put('/me', Controller\Api\Frontend\Account\PutMeAction::class);
$group->put('/password', Controller\Api\Frontend\Account\PutPasswordAction::class)
->setName('api:frontend:account:password');
$group->get('/two-factor', Controller\Api\Frontend\Account\GetTwoFactorAction::class)
->setName('api:frontend:account:two-factor');
$group->put('/two-factor', Controller\Api\Frontend\Account\PutTwoFactorAction::class);
$group->delete('/two-factor', Controller\Api\Frontend\Account\DeleteTwoFactorAction::class);
$group->get(
'/api-keys',
Controller\Api\Frontend\Account\ApiKeysController::class . ':listAction'
)->setName('api:frontend:api-keys');
$group->post(
'/api-keys',
Controller\Api\Frontend\Account\ApiKeysController::class . ':createAction'
);
$group->get(
'/api-key/{id}',
Controller\Api\Frontend\Account\ApiKeysController::class . ':getAction'
)->setName('api:frontend:api-key');
$group->delete(
'/api-key/{id}',
Controller\Api\Frontend\Account\ApiKeysController::class . ':deleteAction'
);
}
);
$group->group(
'/dashboard',
function (RouteCollectorProxy $group) {
$group->get('/charts', Controller\Api\Frontend\Dashboard\ChartsAction::class)
->setName('api:frontend:dashboard:charts');
$group->get('/notifications', Controller\Api\Frontend\Dashboard\NotificationsAction::class)
->setName('api:frontend:dashboard:notifications');
$group->get('/stations', Controller\Api\Frontend\Dashboard\StationsAction::class)
->setName('api:frontend:dashboard:stations');
}
);
}
)->add(Middleware\RequireLogin::class);
$group->group(
'/internal',
function (RouteCollectorProxy $group) {
@ -150,693 +90,16 @@ return static function (RouteCollectorProxy $app) {
}
);
$group->get('/nowplaying[/{station_id}]', Controller\Api\NowplayingController::class)
$group->get('/nowplaying[/{station_id}]', Controller\Api\NowPlayingAction::class)
->setName('api:nowplaying:index');
$group->get('/stations', Controller\Api\Stations\IndexController::class . ':listAction')
->setName('api:stations:list')
->add(new Middleware\RateLimit('api'));
$group->group(
'/admin',
function (RouteCollectorProxy $group) {
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/api-keys',
Controller\Api\Admin\ApiKeysController::class . ':listAction'
)->setName('api:admin:api-keys');
$group->get(
'/api-key/{id}',
Controller\Api\Admin\ApiKeysController::class . ':getAction'
)->setName('api:admin:api-key');
$group->delete(
'/api-key/{id}',
Controller\Api\Admin\ApiKeysController::class . ':deleteAction'
);
}
)->add(new Middleware\Permissions(Acl::GLOBAL_API_KEYS));
$group->get('/auditlog', Controller\Api\Admin\AuditLogAction::class)
->setName('api:admin:auditlog')
->add(new Middleware\Permissions(Acl::GLOBAL_LOGS));
$group->group(
'/backups',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Admin\Backups\GetAction::class)
->setName('api:admin:backups');
$group->post('/run', Controller\Api\Admin\Backups\RunAction::class)
->setName('api:admin:backups:run');
$group->get('/log/{path}', Controller\Api\Admin\Backups\GetLogAction::class)
->setName('api:admin:backups:log');
$group->get('/download/{path}', Controller\Api\Admin\Backups\DownloadAction::class)
->setName('api:admin:backups:download');
$group->delete('/delete/{path}', Controller\Api\Admin\Backups\DeleteAction::class)
->setName('api:admin:backups:delete');
}
)->add(new Middleware\Permissions(Acl::GLOBAL_BACKUPS));
$group->get('/permissions', Controller\Api\Admin\PermissionsController::class)
->add(new Middleware\Permissions(Acl::GLOBAL_ALL));
$group->map(
['GET', 'POST'],
'/relays',
function (ServerRequest $request, Response $response) {
return $response->withRedirect(
(string)$request->getRouter()->fromHere('api:internal:relays')
);
}
);
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/settings[/{group}]',
Controller\Api\Admin\SettingsController::class . ':listAction'
)->setName('api:admin:settings');
$group->put(
'/settings[/{group}]',
Controller\Api\Admin\SettingsController::class . ':updateAction'
);
$group->get(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\GetCustomAssetAction::class
)->setName('api:admin:custom_assets');
$group->post(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\PostCustomAssetAction::class
);
$group->delete(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\DeleteCustomAssetAction::class
);
$group->get(
'/geolite',
Controller\Api\Admin\GeoLite\GetAction::class
)->setName('api:admin:geolite');
$group->post(
'/geolite',
Controller\Api\Admin\GeoLite\PostAction::class
);
$group->get(
'/shoutcast',
Controller\Api\Admin\Shoutcast\GetAction::class
)->setName('api:admin:shoutcast');
$group->post(
'/shoutcast',
Controller\Api\Admin\Shoutcast\PostAction::class
);
}
)->add(new Middleware\Permissions(Acl::GLOBAL_SETTINGS));
$admin_api_endpoints = [
[
'custom_field',
'custom_fields',
Controller\Api\Admin\CustomFieldsController::class,
Acl::GLOBAL_CUSTOM_FIELDS,
],
['role', 'roles', Controller\Api\Admin\RolesController::class, Acl::GLOBAL_ALL],
['station', 'stations', Controller\Api\Admin\StationsController::class, Acl::GLOBAL_STATIONS],
['user', 'users', Controller\Api\Admin\UsersController::class, Acl::GLOBAL_ALL],
[
'storage_location',
'storage_locations',
Controller\Api\Admin\StorageLocationsController::class,
Acl::GLOBAL_STORAGE_LOCATIONS,
],
];
foreach ($admin_api_endpoints as [$singular, $plural, $class, $permission]) {
$group->group(
'',
function (RouteCollectorProxy $group) use ($singular, $plural, $class) {
$group->get('/' . $plural, $class . ':listAction')
->setName('api:admin:' . $plural);
$group->post('/' . $plural, $class . ':createAction');
$group->get('/' . $singular . '/{id}', $class . ':getAction')
->setName('api:admin:' . $singular);
$group->put('/' . $singular . '/{id}', $class . ':editAction');
$group->delete('/' . $singular . '/{id}', $class . ':deleteAction');
}
)->add(new Middleware\Permissions($permission));
}
$group->post('/station/{id}/clone', Controller\Api\Admin\Stations\CloneAction::class)
->setName('api:admin:station:clone')
->add(new Middleware\Permissions(Acl::GLOBAL_STATIONS));
$group->get(
'/stations/storage-locations',
Controller\Api\Admin\Stations\StorageLocationsAction::class
)->setName('api:admin:stations:storage-locations')
->add(new Middleware\Permissions(Acl::GLOBAL_STATIONS));
}
);
$group->group(
'/station/{station_id}',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\IndexController::class . ':indexAction')
->setName('api:stations:index')
->add(new Middleware\RateLimit('api', 5, 2));
$group->group(
'/automation',
function (RouteCollectorProxy $group) {
$group->get(
'/settings',
Controller\Api\Stations\Automation\GetSettingsAction::class
)->setName('api:stations:automation:settings');
$group->put(
'/settings',
Controller\Api\Stations\Automation\PutSettingsAction::class
);
$group->put(
'/run',
Controller\Api\Stations\Automation\RunAction::class
)->setName('api:stations:automation:run');
}
)->add(new Middleware\Permissions(Acl::STATION_AUTOMATION, true));
$group->get('/nowplaying', Controller\Api\NowplayingController::class . ':indexAction');
$group->map(
['GET', 'POST'],
'/nowplaying/update',
Controller\Api\Stations\UpdateMetadataController::class
)
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get('/profile', Controller\Api\Stations\ProfileController::class)
->setName('api:stations:profile')
->add(new Middleware\Permissions(Acl::STATION_VIEW, true));
$group->get(
'/profile/edit',
Controller\Api\Stations\ProfileEditController::class . ':getProfileAction'
)->setName('api:stations:profile:edit')
->add(new Middleware\Permissions(Acl::STATION_PROFILE, true));
$group->put(
'/profile/edit',
Controller\Api\Stations\ProfileEditController::class . ':putProfileAction'
)->add(new Middleware\Permissions(Acl::STATION_PROFILE, true));
$group->get('/schedule', Controller\Api\Stations\ScheduleAction::class)
->setName('api:stations:schedule');
$group->get('/history', Controller\Api\Stations\HistoryController::class)
->setName('api:stations:history')
->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->group(
'/queue',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\QueueController::class . ':listAction')
->setName('api:stations:queue');
$group->post('/clear', Controller\Api\Stations\QueueController::class . ':clearAction')
->setName('api:stations:queue:clear');
$group->delete('/{id}', Controller\Api\Stations\QueueController::class . ':deleteAction')
->setName('api:stations:queue:record');
}
)->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get('/requests', Controller\Api\Stations\RequestsController::class . ':listAction')
->setName('api:requests:list');
$group->map(
['GET', 'POST'],
'/request/{media_id}',
Controller\Api\Stations\RequestsController::class . ':submitAction'
)
->setName('api:requests:submit')
->add(new Middleware\RateLimit('api', 5, 2));
$group->get('/ondemand', Controller\Api\Stations\OnDemand\ListAction::class)
->setName('api:stations:ondemand:list');
$group->get('/ondemand/download/{media_id}', Controller\Api\Stations\OnDemand\DownloadAction::class)
->setName('api:stations:ondemand:download')
->add(new Middleware\RateLimit('ondemand', 1, 2));
$group->get('/listeners', Controller\Api\Stations\ListenersAction::class)
->setName('api:listeners:index')
->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->get(
'/waveform/{media_id:[a-zA-Z0-9\-]+}.json',
Controller\Api\Stations\Waveform\GetWaveformAction::class
)
->setName('api:stations:media:waveform');
$group->get('/art/{media_id:[a-zA-Z0-9\-]+}.jpg', Controller\Api\Stations\Art\GetArtAction::class)
->setName('api:stations:media:art');
$group->get('/art/{media_id:[a-zA-Z0-9\-]+}', Controller\Api\Stations\Art\GetArtAction::class)
->setName('api:stations:media:art-internal');
$group->post('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\PostArtAction::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->delete('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\DeleteArtAction::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
// Public and private podcast pages
$group->group(
'/podcast/{podcast_id}',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\PodcastsController::class . ':getAction')
->setName('api:stations:podcast');
$group->get(
'/art',
Controller\Api\Stations\Podcasts\Art\GetArtAction::class
)->setName('api:stations:podcast:art');
$group->get(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':listAction'
)->setName('api:stations:podcast:episodes');
$group->group(
'/episode/{episode_id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':getAction'
)->setName('api:stations:podcast:episode');
$group->get(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\GetArtAction::class
)->setName('api:stations:podcast:episode:art');
$group->get(
'/download',
Controller\Api\Stations\Podcasts\Episodes\Media\GetMediaAction::class
)->setName('api:stations:podcast:episode:download');
}
);
}
)->add(Middleware\RequirePublishedPodcastEpisodeMiddleware::class);
// Private-only podcast pages
$group->group(
'/podcasts',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\PodcastsController::class . ':listAction')
->setName('api:stations:podcasts');
$group->post('', Controller\Api\Stations\PodcastsController::class . ':createAction');
$group->post('/art', Controller\Api\Stations\Podcasts\Art\PostArtAction::class)
->setName('api:stations:podcasts:new-art');
}
)->add(new Middleware\Permissions(Acl::STATION_PODCASTS, true));
$group->group(
'/podcast/{podcast_id}',
function (RouteCollectorProxy $group) {
$group->put('', Controller\Api\Stations\PodcastsController::class . ':editAction');
$group->delete('', Controller\Api\Stations\PodcastsController::class . ':deleteAction');
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Art\PostArtAction::class
)->setName('api:stations:podcast:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Art\DeleteArtAction::class
);
$group->post(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':createAction'
);
$group->post(
'/episodes/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episodes:new-art');
$group->post(
'/episodes/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episodes:new-media');
$group->group(
'/episode/{episode_id}',
function (RouteCollectorProxy $group) {
$group->put(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':deleteAction'
);
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episode:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\DeleteArtAction::class
);
$group->post(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episode:media-internal');
$group->delete(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\DeleteMediaAction::class
);
}
);
}
)->add(new Middleware\Permissions(Acl::STATION_PODCASTS, true));
$station_api_endpoints = [
['file', 'files', Controller\Api\Stations\FilesController::class, Acl::STATION_MEDIA],
['mount', 'mounts', Controller\Api\Stations\MountsController::class, Acl::STATION_MOUNTS],
[
'playlist',
'playlists',
Controller\Api\Stations\PlaylistsController::class,
Acl::STATION_MEDIA,
],
['remote', 'remotes', Controller\Api\Stations\RemotesController::class, Acl::STATION_REMOTES],
[
'sftp-user',
'sftp-users',
Controller\Api\Stations\SftpUsersController::class,
Acl::STATION_MEDIA,
],
[
'streamer',
'streamers',
Controller\Api\Stations\StreamersController::class,
Acl::STATION_STREAMERS,
],
[
'webhook',
'webhooks',
Controller\Api\Stations\WebhooksController::class,
Acl::STATION_WEB_HOOKS,
],
];
foreach ($station_api_endpoints as [$singular, $plural, $class, $permission]) {
$group->group(
'',
function (RouteCollectorProxy $group) use ($singular, $plural, $class) {
$group->get('/' . $plural, $class . ':listAction')
->setName('api:stations:' . $plural);
$group->post('/' . $plural, $class . ':createAction');
$group->get('/' . $singular . '/{id}', $class . ':getAction')
->setName('api:stations:' . $singular);
$group->put('/' . $singular . '/{id}', $class . ':editAction');
$group->delete('/' . $singular . '/{id}', $class . ':deleteAction');
}
)->add(new Middleware\Permissions($permission, true));
}
$group->group(
'/files',
function (RouteCollectorProxy $group) {
$group->get('/list', Controller\Api\Stations\Files\ListAction::class)
->setName('api:stations:files:list');
$group->get('/directories', Controller\Api\Stations\Files\ListDirectoriesAction::class)
->setName('api:stations:files:directories');
$group->put('/rename', Controller\Api\Stations\Files\RenameAction::class)
->setName('api:stations:files:rename');
$group->put('/batch', Controller\Api\Stations\Files\BatchAction::class)
->setName('api:stations:files:batch');
$group->post('/mkdir', Controller\Api\Stations\Files\MakeDirectoryAction::class)
->setName('api:stations:files:mkdir');
$group->get('/play/{id}', Controller\Api\Stations\Files\PlayAction::class)
->setName('api:stations:files:play');
$group->get('/download', Controller\Api\Stations\Files\DownloadAction::class)
->setName('api:stations:files:download');
$group->map(
['GET', 'POST'],
'/upload',
Controller\Api\Stations\Files\FlowUploadAction::class
)->setName('api:stations:files:upload');
}
)
->add(Middleware\Module\StationFiles::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->post(
'/mounts/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
)->setName('api:stations:mounts:new-intro')
->add(new Middleware\Permissions(Acl::STATION_MOUNTS, true));
$group->group(
'/mount/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'/intro',
Controller\Api\Stations\Mounts\Intro\GetIntroAction::class
)->setName('api:stations:mounts:intro');
$group->post(
'/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
);
$group->delete(
'/intro',
Controller\Api\Stations\Mounts\Intro\DeleteIntroAction::class
);
}
)->add(new Middleware\Permissions(Acl::STATION_MOUNTS, true));
$group->get(
'/playlists/schedule',
Controller\Api\Stations\PlaylistsController::class . ':scheduleAction'
)
->setName('api:stations:playlists:schedule')
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->group(
'/playlist/{id}',
function (RouteCollectorProxy $group) {
$group->put(
'/toggle',
Controller\Api\Stations\Playlists\ToggleAction::class
)->setName('api:stations:playlist:toggle');
$group->put(
'/reshuffle',
Controller\Api\Stations\Playlists\ReshuffleAction::class
)->setName('api:stations:playlist:reshuffle');
$group->get(
'/order',
Controller\Api\Stations\Playlists\GetOrderAction::class
)->setName('api:stations:playlist:order');
$group->put(
'/order',
Controller\Api\Stations\Playlists\PutOrderAction::class
);
$group->get(
'/queue',
Controller\Api\Stations\Playlists\GetQueueAction::class
)->setName('api:stations:playlist:queue');
$group->delete(
'/queue',
Controller\Api\Stations\Playlists\DeleteQueueAction::class
);
$group->post(
'/clone',
Controller\Api\Stations\Playlists\CloneAction::class
)->setName('api:stations:playlist:clone');
$group->post(
'/import',
Controller\Api\Stations\Playlists\ImportAction::class
)->setName('api:stations:playlist:import');
$group->get(
'/export[/{format}]',
Controller\Api\Stations\Playlists\ExportAction::class
)->setName('api:stations:playlist:export');
}
)->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->group(
'/reports',
function (RouteCollectorProxy $group) {
$group->group(
'/requests',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\Reports\RequestsController::class . ':listAction'
)->setName('api:stations:reports:requests');
$group->post(
'/clear',
Controller\Api\Stations\Reports\RequestsController::class . ':clearAction'
)->setName('api:stations:reports:requests:clear');
$group->delete(
'/{request_id}',
Controller\Api\Stations\Reports\RequestsController::class . ':deleteAction'
)->setName('api:stations:reports:requests:delete');
}
)->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get(
'/performance',
Controller\Api\Stations\Reports\PerformanceAction::class
)->setName('api:stations:reports:performance');
$group->get(
'/overview/charts',
Controller\Api\Stations\Reports\Overview\ChartsAction::class
)->setName('api:stations:reports:overview-charts');
$group->get(
'/overview/best-and-worst',
Controller\Api\Stations\Reports\Overview\BestAndWorstAction::class
)->setName('api:stations:reports:best-and-worst');
$group->get(
'/overview/most-played',
Controller\Api\Stations\Reports\Overview\MostPlayedAction::class
)->setName('api:stations:reports:most-played');
$group->get(
'/soundexchange',
Controller\Api\Stations\Reports\SoundExchangeAction::class
)->setName('api:stations:reports:soundexchange');
}
)->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->get(
'/streamers/schedule',
Controller\Api\Stations\StreamersController::class . ':scheduleAction'
)
->setName('api:stations:streamers:schedule')
->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->get(
'/streamers/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)
->setName('api:stations:streamers:broadcasts')
->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->group(
'/streamer/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)
->setName('api:stations:streamer:broadcasts');
$group->get(
'/broadcast/{broadcast_id}/download',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':downloadAction'
)
->setName('api:stations:streamer:broadcast:download');
$group->delete(
'/broadcast/{broadcast_id}',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':deleteAction'
)
->setName('api:stations:streamer:broadcast:delete');
}
)->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->get('/status', Controller\Api\Stations\ServicesController::class . ':statusAction')
->setName('api:stations:status')
->add(new Middleware\Permissions(Acl::STATION_VIEW, true));
$group->post('/backend/{do}', Controller\Api\Stations\ServicesController::class . ':backendAction')
->setName('api:stations:backend')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->post(
'/frontend/{do}',
Controller\Api\Stations\ServicesController::class . ':frontendAction'
)
->setName('api:stations:frontend')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->post('/restart', Controller\Api\Stations\ServicesController::class . ':restartAction')
->setName('api:stations:restart')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->group(
'/webhook/{id}',
function (RouteCollectorProxy $group) {
$group->put(
'/toggle',
Controller\Api\Stations\Webhooks\ToggleAction::class
)->setName('api:stations:webhook:toggle');
$group->put(
'/test',
Controller\Api\Stations\Webhooks\TestAction::class
)->setName('api:stations:webhook:test');
$group->get(
'/test-log/{path}',
Controller\Api\Stations\Webhooks\TestLogAction::class
)->setName('api:stations:webhook:test-log');
}
)->add(new Middleware\Permissions(Acl::STATION_WEB_HOOKS, true));
}
)->add(Middleware\RequireStation::class)
->add(Middleware\GetStation::class);
call_user_func(include(__DIR__ . '/api_admin.php'), $group);
call_user_func(include(__DIR__ . '/api_frontend.php'), $group);
call_user_func(include(__DIR__ . '/api_station.php'), $group);
}
)->add(Middleware\Module\Api::class);
};

165
config/routes/api_admin.php Normal file
View File

@ -0,0 +1,165 @@
<?php
use App\Acl;
use App\Controller;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Middleware;
use Slim\Routing\RouteCollectorProxy;
return static function (RouteCollectorProxy $group) {
$group->group(
'/admin',
function (RouteCollectorProxy $group) {
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/api-keys',
Controller\Api\Admin\ApiKeysController::class . ':listAction'
)->setName('api:admin:api-keys');
$group->get(
'/api-key/{id}',
Controller\Api\Admin\ApiKeysController::class . ':getAction'
)->setName('api:admin:api-key');
$group->delete(
'/api-key/{id}',
Controller\Api\Admin\ApiKeysController::class . ':deleteAction'
);
}
)->add(new Middleware\Permissions(Acl::GLOBAL_API_KEYS));
$group->get('/auditlog', Controller\Api\Admin\AuditLogAction::class)
->setName('api:admin:auditlog')
->add(new Middleware\Permissions(Acl::GLOBAL_LOGS));
$group->group(
'/backups',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Admin\Backups\GetAction::class)
->setName('api:admin:backups');
$group->post('/run', Controller\Api\Admin\Backups\RunAction::class)
->setName('api:admin:backups:run');
$group->get('/log/{path}', Controller\Api\Admin\Backups\GetLogAction::class)
->setName('api:admin:backups:log');
$group->get('/download/{path}', Controller\Api\Admin\Backups\DownloadAction::class)
->setName('api:admin:backups:download');
$group->delete('/delete/{path}', Controller\Api\Admin\Backups\DeleteAction::class)
->setName('api:admin:backups:delete');
}
)->add(new Middleware\Permissions(Acl::GLOBAL_BACKUPS));
$group->get('/permissions', Controller\Api\Admin\PermissionsController::class)
->add(new Middleware\Permissions(Acl::GLOBAL_ALL));
$group->map(
['GET', 'POST'],
'/relays',
function (ServerRequest $request, Response $response) {
return $response->withRedirect(
(string)$request->getRouter()->fromHere('api:internal:relays')
);
}
);
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/settings[/{group}]',
Controller\Api\Admin\SettingsController::class . ':listAction'
)->setName('api:admin:settings');
$group->put(
'/settings[/{group}]',
Controller\Api\Admin\SettingsController::class . ':updateAction'
);
$group->get(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\GetCustomAssetAction::class
)->setName('api:admin:custom_assets');
$group->post(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\PostCustomAssetAction::class
);
$group->delete(
'/custom_assets/{type}',
Controller\Api\Admin\CustomAssets\DeleteCustomAssetAction::class
);
$group->get(
'/geolite',
Controller\Api\Admin\GeoLite\GetAction::class
)->setName('api:admin:geolite');
$group->post(
'/geolite',
Controller\Api\Admin\GeoLite\PostAction::class
);
$group->get(
'/shoutcast',
Controller\Api\Admin\Shoutcast\GetAction::class
)->setName('api:admin:shoutcast');
$group->post(
'/shoutcast',
Controller\Api\Admin\Shoutcast\PostAction::class
);
}
)->add(new Middleware\Permissions(Acl::GLOBAL_SETTINGS));
$admin_api_endpoints = [
[
'custom_field',
'custom_fields',
Controller\Api\Admin\CustomFieldsController::class,
Acl::GLOBAL_CUSTOM_FIELDS,
],
['role', 'roles', Controller\Api\Admin\RolesController::class, Acl::GLOBAL_ALL],
['station', 'stations', Controller\Api\Admin\StationsController::class, Acl::GLOBAL_STATIONS],
['user', 'users', Controller\Api\Admin\UsersController::class, Acl::GLOBAL_ALL],
[
'storage_location',
'storage_locations',
Controller\Api\Admin\StorageLocationsController::class,
Acl::GLOBAL_STORAGE_LOCATIONS,
],
];
foreach ($admin_api_endpoints as [$singular, $plural, $class, $permission]) {
$group->group(
'',
function (RouteCollectorProxy $group) use ($singular, $plural, $class) {
$group->get('/' . $plural, $class . ':listAction')
->setName('api:admin:' . $plural);
$group->post('/' . $plural, $class . ':createAction');
$group->get('/' . $singular . '/{id}', $class . ':getAction')
->setName('api:admin:' . $singular);
$group->put('/' . $singular . '/{id}', $class . ':editAction');
$group->delete('/' . $singular . '/{id}', $class . ':deleteAction');
}
)->add(new Middleware\Permissions($permission));
}
$group->post('/station/{id}/clone', Controller\Api\Admin\Stations\CloneAction::class)
->setName('api:admin:station:clone')
->add(new Middleware\Permissions(Acl::GLOBAL_STATIONS));
$group->get(
'/stations/storage-locations',
Controller\Api\Admin\Stations\StorageLocationsAction::class
)->setName('api:admin:stations:storage-locations')
->add(new Middleware\Permissions(Acl::GLOBAL_STATIONS));
}
);
};

View File

@ -0,0 +1,66 @@
<?php
use App\Controller;
use App\Middleware;
use Slim\Routing\RouteCollectorProxy;
return static function (RouteCollectorProxy $group) {
$group->group(
'/frontend',
function (RouteCollectorProxy $group) {
$group->group(
'/account',
function (RouteCollectorProxy $group) {
$group->get('/me', Controller\Api\Frontend\Account\GetMeAction::class)
->setName('api:frontend:account:me');
$group->put('/me', Controller\Api\Frontend\Account\PutMeAction::class);
$group->put('/password', Controller\Api\Frontend\Account\PutPasswordAction::class)
->setName('api:frontend:account:password');
$group->get('/two-factor', Controller\Api\Frontend\Account\GetTwoFactorAction::class)
->setName('api:frontend:account:two-factor');
$group->put('/two-factor', Controller\Api\Frontend\Account\PutTwoFactorAction::class);
$group->delete('/two-factor', Controller\Api\Frontend\Account\DeleteTwoFactorAction::class);
$group->get(
'/api-keys',
Controller\Api\Frontend\Account\ApiKeysController::class . ':listAction'
)->setName('api:frontend:api-keys');
$group->post(
'/api-keys',
Controller\Api\Frontend\Account\ApiKeysController::class . ':createAction'
);
$group->get(
'/api-key/{id}',
Controller\Api\Frontend\Account\ApiKeysController::class . ':getAction'
)->setName('api:frontend:api-key');
$group->delete(
'/api-key/{id}',
Controller\Api\Frontend\Account\ApiKeysController::class . ':deleteAction'
);
}
);
$group->group(
'/dashboard',
function (RouteCollectorProxy $group) {
$group->get('/charts', Controller\Api\Frontend\Dashboard\ChartsAction::class)
->setName('api:frontend:dashboard:charts');
$group->get('/notifications', Controller\Api\Frontend\Dashboard\NotificationsAction::class)
->setName('api:frontend:dashboard:notifications');
$group->get('/stations', Controller\Api\Frontend\Dashboard\StationsAction::class)
->setName('api:frontend:dashboard:stations');
}
);
}
)->add(Middleware\RequireLogin::class);
};

View File

@ -0,0 +1,549 @@
<?php
use App\Acl;
use App\Controller;
use App\Middleware;
use Slim\Routing\RouteCollectorProxy;
return static function (RouteCollectorProxy $group) {
$group->group(
'/station/{station_id}',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\IndexController::class . ':indexAction')
->setName('api:stations:index')
->add(new Middleware\RateLimit('api', 5, 2));
$group->group(
'/automation',
function (RouteCollectorProxy $group) {
$group->get(
'/settings',
Controller\Api\Stations\Automation\GetSettingsAction::class
)->setName('api:stations:automation:settings');
$group->put(
'/settings',
Controller\Api\Stations\Automation\PutSettingsAction::class
);
$group->put(
'/run',
Controller\Api\Stations\Automation\RunAction::class
)->setName('api:stations:automation:run');
}
)->add(new Middleware\Permissions(Acl::STATION_AUTOMATION, true));
$group->get('/nowplaying', Controller\Api\NowPlayingAction::class . ':indexAction');
$group->map(
['GET', 'POST'],
'/nowplaying/update',
Controller\Api\Stations\UpdateMetadataAction::class
)
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get('/profile', Controller\Api\Stations\ProfileController::class)
->setName('api:stations:profile')
->add(new Middleware\Permissions(Acl::STATION_VIEW, true));
$group->get(
'/profile/edit',
Controller\Api\Stations\ProfileEditController::class . ':getProfileAction'
)->setName('api:stations:profile:edit')
->add(new Middleware\Permissions(Acl::STATION_PROFILE, true));
$group->put(
'/profile/edit',
Controller\Api\Stations\ProfileEditController::class . ':putProfileAction'
)->add(new Middleware\Permissions(Acl::STATION_PROFILE, true));
$group->get('/schedule', Controller\Api\Stations\ScheduleAction::class)
->setName('api:stations:schedule');
$group->get('/history', Controller\Api\Stations\HistoryController::class)
->setName('api:stations:history')
->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->group(
'/queue',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\QueueController::class . ':listAction')
->setName('api:stations:queue');
$group->post('/clear', Controller\Api\Stations\QueueController::class . ':clearAction')
->setName('api:stations:queue:clear');
$group->delete('/{id}', Controller\Api\Stations\QueueController::class . ':deleteAction')
->setName('api:stations:queue:record');
}
)->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get('/requests', Controller\Api\Stations\RequestsController::class . ':listAction')
->setName('api:requests:list');
$group->map(
['GET', 'POST'],
'/request/{media_id}',
Controller\Api\Stations\RequestsController::class . ':submitAction'
)
->setName('api:requests:submit')
->add(new Middleware\RateLimit('api', 5, 2));
$group->get('/ondemand', Controller\Api\Stations\OnDemand\ListAction::class)
->setName('api:stations:ondemand:list');
$group->get('/ondemand/download/{media_id}', Controller\Api\Stations\OnDemand\DownloadAction::class)
->setName('api:stations:ondemand:download')
->add(new Middleware\RateLimit('ondemand', 1, 2));
$group->get('/listeners', Controller\Api\Stations\ListenersAction::class)
->setName('api:listeners:index')
->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->get(
'/waveform/{media_id:[a-zA-Z0-9\-]+}.json',
Controller\Api\Stations\Waveform\GetWaveformAction::class
)
->setName('api:stations:media:waveform');
$group->get('/art/{media_id:[a-zA-Z0-9\-]+}.jpg', Controller\Api\Stations\Art\GetArtAction::class)
->setName('api:stations:media:art');
$group->get('/art/{media_id:[a-zA-Z0-9\-]+}', Controller\Api\Stations\Art\GetArtAction::class)
->setName('api:stations:media:art-internal');
$group->post('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\PostArtAction::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->delete('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\DeleteArtAction::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->group(
'/liquidsoap-config',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\LiquidsoapConfig\GetAction::class
)->setName('api:stations:liquidsoap-config');
$group->put(
'',
Controller\Api\Stations\LiquidsoapConfig\PutAction::class
);
}
)->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
// Public and private podcast pages
$group->group(
'/podcast/{podcast_id}',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\PodcastsController::class . ':getAction')
->setName('api:stations:podcast');
$group->get(
'/art',
Controller\Api\Stations\Podcasts\Art\GetArtAction::class
)->setName('api:stations:podcast:art');
$group->get(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':listAction'
)->setName('api:stations:podcast:episodes');
$group->group(
'/episode/{episode_id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':getAction'
)->setName('api:stations:podcast:episode');
$group->get(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\GetArtAction::class
)->setName('api:stations:podcast:episode:art');
$group->get(
'/download',
Controller\Api\Stations\Podcasts\Episodes\Media\GetMediaAction::class
)->setName('api:stations:podcast:episode:download');
}
);
}
)->add(Middleware\RequirePublishedPodcastEpisodeMiddleware::class);
// Private-only podcast pages
$group->group(
'/podcasts',
function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\PodcastsController::class . ':listAction')
->setName('api:stations:podcasts');
$group->post('', Controller\Api\Stations\PodcastsController::class . ':createAction');
$group->post('/art', Controller\Api\Stations\Podcasts\Art\PostArtAction::class)
->setName('api:stations:podcasts:new-art');
}
)->add(new Middleware\Permissions(Acl::STATION_PODCASTS, true));
$group->group(
'/podcast/{podcast_id}',
function (RouteCollectorProxy $group) {
$group->put('', Controller\Api\Stations\PodcastsController::class . ':editAction');
$group->delete('', Controller\Api\Stations\PodcastsController::class . ':deleteAction');
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Art\PostArtAction::class
)->setName('api:stations:podcast:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Art\DeleteArtAction::class
);
$group->post(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':createAction'
);
$group->post(
'/episodes/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episodes:new-art');
$group->post(
'/episodes/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episodes:new-media');
$group->group(
'/episode/{episode_id}',
function (RouteCollectorProxy $group) {
$group->put(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':deleteAction'
);
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episode:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\DeleteArtAction::class
);
$group->post(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episode:media-internal');
$group->delete(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\DeleteMediaAction::class
);
}
);
}
)->add(new Middleware\Permissions(Acl::STATION_PODCASTS, true));
$station_api_endpoints = [
['file', 'files', Controller\Api\Stations\FilesController::class, Acl::STATION_MEDIA],
['mount', 'mounts', Controller\Api\Stations\MountsController::class, Acl::STATION_MOUNTS],
[
'playlist',
'playlists',
Controller\Api\Stations\PlaylistsController::class,
Acl::STATION_MEDIA,
],
['remote', 'remotes', Controller\Api\Stations\RemotesController::class, Acl::STATION_REMOTES],
[
'sftp-user',
'sftp-users',
Controller\Api\Stations\SftpUsersController::class,
Acl::STATION_MEDIA,
],
[
'streamer',
'streamers',
Controller\Api\Stations\StreamersController::class,
Acl::STATION_STREAMERS,
],
[
'webhook',
'webhooks',
Controller\Api\Stations\WebhooksController::class,
Acl::STATION_WEB_HOOKS,
],
];
foreach ($station_api_endpoints as [$singular, $plural, $class, $permission]) {
$group->group(
'',
function (RouteCollectorProxy $group) use ($singular, $plural, $class) {
$group->get('/' . $plural, $class . ':listAction')
->setName('api:stations:' . $plural);
$group->post('/' . $plural, $class . ':createAction');
$group->get('/' . $singular . '/{id}', $class . ':getAction')
->setName('api:stations:' . $singular);
$group->put('/' . $singular . '/{id}', $class . ':editAction');
$group->delete('/' . $singular . '/{id}', $class . ':deleteAction');
}
)->add(new Middleware\Permissions($permission, true));
}
$group->group(
'/files',
function (RouteCollectorProxy $group) {
$group->get('/list', Controller\Api\Stations\Files\ListAction::class)
->setName('api:stations:files:list');
$group->get('/directories', Controller\Api\Stations\Files\ListDirectoriesAction::class)
->setName('api:stations:files:directories');
$group->put('/rename', Controller\Api\Stations\Files\RenameAction::class)
->setName('api:stations:files:rename');
$group->put('/batch', Controller\Api\Stations\Files\BatchAction::class)
->setName('api:stations:files:batch');
$group->post('/mkdir', Controller\Api\Stations\Files\MakeDirectoryAction::class)
->setName('api:stations:files:mkdir');
$group->get('/play/{id}', Controller\Api\Stations\Files\PlayAction::class)
->setName('api:stations:files:play');
$group->get('/download', Controller\Api\Stations\Files\DownloadAction::class)
->setName('api:stations:files:download');
$group->map(
['GET', 'POST'],
'/upload',
Controller\Api\Stations\Files\FlowUploadAction::class
)->setName('api:stations:files:upload');
}
)
->add(Middleware\Module\StationFiles::class)
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->post(
'/mounts/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
)->setName('api:stations:mounts:new-intro')
->add(new Middleware\Permissions(Acl::STATION_MOUNTS, true));
$group->group(
'/mount/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'/intro',
Controller\Api\Stations\Mounts\Intro\GetIntroAction::class
)->setName('api:stations:mounts:intro');
$group->post(
'/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
);
$group->delete(
'/intro',
Controller\Api\Stations\Mounts\Intro\DeleteIntroAction::class
);
}
)->add(new Middleware\Permissions(Acl::STATION_MOUNTS, true));
$group->get(
'/playlists/schedule',
Controller\Api\Stations\PlaylistsController::class . ':scheduleAction'
)
->setName('api:stations:playlists:schedule')
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->group(
'/playlist/{id}',
function (RouteCollectorProxy $group) {
$group->put(
'/toggle',
Controller\Api\Stations\Playlists\ToggleAction::class
)->setName('api:stations:playlist:toggle');
$group->put(
'/reshuffle',
Controller\Api\Stations\Playlists\ReshuffleAction::class
)->setName('api:stations:playlist:reshuffle');
$group->get(
'/order',
Controller\Api\Stations\Playlists\GetOrderAction::class
)->setName('api:stations:playlist:order');
$group->put(
'/order',
Controller\Api\Stations\Playlists\PutOrderAction::class
);
$group->get(
'/queue',
Controller\Api\Stations\Playlists\GetQueueAction::class
)->setName('api:stations:playlist:queue');
$group->delete(
'/queue',
Controller\Api\Stations\Playlists\DeleteQueueAction::class
);
$group->post(
'/clone',
Controller\Api\Stations\Playlists\CloneAction::class
)->setName('api:stations:playlist:clone');
$group->post(
'/import',
Controller\Api\Stations\Playlists\ImportAction::class
)->setName('api:stations:playlist:import');
$group->get(
'/export[/{format}]',
Controller\Api\Stations\Playlists\ExportAction::class
)->setName('api:stations:playlist:export');
}
)->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->group(
'/reports',
function (RouteCollectorProxy $group) {
$group->group(
'/requests',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\Reports\RequestsController::class . ':listAction'
)->setName('api:stations:reports:requests');
$group->post(
'/clear',
Controller\Api\Stations\Reports\RequestsController::class . ':clearAction'
)->setName('api:stations:reports:requests:clear');
$group->delete(
'/{request_id}',
Controller\Api\Stations\Reports\RequestsController::class . ':deleteAction'
)->setName('api:stations:reports:requests:delete');
}
)->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->get(
'/performance',
Controller\Api\Stations\Reports\PerformanceAction::class
)->setName('api:stations:reports:performance');
$group->get(
'/overview/charts',
Controller\Api\Stations\Reports\Overview\ChartsAction::class
)->setName('api:stations:reports:overview-charts');
$group->get(
'/overview/best-and-worst',
Controller\Api\Stations\Reports\Overview\BestAndWorstAction::class
)->setName('api:stations:reports:best-and-worst');
$group->get(
'/overview/most-played',
Controller\Api\Stations\Reports\Overview\MostPlayedAction::class
)->setName('api:stations:reports:most-played');
$group->get(
'/soundexchange',
Controller\Api\Stations\Reports\SoundExchangeAction::class
)->setName('api:stations:reports:soundexchange');
}
)->add(new Middleware\Permissions(Acl::STATION_REPORTS, true));
$group->get(
'/streamers/schedule',
Controller\Api\Stations\StreamersController::class . ':scheduleAction'
)
->setName('api:stations:streamers:schedule')
->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->get(
'/streamers/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)
->setName('api:stations:streamers:broadcasts')
->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->group(
'/streamer/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)
->setName('api:stations:streamer:broadcasts');
$group->get(
'/broadcast/{broadcast_id}/download',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':downloadAction'
)
->setName('api:stations:streamer:broadcast:download');
$group->delete(
'/broadcast/{broadcast_id}',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':deleteAction'
)
->setName('api:stations:streamer:broadcast:delete');
}
)->add(new Middleware\Permissions(Acl::STATION_STREAMERS, true));
$group->get('/status', Controller\Api\Stations\ServicesController::class . ':statusAction')
->setName('api:stations:status')
->add(new Middleware\Permissions(Acl::STATION_VIEW, true));
$group->post('/backend/{do}', Controller\Api\Stations\ServicesController::class . ':backendAction')
->setName('api:stations:backend')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->post(
'/frontend/{do}',
Controller\Api\Stations\ServicesController::class . ':frontendAction'
)
->setName('api:stations:frontend')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->post('/restart', Controller\Api\Stations\ServicesController::class . ':restartAction')
->setName('api:stations:restart')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));
$group->group(
'/webhook/{id}',
function (RouteCollectorProxy $group) {
$group->put(
'/toggle',
Controller\Api\Stations\Webhooks\ToggleAction::class
)->setName('api:stations:webhook:toggle');
$group->put(
'/test',
Controller\Api\Stations\Webhooks\TestAction::class
)->setName('api:stations:webhook:test');
$group->get(
'/test-log/{path}',
Controller\Api\Stations\Webhooks\TestLogAction::class
)->setName('api:stations:webhook:test-log');
}
)->add(new Middleware\Permissions(Acl::STATION_WEB_HOOKS, true));
}
)->add(Middleware\RequireStation::class)
->add(Middleware\GetStation::class);
};

View File

@ -30,7 +30,7 @@ return static function (RouteCollectorProxy $app) {
->setName('stations:files:index')
->add(new Middleware\Permissions(Acl::STATION_MEDIA, true));
$group->map(['GET', 'POST'], '/ls_config', Controller\Stations\EditLiquidsoapConfigAction::class)
$group->get('/ls_config', Controller\Stations\EditLiquidsoapConfigAction::class)
->setName('stations:util:ls_config')
->add(new Middleware\Permissions(Acl::STATION_BROADCASTING, true));

View File

@ -128,8 +128,6 @@ export default {
this.axios.get(this.apiUrl).then((resp) => {
this.populateForm(resp.data);
this.loading = false;
}).catch((error) => {
this.close();
});
},
populateForm(data) {
@ -181,7 +179,6 @@ export default {
this.$notifySuccess(this.$gettext('Changes saved.'));
this.relist();
});
}
}
}

View File

@ -0,0 +1,103 @@
<template>
<form class="form vue-form" @submit.prevent="submit">
<section class="card" role="region">
<div class="card-header bg-primary-dark">
<h2 class="card-title">
<translate key="lang_hdr">Edit Liquidsoap Configuration</translate>
</h2>
</div>
<b-overlay variant="card" :show="loading">
<div class="card-body">
<b-form-fieldset v-for="(row, index) in config" :key="index" class="mb-0">
<b-wrapped-form-group v-if="row.is_field" :field="$v.form[row.field_name]"
:id="'form_edit_'+row.field_name" input-type="textarea"
:input-attrs="{class: 'text-preformatted mb-3', spellcheck: 'false', style: 'height: 150px;'}">
</b-wrapped-form-group>
<b-form-markup v-else :id="'form_section_'+index">
<pre class="typography-body-1">{{ row.markup }}</pre>
</b-form-markup>
</b-form-fieldset>
<b-button size="lg" type="submit" variant="primary" :disabled="$v.form.$invalid">
<translate key="lang_btn_save_changes">Save Changes</translate>
</b-button>
</div>
</b-overlay>
</section>
</form>
</template>
<script>
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import BFormMarkup from "~/components/Form/BFormMarkup";
import {validationMixin} from "vuelidate";
import _ from "lodash";
import mergeExisting from "~/functions/mergeExisting";
export default {
name: 'StationsLiquidsoapConfig',
components: {BFormFieldset, BWrappedFormGroup, BFormMarkup},
props: {
settingsUrl: String,
config: Array,
sections: Array,
},
mixins: [
validationMixin
],
validations() {
let validations = {form: {}};
_.forEach(this.sections, (section) => {
validations.form[section] = {};
});
return validations;
},
data() {
return {
loading: true,
form: {},
}
},
mounted() {
this.relist();
},
methods: {
resetForm() {
let form = {};
_.forEach(this.sections, (section) => {
form[section] = null;
});
this.form = form;
},
relist() {
this.resetForm();
this.$v.form.$reset();
this.loading = true;
this.axios.get(this.settingsUrl).then((resp) => {
this.form = mergeExisting(this.form, resp.data);
this.loading = false;
});
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
return;
}
this.$wrapWithLoading(
this.axios({
method: 'PUT',
url: this.settingsUrl,
data: this.form
})
).then((resp) => {
this.$notifySuccess();
this.relist();
});
}
}
}
</script>

View File

@ -0,0 +1,7 @@
import initBase from '~/base.js';
import '~/vendor/bootstrapVue.js';
import LiquidsoapConfig from '~/components/Stations/LiquidsoapConfig.vue';
export default initBase(LiquidsoapConfig);

View File

@ -31,6 +31,7 @@ module.exports = {
SetupSettings: '~/pages/Setup/Settings.js',
SetupStation: '~/pages/Setup/Station.js',
StationsAutomation: '~/pages/Stations/Automation.js',
StationsLiquidsoapConfig: '~/pages/Stations/LiquidsoapConfig.js',
StationsMedia: '~/pages/Stations/Media.js',
StationsMounts: '~/pages/Stations/Mounts.js',
StationsPlaylists: '~/pages/Stations/Playlists.js',

View File

@ -15,7 +15,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class NowplayingController implements EventSubscriberInterface
class NowPlayingAction implements EventSubscriberInterface
{
public function __construct(
protected EntityManagerInterface $em,

View File

@ -9,7 +9,7 @@ use App\Http\Response;
use App\Http\ServerRequest;
use Psr\Http\Message\ResponseInterface;
class OpenApiController
class OpenApiAction
{
public function __invoke(
ServerRequest $request,

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api\Stations\LiquidsoapConfig;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Radio\Backend\Liquidsoap\ConfigWriter;
use Psr\Http\Message\ResponseInterface;
class GetAction
{
public function __invoke(
ServerRequest $request,
Response $response
): ResponseInterface {
$backendConfig = $request->getStation()->getBackendConfig();
$return = [];
foreach (ConfigWriter::getCustomConfigurationSections() as $field) {
$return[$field] = $backendConfig[$field] ?? null;
}
return $response->withJson($return);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api\Stations\LiquidsoapConfig;
use App\Doctrine\ReloadableEntityManagerInterface;
use App\Entity;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Radio\Backend\Liquidsoap\ConfigWriter;
use Psr\Http\Message\ResponseInterface;
class PutAction
{
public function __invoke(
ServerRequest $request,
Response $response,
ReloadableEntityManagerInterface $em
): ResponseInterface {
$body = (array)$request->getParsedBody();
$station = $em->refetch($request->getStation());
$backendConfig = $station->getBackendConfig();
foreach (ConfigWriter::getCustomConfigurationSections() as $field) {
if (isset($body[$field])) {
$backendConfig->set($field, $body[$field]);
}
}
$station->setBackendConfig($backendConfig);
$em->persist($station);
$em->flush();
return $response->withJson(Entity\Api\Status::updated());
}
}

View File

@ -11,7 +11,7 @@ use Psr\Http\Message\ResponseInterface;
use const ARRAY_FILTER_USE_KEY;
class UpdateMetadataController
class UpdateMetadataAction
{
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
{

View File

@ -5,13 +5,10 @@ declare(strict_types=1);
namespace App\Controller\Stations;
use App\Entity\Repository\SettingsRepository;
use App\Exception\AdvancedFeatureException;
use App\Exception\StationUnsupportedException;
use App\Form\Form;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Radio\Backend\Liquidsoap;
use App\Session\Flash;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Http\Message\ResponseInterface;
@ -24,102 +21,50 @@ class EditLiquidsoapConfigAction
SettingsRepository $settingsRepo
): ResponseInterface {
$station = $request->getStation();
$backend = $request->getStationBackend();
$settings = $settingsRepo->readSettings();
if (!$settings->getEnableAdvancedFeatures()) {
throw new AdvancedFeatureException();
}
if (!($backend instanceof Liquidsoap)) {
throw new StationUnsupportedException();
}
$configSections = Liquidsoap\ConfigWriter::getCustomConfigurationSections();
$config = $backend->getEditableConfiguration($station);
$tokens = Liquidsoap\ConfigWriter::getDividerString();
$formConfig = [
'method' => 'post',
'enctype' => 'multipart/form-data',
'groups' => [
'ls_config' => [
'elements' => [],
],
'submit_grp' => [
'elements' => [
'submit' => [
'submit',
[
'type' => 'submit',
'label' => __('Save Changes'),
'class' => 'btn btn-lg btn-primary',
],
],
],
],
],
];
$areas = [];
$tok = strtok($config, $tokens);
$i = 0;
while ($tok !== false) {
$tok = trim($tok);
$i++;
if (in_array($tok, $configSections, true)) {
$formConfig['groups']['ls_config']['elements'][$tok] = [
'textarea',
[
'belongsTo' => 'backend_config',
'class' => 'text-preformatted',
'spellcheck' => 'false',
],
$areas[] = [
'is_field' => true,
'field_name' => $tok,
];
} else {
$formConfig['groups']['ls_config']['elements']['config_section_' . $i] = [
'markup',
[
'markup' => '<pre class="typography-body-1">' . $tok . '</pre>',
],
$areas[] = [
'is_field' => false,
'markup' => $tok,
];
}
$tok = strtok($tokens);
}
$backendConfig = $station->getBackendConfig();
$form = new Form($formConfig, ['backend_config' => $backendConfig->toArray()]);
if ($form->isValid($request)) {
$data = $form->getValues();
foreach ($data['backend_config'] as $configKey => $configValue) {
$backendConfig[$configKey] = $configValue;
}
$station->setBackendConfig($backendConfig);
$em->persist($station);
$em->flush();
$request->getFlash()->addMessage(__('Changes saved.'), Flash::SUCCESS);
return $response->withRedirect($request->getUri()->getPath());
}
return $request->getView()->renderToResponse(
$response,
'system/form_page',
[
'form' => $form,
'render_mode' => 'edit',
'title' => __('Edit Liquidsoap Configuration'),
]
$router = $request->getRouter();
return $request->getView()->renderVuePage(
response: $response,
component: 'Vue_StationsLiquidsoapConfig',
id: 'station-liquidsoap-config',
title: __('Edit Liquidsoap Configuration'),
props: [
'settingsUrl' => (string)$router->fromHere('api:stations:liquidsoap-config'),
'config' => $areas,
'sections' => $configSections,
],
);
}
}