Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ If you need to auto add starr apps and 3rd party apps you can do that via the ap
}
```

The `template` variable is not required but if you do not use an existing template then the app will have no starr api access initially.
The `template` variable is not required but if you do not use an existing template then the app will have no starr api access initially.

SignalR/WebSocket access is separately disabled by default. Enable the SignalR option when configuring an app that needs an arr `/signalr` connection. The Radarr and Sonarr Bazarr templates enable this option automatically.

An example curl would be:

Expand Down
33 changes: 22 additions & 11 deletions root/app/www/public/ajax/starr.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,16 @@
</select>
</td>
</tr>
<tr>
<td>Endpoint template<br><span class="text-small">Automatically select the endpoints based on an app template</span></td>
<tr>
<td>Endpoint template<br><span class="text-small">Automatically select the endpoints based on an app template</span></td>
<td>
<select class="form-select" id="access-template" onchange="applyTemplateOptions()"><?= $templateOptions ?></select>
</td>
</tr>
</td>
</tr>
<tr>
<td>SignalR<br><span class="text-small">Allow this app to use the arr SignalR/WebSocket endpoint</span></td>
<td><input type="checkbox" class="form-check-input" id="access-signalr" <?= $existing['signalr'] ? 'checked' : '' ?>></td>
</tr>
<tr>
<td>
<?= ucfirst($app) ?> endpoints<br>
Expand Down Expand Up @@ -240,8 +244,9 @@
$fields['apikey'] = $_POST['apikey'];
$fields['starr_id'] = intval($_POST['starr_id']);
$fields['endpoints'] = json_encode($endpoints, JSON_UNESCAPED_SLASHES);
$fields['template'] = $_POST['template'];
$fields['redactions'] = $_POST['redactions'];
$fields['template'] = $_POST['template'];
$fields['redactions'] = $_POST['redactions'];
$fields['signalr'] = intval($_POST['signalr']);

if ($_POST['id'] != 99) {
$error = $proxyDb->updateApp($_POST['id'], $fields);
Expand Down Expand Up @@ -306,9 +311,11 @@
$templateFile = file_exists($app['template']) ? $app['template'] : str_replace('../', './', $app['template']);
$appTemplate = getFile($templateFile);

if ($appTemplate) {
$app['endpoints'] = json_encode($appTemplate);
$error = $proxyDb->updateApp($_POST['appId'], $app);
if ($appTemplate) {
$app['signalr'] = intval(!empty($appTemplate['signalr']));
unset($appTemplate['signalr']);
$app['endpoints'] = json_encode($appTemplate);
$error = $proxyDb->updateApp($_POST['appId'], $app);
}

break;
Expand All @@ -331,8 +338,12 @@
}

$endpoints = [];
foreach ($templateFile as $templateEndpoint => $methods) {
foreach ($methods as $method) {
foreach ($templateFile as $templateEndpoint => $methods) {
if (!is_array($methods)) {
continue;
}

foreach ($methods as $method) {
$endpoints[$templateEndpoint][$method][] = 'template';
}
}
Expand Down
7 changes: 5 additions & 2 deletions root/app/www/public/ajax/templates.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@
}

if ($_POST['m'] == 'saveTemplateStarrAccess') {
$existing = $proxyDb->getAppFromId($_POST['id'], $appsTable);
$endpoints = $existing['endpoints'] ? json_decode($existing['endpoints'], true) : [];
$existing = $proxyDb->getAppFromId($_POST['id'], $appsTable);
$endpoints = $existing['endpoints'] ? json_decode($existing['endpoints'], true) : [];
if ($existing['signalr']) {
$endpoints['signalr'] = true;
}
$name = strtolower(preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['name']));
file_put_contents(APP_USER_TEMPLATES_PATH . $app . '/' . $name . '.json', json_encode($endpoints, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
Expand Down
21 changes: 12 additions & 9 deletions root/app/www/public/api/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,18 @@
$starrApp = $starr->getAppFromStarrKey($request['apikey'], $starrsTable);

if (!$error) {
$scopeKey = generateApikey();
$scopeAccess = $request['template'] ? json_decode(file_get_contents('../templates/' . $request['starr'] . '/' . $request['template'] . '.json'), true) : [];

$fields = [
'name' => $request['name'],
'apikey' => $scopeKey,
'starr_id' => intval($starrApp['id']),
'endpoints' => json_encode($scopeAccess, JSON_UNESCAPED_SLASHES)
];
$scopeKey = generateApikey();
$scopeAccess = $request['template'] ? json_decode(file_get_contents('../templates/' . $request['starr'] . '/' . $request['template'] . '.json'), true) : [];
$signalr = !empty($scopeAccess['signalr']);
unset($scopeAccess['signalr']);

$fields = [
'name' => $request['name'],
'apikey' => $scopeKey,
'starr_id' => intval($starrApp['id']),
'endpoints' => json_encode($scopeAccess, JSON_UNESCAPED_SLASHES),
'signalr' => intval($signalr)
];
$error = $proxyDb->addApp($fields);

if (!$error) {
Expand Down
1 change: 1 addition & 0 deletions root/app/www/public/js/starr.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ function saveAppStarrAccess(app, id)
params += '&starr_id=' + $('#access-instance').val();
params += '&template=' + $('#access-template').val();
params += '&redactions=' + $('#access-redactions').val();
params += '&signalr=' + ($('#access-signalr').prop('checked') ? 1 : 0);

$.each($('[id^=endpoint-counter-]'), function() {
const counter = $(this).attr('id').replace('endpoint-counter-', '');
Expand Down
6 changes: 6 additions & 0 deletions root/app/www/public/js/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ function applyTemplateOptions()
const loopId = $(this).prop('id');

$.each(resultData, function(endpoint, methods) {
if (!Array.isArray(methods)) {
return;
}

if (loopEndpoint == endpoint && methods.includes(loopMethod)) {
$('#' + loopId).prop('checked', true);
}
});
});

$('#access-signalr').prop('checked', resultData.signalr === true);

toast('Templates', 'The selected template access has been applied', 'info');
}
});
Expand Down
23 changes: 23 additions & 0 deletions root/app/www/public/migrations/009_signalr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

//-- RESET THE LIST
$q = [];

//-- ALWAYS NEED TO BUMP THE MIGRATION ID
$q[] = "UPDATE " . SETTINGS_TABLE . "
SET value = '009'
WHERE name = 'migration'";

$q[] = "ALTER TABLE " . APPS_TABLE . " ADD signalr INTEGER NOT NULL DEFAULT 0";

foreach ($q as $query) {
logger(MIGRATION_LOG, ['text' => '<span class="text-success">[Q]</span> ' . preg_replace('!\s+!', ' ', $query)]);

$proxyDb->query($query);

if ($proxyDb->error() != 'not an error') {
logger(MIGRATION_LOG, ['text' => '<span class="text-info">[R]</span> ' . $proxyDb->error()]);
} else {
logger(MIGRATION_LOG, ['text' => '<span class="text-info">[R]</span> query applied!']);
}
}
3 changes: 2 additions & 1 deletion root/app/www/public/pages/home.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@
$appAccess = json_decode($app['endpoints'], true);

if (file_exists($templateFile)) {
$appTemplate = getFile($templateFile);
$appTemplate = getFile($templateFile);
unset($appTemplate['signalr']);

if (count($appAccess, COUNT_RECURSIVE) != count($appTemplate, COUNT_RECURSIVE)) {
foreach ($starrsTable as $starrApp) {
Expand Down
3 changes: 2 additions & 1 deletion root/app/www/public/pages/starr.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@

$templateFile = file_exists($accessApp['template']) ? $accessApp['template'] : str_replace('../', './', $accessApp['template']);
if (file_exists($templateFile)) {
$templateEndpoints = getFile($templateFile);
$templateEndpoints = getFile($templateFile);
unset($templateEndpoints['signalr']);
$template = '<span ' . (count($accessApp['endpoints'], COUNT_RECURSIVE) != count($templateEndpoints, COUNT_RECURSIVE) ? 'class="text-warning" title="Template does not match, click to fix that" style="cursor: pointer;" onclick="viewAppEndpointDiff(' . $accessApp['id'] . ')"' : '') . '>Template: ' . count($templateEndpoints, COUNT_RECURSIVE) . ' endpoint' . (count($templateEndpoints, COUNT_RECURSIVE) == 1 ? '' : 's') . '</span>';
}
?>
Expand Down
35 changes: 35 additions & 0 deletions root/app/www/public/signalr/auth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

if (!defined('ABSOLUTE_PATH')) {
define('ABSOLUTE_PATH', dirname(__DIR__) . '/');
}

require ABSOLUTE_PATH . 'loader.php';

$apikey = $_GET['access_token'] ?: $_GET['apikey'] ?: $_SERVER['HTTP_X_API_KEY'];
$proxiedApp = $starr->getAppFromProxiedKey($apikey);

if (!$apikey || !$proxiedApp['starrApp'] || !$proxiedApp['starrAppDetails'] || !$proxiedApp['proxiedAppDetails']) {
http_response_code(401);
exit();
}

if (!$proxiedApp['proxiedAppDetails']['signalr']) {
http_response_code(403);
exit();
}

$backendUrl = rtrim($proxiedApp['starrAppDetails']['url'], '/');
$backendParts = parse_url($backendUrl);
if (!filter_var($backendUrl, FILTER_VALIDATE_URL) || !in_array(strtolower($backendParts['scheme']), ['http', 'https'])) {
http_response_code(502);
exit();
}

header('X-Starr-Backend-Url: ' . $backendUrl);
header('X-Starr-Api-Key: ' . $proxiedApp['starrAppDetails']['apikey']);
$proxyArgs = $_GET;
unset($proxyArgs['access_token'], $proxyArgs['apikey']);
$proxyArgs = ['access_token' => $proxiedApp['starrAppDetails']['apikey']] + $proxyArgs;
header('X-Starr-Proxy-Args: ' . http_build_query($proxyArgs));
http_response_code(204);
1 change: 1 addition & 0 deletions root/app/www/public/templates/radarr/bazarr.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"signalr": true,
"/api/v3/command": [
"post"
],
Expand Down
1 change: 1 addition & 0 deletions root/app/www/public/templates/sonarr/bazarr.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"signalr": true,
"/api/v3/command": [
"post"
],
Expand Down
26 changes: 26 additions & 0 deletions root/etc/nginx/template.conf
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,32 @@ http {
fastcgi_param QUERY_STRING endpoint=/api/$2&$args;
fastcgi_index index.php;
}

# SignalR routing must stay at the NGINX layer because PHP cURL cannot proxy WebSockets.
location = /signalr-auth {
internal;
alias $root/;
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $root/signalr/auth.php;
fastcgi_param QUERY_STRING $args;
fastcgi_index index.php;
}

location ~ ^(.*)/signalr/(.*)$ {
alias $root/;
set $signalr_path $2;
auth_request /signalr-auth;
auth_request_set $signalr_backend_url $upstream_http_x_starr_backend_url;
auth_request_set $signalr_real_key $upstream_http_x_starr_api_key;
auth_request_set $signalr_proxy_args $upstream_http_x_starr_proxy_args;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Api-Key $signalr_real_key;
proxy_pass $signalr_backend_url/signalr/$signalr_path?$signalr_proxy_args;
proxy_read_timeout 300s;
}
location ~ ^(.*)/feed/(.*)$ {
alias $root/;
fastcgi_pass 127.0.0.1:9000;
Expand Down