refactor: consolidate UpdateOS php files into a single class

This commit is contained in:
Zack Spear
2024-01-30 16:00:13 -08:00
committed by Zack Spear
parent ce3ba7d070
commit c33b4ef709
6 changed files with 279 additions and 265 deletions

View File

@@ -318,6 +318,7 @@ if [ -e /etc/rc.d/rc.unraid-api ]; then
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/include/ShowChanges.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/showchanges && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers1.php && [[ -f "$FILE-" ]] && mv -f "$FILE-" "$FILE"
@@ -411,6 +412,7 @@ FILE=/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php && [[ -f "$FILE" ]]
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/Downgrade.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/Update.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/MyServers.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/Registration.page && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"
FILE=/usr/local/emhttp/plugins/dynamix.my.servers/include/myservers1.php && [[ -f "$FILE" ]] && mv -f "$FILE" "$FILE-"

View File

@@ -0,0 +1,264 @@
<?php
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
/**
* Abstracting this code into a separate file allows us to use it in multiple places without duplicating code.
* 1. unraidcheck script can call this
* require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
* $unraidOsCheck = new UnraidOsCheck();
* $unraidOsCheck->checkForUpdate();
*
* 2. Unraid webgui web components can GET this file with action params to get updates, ignore updates, etc.
* - EX: Unraid webgui web components can check for updates via a GET request and receive a response with the json file directly
* - this is useful for the UPC to check for updates and display a model based on the value
* - `/plugins/dynamix.plugin.manager/scripts/unraidcheck.php?json=true`
* - note the json=true query param to receive a json response
*
* @param action {'check'|'removeAllIgnored'|'removeIgnoredVersion'|'ignoreVersion'} - the action to perform
* @param version {string} - the version to ignore or remove
* @param json {string} - if set to true, will return the json response from the external request
* @param altUrl {URL} - if set, will use this url instead of the default
*/
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
class UnraidOsCheck
{
private const BASE_RELEASES_URL = 'https://releases.unraid.net/os';
private const JSON_FILE_IGNORED = '/tmp/unraidcheck/ignored.json';
private const JSON_FILE_IGNORED_KEY = 'updateOsIgnoredReleases';
private const JSON_FILE_RESULT = '/tmp/unraidcheck/result.json';
private const PLG_PATH = '/var/log/plugins/unRAIDServer.plg';
public function __construct()
{
$getHasAction = $_GET !== null && !empty($_GET) && isset($_GET['action']);
if (!empty($_SERVER) && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET' && $getHasAction) {
$this->handleGetRequestWithActions();
}
}
private function handleGetRequestWithActions()
{
switch ($_GET['action']) {
case 'check':
$this->checkForUpdate();
break;
case 'removeAllIgnored':
$this->removeAllIgnored();
break;
case 'removeIgnoredVersion':
if (isset($_GET['version'])) {
$this->removeIgnoredVersion($_GET['version']);
}
break;
case 'ignoreVersion':
if (isset($_GET['version'])) {
$this->ignoreVersion($_GET['version']);
}
break;
default:
$this->respondWithError(400, "Unhandled action");
break;
}
}
public function getUnraidOSCheckResult()
{
if (file_exists(self::JSON_FILE_RESULT)) {
return $this->readJsonFile(self::JSON_FILE_RESULT);
}
}
public function getIgnoredReleases()
{
if (!file_exists(self::JSON_FILE_IGNORED)) {
return [];
}
$ignoredData = $this->readJsonFile(self::JSON_FILE_IGNORED);
if (is_array($ignoredData) && array_key_exists(self::JSON_FILE_IGNORED_KEY, $ignoredData)) {
return $ignoredData[self::JSON_FILE_IGNORED_KEY];
}
return [];
}
public function checkForUpdate()
{
// Multi-language support
if (!function_exists('_')) {
function _($text) {return $text;}
}
extract(parse_plugin_cfg('dynamix', true));
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
$params = [];
$params['branch'] = plugin('category', self::PLG_PATH, 'stable');
$params['current_version'] = plugin('version', self::PLG_PATH) ?: _var($var,'version');
if (_var($var,'regExp')) $params['update_exp'] = date('m-d-Y', _var($var,'regExp')*1);
$defaultUrl = self::BASE_RELEASES_URL;
// pass a param of altUrl to use the provided url instead of the default
$parsedAltUrl = (array_key_exists('altUrl',$_GET) && $_GET['altUrl']) ? $_GET['altUrl'] : null;
// if $parsedAltUrl pass to params
if ($parsedAltUrl) $params['altUrl'] = $parsedAltUrl;
$urlbase = $parsedAltUrl ?? $defaultUrl;
$url = $urlbase.'?'.http_build_query($params);
$response = "";
// use error handler to convert warnings from file_get_contents to errors so they can be captured
function warning_as_error($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler("warning_as_error");
try {
$response = file_get_contents($url);
} catch (Exception $e) {
$response = json_encode(array('error' => $e->getMessage()), JSON_PRETTY_PRINT);
}
restore_error_handler();
$responseMutated = json_decode($response, true);
if (!$responseMutated) {
$response = json_encode(array('error' => 'Invalid response from '.$urlbase), JSON_PRETTY_PRINT);
$responseMutated = json_decode($response, true);
}
// add params that were sent to $urlbase
$responseMutated['params'] = $params;
// store locally for UPC to access
$this->writeJsonFile(self::JSON_FILE_RESULT, $responseMutated);
// if we have a query param of json=true then just output the json
if (array_key_exists('json',$_GET) && $_GET['json']) {
header('Content-Type: application/json');
echo $response;
exit(0);
}
// send notification if a newer version is available and not ignored
$isNewerVersion = array_key_exists('isNewer',$responseMutated) ? $responseMutated['isNewer'] : false;
$isReleaseIgnored = in_array($responseMutated['version'], $this->getIgnoredReleases());
if ($responseMutated && $isNewerVersion && !$isReleaseIgnored) {
$output = _var($notify,'plugin');
$server = strtoupper(_var($var,'NAME','server'));
$newver = (array_key_exists('version',$responseMutated) && $responseMutated['version']) ? $responseMutated['version'] : 'unknown';
$script = '/usr/local/emhttp/webGui/scripts/notify';
exec("$script -e ".escapeshellarg("System - Unraid [$newver]")." -s ".escapeshellarg("Notice [$server] - Version update $newver")." -d ".escapeshellarg("A new version of Unraid is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
exit(0);
}
private function removeAllIgnored()
{
if (file_exists(self::JSON_FILE_IGNORED)) {
$this->deleteJsonFile(self::JSON_FILE_IGNORED);
$this->respondWithSuccess([]);
} else {
$this->respondWithError(400, "No JSON file found");
}
}
private function removeIgnoredVersion($removeVersion)
{
if ($this->isValidSemVerFormat($removeVersion)) {
if (file_exists(self::JSON_FILE_IGNORED)) {
$existingData = $this->readJsonFile(self::JSON_FILE_IGNORED);
if (isset($existingData[self::JSON_FILE_IGNORED_KEY])) {
$existingData[self::JSON_FILE_IGNORED_KEY] = array_diff($existingData[self::JSON_FILE_IGNORED_KEY], [$removeVersion]);
$this->writeJsonFile(self::JSON_FILE_IGNORED, $existingData);
$this->respondWithSuccess($existingData);
} else {
$this->respondWithError(400, "No versions to remove in the JSON file");
}
} else {
$this->respondWithError(400, "No JSON file found");
}
} else {
$this->respondWithError(400, "Invalid removeVersion format");
}
}
private function ignoreVersion($version)
{
if ($this->isValidSemVerFormat($version)) {
$newData = [$this::JSON_FILE_IGNORED_KEY => [$version]];
$existingData = file_exists(self::JSON_FILE_IGNORED) ? $this->readJsonFile(self::JSON_FILE_IGNORED) : [];
if (isset($existingData[self::JSON_FILE_IGNORED_KEY])) {
$existingData[self::JSON_FILE_IGNORED_KEY][] = $version;
} else {
$existingData[self::JSON_FILE_IGNORED_KEY] = [$version];
}
$this->writeJsonFile(self::JSON_FILE_IGNORED, $existingData);
$this->respondWithSuccess($existingData);
} else {
$this->respondWithError(400, "Invalid version format");
}
}
private function isValidSemVerFormat($version)
{
return preg_match('/^\d+\.\d+(\.\d+)?(-.+)?$/', $version);
}
private function readJsonFile($file)
{
return @json_decode(@file_get_contents($file), true) ?? [];
}
private function writeJsonFile($file, $data)
{
if (!is_dir(dirname($file))) { // prevents errors when directory doesn't exist
mkdir(dirname($file));
}
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT));
}
private function deleteJsonFile($file)
{
unlink($file);
}
private function respondWithError($statusCode, $message)
{
http_response_code($statusCode);
echo $message;
}
private function respondWithSuccess($data)
{
http_response_code(200);
header('Content-Type: application/json');
echo json_encode($data, JSON_PRETTY_PRINT);
}
}
// Instantiate and handle the request for GET requests with actions
$getHasAction = $_GET !== null && !empty($_GET) && isset($_GET['action']); // duplicated from above on purpose
if (!empty($_SERVER) && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET' && $getHasAction) {
new UnraidOsCheck();
}

View File

@@ -1,110 +0,0 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
/**
* Abstracting this code into a separate file allows us to use it in multiple places
* without duplicating code.
* 1. unraidcheck script can call this
* 2. Unraid webgui web components and request this file
* 3. Unraid webgui web components can request the json file directly
* - this is useful for the UPC to check for updates and display a model based on the value
* - `/plugins/dynamix.plugin.manager/scripts/unraidcheck.php?json=true`
* - note the json=true query param to receive a json response
*
* @param json {string} - if set to true, will return the json response from the external request
* @param altUrl {URL} - if set, will use this url instead of the default
*/
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
// Multi-language support
if (!function_exists('_')) {
function _($text) {return $text;}
}
extract(parse_plugin_cfg('dynamix', true));
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
$script = "$docroot/webGui/scripts/notify";
$server = strtoupper(_var($var,'NAME','server'));
$output = _var($notify,'plugin');
$plg = '/var/log/plugins/unRAIDServer.plg';
$params = [];
$params['branch'] = plugin('category', $plg, 'stable');
$params['current_version'] = plugin('version', $plg) ?: _var($var,'version');
if (_var($var,'regExp')) $params['update_exp'] = date('m-d-Y', _var($var,'regExp')*1);
$defaultUrl = 'https://releases.unraid.net/os';
// pass a param of altUrl to use the provided url instead of the default
$parsedAltUrl = (array_key_exists('altUrl',$_GET) && $_GET['altUrl']) ? $_GET['altUrl'] : false;
// if $parsedAltUrl pass to params
if ($parsedAltUrl) $params['altUrl'] = $parsedAltUrl;
$urlbase = $parsedAltUrl ?? $defaultUrl;
$url = $urlbase.'?'.http_build_query($params);
$response = "";
// use error handler to convert warnings from file_get_contents to errors so they can be captured
function warning_as_error($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler("warning_as_error");
try {
$response = file_get_contents($url);
} catch (Exception $e) {
$response = json_encode(array('error' => $e->getMessage()), JSON_PRETTY_PRINT);
}
restore_error_handler();
$json = json_decode($response, true);
if (!$json) {
$response = json_encode(array('error' => 'Invalid response from '.$urlbase), JSON_PRETTY_PRINT);
$json = json_decode($response, true);
}
// add params that were sent to $urlbase
$json['params'] = $params;
// store locally for UPC to access
$file = '/tmp/unraidcheck/result.json';
if (!is_dir(dirname($file))) mkdir(dirname($file));
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT));
// if we have a query param of json=true then just output the json
if (array_key_exists('json',$_GET) && $_GET['json']) {
header('Content-Type: application/json');
echo $response;
exit(0);
}
// before sending a notification, check to see if the user requested to ignore the version
$ignoredReleasesFile = '/tmp/unraidcheck/ignored.json';
$ignoredReleasesResult = [];
if (file_exists($ignoredReleasesFile)) {
$ignoredData = json_decode(file_get_contents($ignoredReleasesFile), true);
if (is_array($ignoredData) && array_key_exists('updateOsIgnoredReleases', $ignoredData)) {
$ignoredReleasesResult = $ignoredData['updateOsIgnoredReleases'];
}
}
// send notification if a newer version is available
$isNewerVersion = array_key_exists('isNewer',$json) ? $json['isNewer'] : false;
$isReleaseIgnored = in_array($json['version'], $ignoredReleasesResult);
if ($json && $isNewerVersion && !$isReleaseIgnored) {
$newver = (array_key_exists('version',$json) && $json['version']) ? $json['version'] : 'unknown';
exec("$script -e ".escapeshellarg("System - Unraid [$newver]")." -s ".escapeshellarg("Notice [$server] - Version update $newver")." -d ".escapeshellarg("A new version of Unraid is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
exit(0);
?>

View File

@@ -1,143 +0,0 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
/**
* @param removeAll {boolean} - if true, will remove all versions from the JSON file
* @param removeVersion {string} - the version of the OS release we want to remove
* @param version {string} - the version of the OS release we want to ignore
*/
$json_file_key = 'updateOsIgnoredReleases';
$json_file = '/tmp/unraidcheck/ignored.json';
function isValidSemVerFormat($version) {
return preg_match('/^\d+\.\d+(\.\d+)?(-.+)?$/',$version);
}
// Ensure that the request is a GET request
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Read the JSON data from the request body
// $json_data = file_get_contents('php://input');
$json_data = $_GET;
if (empty($json_data)) {
http_response_code(400); // Bad Request
echo "No JSON data found";
return;
}
$data = $json_data;
if ($data !== null) {
// Check if the "removeAll" key exists in the $data array
if (isset($data['removeAll']) && $data['removeAll'] === true) {
// Check if the JSON file exists
if (file_exists($json_file)) {
// Delete the JSON file
unlink($json_file);
// return empty array to represent no ignored versions
header('Content-Type: application/json');
echo json_encode([$json_file_key => []], JSON_PRETTY_PRINT);
} else {
http_response_code(400); // Bad Request
echo "No JSON file found";
}
}
// Check if the "removeVersion" key exists in the $data array
else if (isset($data['removeVersion'])) {
// Check if the "removeVersion" value is a valid PHP-standardized version number string
$remove_version = $data['removeVersion'];
if (isValidSemVerFormat($remove_version)) {
// Check if the JSON file exists
if (file_exists($json_file)) {
// If the file exists, read its content
$existing_data = json_decode(file_get_contents($json_file), true);
// Check if key exists
if (isset($existing_data[$json_file_key])) {
// Remove the specified version from the array
$existing_data[$json_file_key] = array_diff($existing_data[$json_file_key], [$remove_version]);
// Save the updated data to the JSON file
file_put_contents($json_file, json_encode($existing_data, JSON_PRETTY_PRINT));
http_response_code(200); // OK
header('Content-Type: application/json');
echo json_encode($existing_data, JSON_PRETTY_PRINT);
} else {
http_response_code(400); // Bad Request
echo "No versions to remove in the JSON file";
}
} else {
http_response_code(400); // Bad Request
echo "No JSON file found";
}
} else {
http_response_code(400); // Bad Request
echo "Invalid removeVersion format";
}
}
// Check if the "version" key exists in the $data array
else if (isset($data['version'])) {
// Check if the "version" value is a valid PHP-standardized version number string
$version = $data['version'];
if (isValidSemVerFormat($version)) {
// Prepare the new data structure
$new_data = [$json_file_key => [$version]];
// Check if the JSON file already exists
if (file_exists($json_file)) {
// If the file exists, read its content
$existing_data = json_decode(file_get_contents($json_file), true);
// Check if key already exists
if (isset($existing_data[$json_file_key])) {
// Append the new version to the existing array
$existing_data[$json_file_key][] = $version;
} else {
// If key doesn't exist, create it
$existing_data[$json_file_key] = [$version];
}
// Update the data to be saved
$new_data = $existing_data;
}
// Save the data to the JSON file
file_put_contents($json_file, json_encode($new_data, JSON_PRETTY_PRINT));
http_response_code(200); // OK
header('Content-Type: application/json');
echo json_encode($new_data, JSON_PRETTY_PRINT);
} else {
http_response_code(400); // Bad Request
echo "Invalid version format";
}
} else {
http_response_code(400); // Bad Request
echo "Invalid param data";
}
} else {
http_response_code(400); // Bad Request
echo "Error decoding JSON data";
}
} else {
// Handle non-GET requests
http_response_code(405); // Method Not Allowed
echo "Only GET requests are allowed";
}

View File

@@ -95,14 +95,18 @@ export const WebguiNotify = async (payload: NotifyPostParameters) => {
}
};
interface WebguiCheckForUpdatePayload {
json?: boolean;
interface WebguiUnraidCheckPayload {
action: 'check' | 'removeAllIgnored' | 'removeIgnoredVersion' | 'ignoreVersion';
altUrl?: string;
json?: boolean;
version?: string;
}
export const WebguiCheckForUpdate = async (): Promise<ServerUpdateOsResponse | unknown> => {
console.debug('[WebguiCheckForUpdate]');
try {
const params: WebguiCheckForUpdatePayload = {
const params: WebguiUnraidCheckPayload = {
action: 'check',
json: true,
};
// conditionally add altUrl if OS_RELEASES.toString() is not 'https://releases.unraid.net/os'
@@ -127,16 +131,11 @@ export const WebguiCheckForUpdate = async (): Promise<ServerUpdateOsResponse | u
}
};
export interface WebguiUpdateIgnorePayload {
removeAll?: boolean;
removeVersion?: string;
version?: string;
}
export const WebguiUpdateIgnore = async (payload: WebguiUpdateIgnorePayload): Promise<any | void> => {
export const WebguiUpdateIgnore = async (payload: WebguiUnraidCheckPayload): Promise<any | void> => {
console.debug('[WebguiUpdateIgnore] payload', payload);
try {
const response = await request
.url('/plugins/dynamix.plugin.manager/include/UnraidIgnore.php')
.url('/plugins/dynamix.plugin.manager/include/UnraidCheck.php')
.query(payload)
.get()
.json((json) => {

View File

@@ -970,6 +970,7 @@ export const useServerStore = defineStore('server', () => {
const updateOsIgnoreRelease = (release: string) => {
updateOsIgnoredReleases.value.push(release);
const response = WebguiUpdateIgnore({
action: 'ignoreVersion',
version: release,
});
console.debug('[updateOsIgnoreRelease] response', response);
@@ -979,7 +980,8 @@ export const useServerStore = defineStore('server', () => {
const updateOsRemoveIgnoredRelease = (release: string) => {
updateOsIgnoredReleases.value = updateOsIgnoredReleases.value.filter(r => r !== release);
const response = WebguiUpdateIgnore({
removeVersion: release,
action: 'removeIgnoredVersion',
version: release,
});
console.debug('[updateOsRemoveIgnoredRelease] response', response);
};
@@ -987,7 +989,7 @@ export const useServerStore = defineStore('server', () => {
const updateOsRemoveAllIgnoredReleases = () => {
updateOsIgnoredReleases.value = [];
const response = WebguiUpdateIgnore({
removeAll: true,
action: 'removeAllIgnored',
});
console.debug('[updateOsRemoveAllIgnoredReleases] response', response);
};