feat: UnraidCheckExec for Check OS Updates via UPC dropdown (#1265)

- Added `UnraidCheckExec.php` to separate concerns between UnraidCheck
and ReplaceKey, allowing for JSON responses.
- Updated `unraidcheck` script to parse query strings for compatibility
with the new class.
- Modified `webgui.ts` to call `UnraidCheckExec.php` instead of
`UnraidCheck.php` for update checks.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Updated the plugin installation process to ensure critical files
remain protected during updates.
- Introduced a dedicated update check component that now returns results
in a JSON format.
- Enhanced the web interface’s update check functionality with
streamlined request parameters.

- **Refactor**
- Separated update checking responsibilities for improved logic clarity
and overall reliability.
- Updated the interface for the update check payload to enhance
parameter handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Zack Spear
2025-03-21 14:05:17 -07:00
committed by GitHub
parent bf81a63f8e
commit 3a20930ead
4 changed files with 80 additions and 3 deletions

View File

@@ -392,6 +392,7 @@ if [ -f /tmp/restore-files-dynamix-unraid-net ]; then
"/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/showchanges"
"/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck"
"/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php"
"/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php"
"/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page"
"/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page"
"/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page"
@@ -511,6 +512,7 @@ preserveFilesDirs=(
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page:preventDowngrade"
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck:preventDowngrade"
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php:preventDowngrade"
"move:/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php:preventDowngrade"
"move:/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page:skip"
"move:/usr/local/emhttp/plugins/dynamix.my.servers/Connect.page:skip"
"move:/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page:preventDowngrade"

View File

@@ -0,0 +1,68 @@
<?php
/**
* This file exists to maintain separation of concerns between UnraidCheck and ReplaceKey.
* Instead of combining both classes directly, we utilize the unraidcheck script which already
* handles both operations in a simplified manner.
*
* It's called via the WebguiCheckForUpdate function in composables/services/webgui.ts of the web components.
* Handles WebguiUnraidCheckExecPayload interface parameters:
* - altUrl?: string
* - json?: boolean
*/
class UnraidCheckExec
{
private const SCRIPT_PATH = '/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck';
private const ALLOWED_DOMAIN = 'releases.unraid.net';
private function setupEnvironment(): void
{
header('Content-Type: application/json');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('Content-Security-Policy: default-src \'none\'');
$params = [
'json' => 'true',
];
if (isset($_GET['altUrl'])) {
$url = filter_var($_GET['altUrl'], FILTER_VALIDATE_URL);
if ($url !== false) {
$host = parse_url($url, PHP_URL_HOST);
$scheme = parse_url($url, PHP_URL_SCHEME);
if ($host && $scheme === 'https' && (
$host === self::ALLOWED_DOMAIN ||
str_ends_with($host, '.' . self::ALLOWED_DOMAIN)
)) {
$params['altUrl'] = $url;
}
}
}
putenv('QUERY_STRING=' . http_build_query($params));
}
public function execute(): string
{
// Validate script with all necessary permissions
if (!is_file(self::SCRIPT_PATH) ||
!is_readable(self::SCRIPT_PATH) ||
!is_executable(self::SCRIPT_PATH)) {
throw new RuntimeException('Script not found or not executable');
}
$this->setupEnvironment();
$output = [];
$command = escapeshellcmd(self::SCRIPT_PATH);
if (exec($command, $output) === false) {
throw new RuntimeException('Script execution failed');
}
return implode("\n", $output);
}
}
// Usage
$checker = new UnraidCheckExec();
echo $checker->execute();

View File

@@ -18,6 +18,9 @@ require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
$replaceKey = new ReplaceKey();
$replaceKey->check();
// utilized by UnraidCheckExec.php to have UnraidCheck.php return a json response when this script is called directly
parse_str(getenv('QUERY_STRING') ?? '', $_GET);
require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
$unraidOsCheck = new UnraidOsCheck();
$unraidOsCheck->checkForUpdate();

View File

@@ -92,6 +92,11 @@ interface WebguiUnraidCheckPayload {
version?: string;
}
interface WebguiUnraidCheckExecPayload {
altUrl?: string;
json?: boolean;
}
interface WebguiUnraidCheckIgnoreResponse {
updateOsIgnoredReleases: string[];
}
@@ -99,8 +104,7 @@ interface WebguiUnraidCheckIgnoreResponse {
export const WebguiCheckForUpdate = async (): Promise<ServerUpdateOsResponse | unknown> => {
console.debug('[WebguiCheckForUpdate]');
try {
const params: WebguiUnraidCheckPayload = {
action: 'check',
const params: WebguiUnraidCheckExecPayload = {
json: true,
};
// conditionally add altUrl if OS_RELEASES.toString() is not 'https://releases.unraid.net/os'
@@ -108,7 +112,7 @@ export const WebguiCheckForUpdate = async (): Promise<ServerUpdateOsResponse | u
params.altUrl = OS_RELEASES.toString();
}
const response = await request
.url('/plugins/dynamix.plugin.manager/include/UnraidCheck.php') // @todo replace with /scripts/unraidcheck
.url('/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php')
.query(params)
.get()
.json((json) => {