feat: enhance ServerState class for update checks and license key extensions

- Added functionality to the `ServerState` class to check for updates and new license keys when rendering specific pages like `/Tools/Registration`, `/Tools/Update`, and `/Tools/Downgrade`.
- Introduced a new method `shouldCheckForUpdates()` to determine if an update check is necessary based on the current path.
- Updated relevant pages to reflect these changes, ensuring a more seamless user experience during registration and updates.
This commit is contained in:
Zack Spear
2025-03-19 18:16:56 -07:00
parent b700278ba4
commit df1e1f0e34
6 changed files with 106 additions and 34 deletions
@@ -14,9 +14,9 @@ Tag="pencil"
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
$replaceKey = new ReplaceKey();
$replaceKey->check();
/**
* @note ServerState class constructor will check for updates and new license key, bypassing the check window period, when rendering /Tools/Registration
*/
?>
<unraid-i18n-host>
<unraid-registration></unraid-registration>
@@ -35,6 +35,9 @@ class ServerState
{
protected $webguiGlobals;
private const VAR_INI_FILE = '/var/local/emhttp/var.ini';
private const NGINX_INI_FILE = '/var/local/emhttp/nginx.ini';
private $var;
private $apiKey = '';
private $apiVersion = '';
@@ -54,6 +57,7 @@ class ServerState
"nokeyserver" => 'NO_KEY_SERVER',
"withdrawn" => 'WITHDRAWN',
];
/**
* SSO Sub IDs from the my servers config file.
*/
@@ -81,20 +85,43 @@ class ServerState
public $activationCodeData = [];
public $state = 'UNKNOWN';
public $pathsForUpdateOsCheck = [
[
'path' => '/Tools/Registration',
'forceReplaceKeyCheck' => true,
],
[
'path' => '/Tools/Update',
'forceReplaceKeyCheck' => false,
],
[
'path' => '/Tools/Downgrade',
'forceReplaceKeyCheck' => false,
],
];
/**
* Constructor to initialize class properties and gather server information.
*/
public function __construct()
{
$this->updateOsCheck = new UnraidOsCheck();
/**
* This is positioned here to ensure that if during the UpdateOSCheck, which includes a ReplaceKeyCheck,
* that we get the latest var.ini values before anything else attempts to use them throughout the class.
*/
$updateCheck = $this->shouldCheckForUpdates();
if ($updateCheck) {
$this->updateOsCheck->checkForUpdate($updateCheck['forceReplaceKeyCheck']);
}
/**
* @note necessary evil until full webgui is class based.
* @see - getWebguiGlobal() for usage
* */
global $webguiGlobals;
$this->webguiGlobals = &$webguiGlobals;
// echo "<pre>" . json_encode($this->webguiGlobals, JSON_PRETTY_PRINT) . "</pre>";
$this->var = $webguiGlobals['var'];
$this->var = (array)@parse_ini_file(self::VAR_INI_FILE);
// If we're on a patch, we need to use the combinedVersion to check for updates
if (file_exists('/tmp/Patcher/patches.json')) {
@@ -102,7 +129,7 @@ class ServerState
$this->var['version'] = $patchJson['combinedVersion'] ?? $this->var['version'];
}
$this->nginxCfg = @parse_ini_file('/var/local/emhttp/nginx.ini') ?? [];
$this->nginxCfg = @parse_ini_file(self::NGINX_INI_FILE);
$this->state = strtoupper(empty($this->var['regCheck']) ? $this->var['regTy'] : $this->var['regCheck']);
$this->osVersion = $this->var['version'];
@@ -119,7 +146,6 @@ class ServerState
$this->keyfileBase64UrlSafe = str_replace(['+', '/', '='], ['-', '_', ''], trim($this->keyfileBase64));
}
$this->updateOsCheck = new UnraidOsCheck();
$this->updateOsIgnoredReleases = $this->updateOsCheck->getIgnoredReleases();
$this->updateOsNotificationsEnabled = !empty(@$this->getWebguiGlobal('notify', 'unraidos'));
$this->updateOsResponse = $this->updateOsCheck->getUnraidOSCheckResult();
@@ -268,6 +294,19 @@ class ServerState
$this->activationCodeData = $data;
}
private function shouldCheckForUpdates(): ?array
{
$currentPath = $_SERVER['REQUEST_URI'];
foreach ($this->pathsForUpdateOsCheck as $pathConfig) {
if (strpos($currentPath, $pathConfig['path']) !== false) {
return $pathConfig;
}
}
return null;
}
/**
* Retrieve the server information as an associative array
*
@@ -15,6 +15,7 @@ Tag="upload"
*/
/**
* @note icon-update is rotated via CSS in myservers1.php
* @note ServerState class constructor will check for updates and new license key when rendering /Tools/Registration
*/
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
@@ -13,11 +13,11 @@ Tag="upload"
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
/**
* @note ServerState class constructor will check for updates and new license key when rendering /Tools/Update
*/
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
$rebootDetails = new RebootDetails();
$replaceKey = new ReplaceKey();
$replaceKey->check();
?>
<script>
function cleanUpFlashBackup(zip) {
@@ -29,6 +29,7 @@
*/
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/webGui/include/ReplaceKey.php";
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
class UnraidOsCheck
@@ -38,6 +39,7 @@ class UnraidOsCheck
private const JSON_FILE_IGNORED_KEY = 'updateOsIgnoredReleases';
private const JSON_FILE_RESULT = '/tmp/unraidcheck/result.json';
private const PLG_PATH = '/usr/local/emhttp/plugins/unRAIDServer/unRAIDServer.plg';
private const VAR_INI_FILE = '/var/local/emhttp/var.ini';
public function __construct()
{
@@ -53,7 +55,8 @@ class UnraidOsCheck
{
switch ($_GET['action']) {
case 'check':
$this->checkForUpdate();
$forceReplaceKeyCheck = (isset($_GET['forceReplaceKeyCheck'])) ? true : false;
$this->checkForUpdate($forceReplaceKeyCheck);
break;
case 'removeAllIgnored':
@@ -100,30 +103,51 @@ class UnraidOsCheck
return [];
}
/** @todo clean up this method to be more extensible */
public function checkForUpdate()
/**
* Check for Unraid OS updates and new license keys
*
* @param bool $forceReplaceKeyCheck Force check for license key replacement regardless of expiry window
* @return ?bool Returns:
* - true: Successfully checked for updates with no errors
* - false: Error occurred during update check
* - null: No update check was performed (e.g. invalid params)
* Note: If $_GET['json'] is set, outputs JSON response and exits with code 0 instead of returning
*/
public function checkForUpdate(bool $forceReplaceKeyCheck = false): ?bool
{
// Multi-language support
if (!function_exists('_')) {
function _($text) {return $text;}
$var = (array)@parse_ini_file(self::VAR_INI_FILE);
$initialRegExp = _var($var, 'regExp');
// checking for a new license key created via auto-extension
$replaceKey = new ReplaceKey();
if ($replaceKey->check($forceReplaceKeyCheck)) {
// if we have a new key, we need to wait for emhttp to update var.ini with the new regExp value
$startTime = time();
$timeout = 30; // 30 seconds
while (time() - $startTime < $timeout) {
$currentVar = (array)@parse_ini_file(self::VAR_INI_FILE);
$currentRegExp = _var($currentVar, 'regExp');
// Handle cases where either value might be undefined or different
if ((!$initialRegExp && $currentRegExp) || ($initialRegExp && !$currentRegExp) ||
($initialRegExp && $currentRegExp && $currentRegExp !== $initialRegExp)) {
$var = $currentVar;
break;
}
usleep(250000); // Sleep for 0.25 seconds between checks
}
if (time() - $startTime >= $timeout) { // if we timeout, use the current value
$var = (array)@parse_ini_file(self::VAR_INI_FILE);
}
}
// this command will set the $notify array
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');
// Get current version from patches.json if it exists, otherwise fall back to plugin version or var.ini
$patcherVersion = null;
if (file_exists('/tmp/Patcher/patches.json')) {
$patcherData = @json_decode(file_get_contents('/tmp/Patcher/patches.json'), true);
$patcherVersion = $patcherData['combinedVersion'] ?? null;
}
$params['current_version'] = $patcherVersion ?: plugin('version', self::PLG_PATH) ?: _var($var, 'version');
if (_var($var,'regExp')) $params['update_exp'] = date('Y-m-d', _var($var,'regExp')*1);
$params['current_version'] = plugin('version', self::PLG_PATH) ?: _var($var, 'version');
if (_var($var, 'regExp')) $params['update_exp'] = date('Y-m-d', _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;
@@ -161,8 +185,8 @@ class UnraidOsCheck
$isReleaseIgnored = array_key_exists('version',$responseMutated) ? in_array($responseMutated['version'], $this->getIgnoredReleases()) : false;
if ($responseMutated && $isNewerVersion && !$isReleaseIgnored) {
$output = _var($notify,'plugin');
$server = strtoupper(_var($var,'NAME','server'));
$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';
$event = "System - Unraid [$newver]";
@@ -171,7 +195,7 @@ class UnraidOsCheck
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
exit(0);
return !array_key_exists('error', $responseMutated);
}
private function removeAllIgnored()
@@ -9,11 +9,19 @@
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Usage:
* unraidcheck - Normal check for updates
* unraidcheck --force-replace-key-check - Force check for new license key before update check
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
$forceReplaceKeyCheck = in_array('--force-replace-key-check', $argv);
$unraidOsCheck = new UnraidOsCheck();
$unraidOsCheck->checkForUpdate();
$unraidOsCheckResult = $unraidOsCheck->checkForUpdate($forceReplaceKeyCheck);
exit($unraidOsCheckResult ? 0 : 1);