Merge pull request #2077 from zackspear/feat/replace-key-improvements

feat: replace key improvements
This commit is contained in:
tom mortensen
2025-03-21 00:24:53 -07:00
committed by GitHub
7 changed files with 125 additions and 25 deletions

View File

@@ -14,7 +14,10 @@ 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(true);
?>
<unraid-i18n-host>
<unraid-registration></unraid-registration>
</unraid-i18n-host>
</unraid-i18n-host>

View File

@@ -16,11 +16,12 @@ Tag="upload"
/**
* @note icon-update is rotated via CSS in myservers1.php
*/
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
$replaceKey = new ReplaceKey();
$replaceKey->check();
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
// Create an instance of the RebootDetails class
$rebootDetails = new RebootDetails();
// Get the current reboot details if there are any
$rebootDetails->setPrevious();
$serverNameEscaped = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));

View File

@@ -13,6 +13,10 @@ Tag="upload"
* 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();
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
$rebootDetails = new RebootDetails();
?>

View File

@@ -0,0 +1,42 @@
<?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 function setupEnvironment(): void
{
header('Content-Type: application/json');
$params = [
'json' => 'true',
];
// allows the web components to determine the OS_RELEASES url
if (isset($_GET['altUrl']) && filter_var($_GET['altUrl'], FILTER_VALIDATE_URL)) {
$params['altUrl'] = $_GET['altUrl'];
}
// pass the params to the unraidcheck script for usage in UnraidCheck.php
putenv('QUERY_STRING=' . http_build_query($params));
}
public function execute(): string
{
$this->setupEnvironment();
$output = [];
exec(self::SCRIPT_PATH, $output);
return implode("\n", $output);
}
}
// Usage
$checker = new UnraidCheckExec();
echo $checker->execute();

View File

@@ -13,7 +13,13 @@
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php";
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();
$unraidOsCheck->checkForUpdate();

View File

@@ -32,20 +32,20 @@ class KeyInstaller
* @param int $httpcode https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
* @param string|array $result - strings are assumed to be encoded JSON. Arrays will be encoded to JSON.
*/
private function responseComplete($httpcode, $result)
private function responseComplete($httpcode, $result): string
{
$mutatedResult = is_array($result) ? json_encode($result) : $result;
if ($this->isGetRequest && $this->getHasUrlParam) { // return JSON to the caller
header('Content-Type: application/json');
http_response_code($httpcode);
exit((string)$mutatedResult);
header('Content-Type: application/json');
http_response_code($httpcode);
exit((string)$mutatedResult);
} else { // return the result to the caller
return $mutatedResult;
return $mutatedResult;
}
}
public function installKey($keyUrl = null)
public function installKey($keyUrl = null): string
{
$url = unscript($keyUrl ?? _var($_GET, 'url'));
$host = parse_url($url)['host'] ?? '';
@@ -61,9 +61,12 @@ class KeyInstaller
if ($returnVar === 0) {
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
if (_var($var, 'mdState') == "STARTED") {
return $this->responseComplete(200, ['status' => _('Please Stop array to complete key installation')], _('success') . ', ' . _('Please Stop array to complete key installation'));
return $this->responseComplete(200, [
'status' => 'success',
'message' => _('Please Stop array to complete key installation'),
]);
} else {
return $this->responseComplete(200, ['status' => ''], _('success'));
return $this->responseComplete(200, ['status' => 'success']);
}
} else {
@unlink(escapeshellarg("/boot/config/$keyFile"));
@@ -81,4 +84,4 @@ $getHasUrlParam = $_GET !== null && !empty($_GET) && isset($_GET['url']);
if ($isGetRequest && $getHasUrlParam) {
$keyInstaller = new KeyInstaller();
$keyInstaller->installKey();
}
}

View File

@@ -1,5 +1,4 @@
<?php
$webguiGlobals = $GLOBALS;
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
class ReplaceKey
@@ -144,31 +143,71 @@ class ReplaceKey
require_once "$this->docroot/webGui/include/InstallKey.php";
$KeyInstaller = new KeyInstaller();
$KeyInstaller->installKey($key);
$installResponse = $KeyInstaller->installKey($key);
$installSuccess = false;
if (!empty($installResponse)) {
$decodedResponse = json_decode($installResponse, true);
if (isset($decodedResponse['error'])) {
$this->writeJsonFile(
'/tmp/ReplaceKey/error.json',
[
'error' => $decodedResponse['error'],
'ts' => time(),
]
);
$installSuccess = false;
} else {
$installSuccess = true;
}
}
$keyType = basename($key, '.key');
$output = isset($GLOBALS['notify']) ? _var($GLOBALS['notify'],'plugin') : '';
$script = '/usr/local/emhttp/webGui/scripts/notify';
if ($installSuccess) {
$event = "Installed New $keyType License";
$subject = "Your new $keyType license key has been automatically installed";
$description = "";
$importance = "normal $output";
} else {
$event = "Failed to Install New $keyType License";
$subject = "Failed to automatically install your new $keyType license key";
$description = isset($decodedResponse['error']) ? $decodedResponse['error'] : "Unknown error occurred";
$importance = "alert $output";
}
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg($importance)." -l '/Tools/Registration' -x");
return $installSuccess;
}
private function writeJsonFile($file, $data)
{
if (!is_dir(dirname($file))) { // prevents errors when directory doesn't exist
if (!is_dir(dirname($file))) {
mkdir(dirname($file));
}
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
public function check()
public function check(bool $forceCheck = false)
{
// we don't need to check
if (empty($this->guid) || empty($this->keyfile) || empty($this->regExp)) {
return;
}
// set $isAlreadyExpired to true if regExp is in the past
$isAlreadyExpired = $this->regExp <= time();
// if regExp is seven days or less from now, we need to check
$isWithinSevenDays = $this->regExp <= strtotime('+7 days');
// Check if we're within the 7-day window before and after regExp
$now = time();
$sevenDaysBefore = strtotime('-7 days', $this->regExp);
$sevenDaysAfter = strtotime('+7 days', $this->regExp);
$shouldCheck = $isAlreadyExpired || $isWithinSevenDays;
if (!$shouldCheck) {
$isWithinWindow = ($now >= $sevenDaysBefore && $now <= $sevenDaysAfter);
if (!$forceCheck && !$isWithinWindow) {
return;
}
@@ -187,10 +226,12 @@ class ReplaceKey
'/tmp/ReplaceKey/error.json',
[
'error' => 'Failed to retrieve latest key after getting a `hasNewerKeyfile` in the validation response.',
'ts' => time(),
]
);
return;
}
$this->installNewKey($latestKey);
}
}
}