From c33b4ef7098782ea10323a72c81f638cbcf4fac4 Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Tue, 30 Jan 2024 16:00:13 -0800 Subject: [PATCH] refactor: consolidate UpdateOS php files into a single class --- plugin/plugins/dynamix.unraid.net.plg | 2 + .../include/UnraidCheck.php | 264 ++++++++++++++++++ .../includes/UnraidCheck.php | 110 -------- .../includes/UnraidIgnore.php | 143 ---------- web/composables/services/webgui.ts | 19 +- web/store/server.ts | 6 +- 6 files changed, 279 insertions(+), 265 deletions(-) create mode 100644 plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php delete mode 100644 plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidCheck.php delete mode 100644 plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidIgnore.php diff --git a/plugin/plugins/dynamix.unraid.net.plg b/plugin/plugins/dynamix.unraid.net.plg index ef720a175..07f787876 100755 --- a/plugin/plugins/dynamix.unraid.net.plg +++ b/plugin/plugins/dynamix.unraid.net.plg @@ -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-" diff --git a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php new file mode 100644 index 000000000..8ef2c9c3b --- /dev/null +++ b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php @@ -0,0 +1,264 @@ +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(); +} diff --git a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidCheck.php b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidCheck.php deleted file mode 100644 index 24ad49ee7..000000000 --- a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidCheck.php +++ /dev/null @@ -1,110 +0,0 @@ - - $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); -?> diff --git a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidIgnore.php b/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidIgnore.php deleted file mode 100644 index 055bd118d..000000000 --- a/plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.plugin.manager/includes/UnraidIgnore.php +++ /dev/null @@ -1,143 +0,0 @@ - - []], 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"; -} diff --git a/web/composables/services/webgui.ts b/web/composables/services/webgui.ts index b6f110ca0..554b9d389 100644 --- a/web/composables/services/webgui.ts +++ b/web/composables/services/webgui.ts @@ -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 => { 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 => { +export const WebguiUpdateIgnore = async (payload: WebguiUnraidCheckPayload): Promise => { 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) => { diff --git a/web/store/server.ts b/web/store/server.ts index 29f7b5803..fd28fb183 100644 --- a/web/store/server.ts +++ b/web/store/server.ts @@ -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); };