refactor: downgrade & update os web components

This commit is contained in:
Zack Spear
2023-11-07 16:46:40 -08:00
parent ff3db071b9
commit 6d64b53ac7
14 changed files with 261 additions and 125 deletions

View File

@@ -1,4 +1,4 @@
<?PHP
<?php
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
$var = (array)parse_ini_file('state/var.ini');

View File

@@ -16,6 +16,15 @@
margin-left: auto;
height: 100%;
}
/**
* Tools page, rotate the Downgrade icon to prevent needing to add a new icon to the icon font.
* The pseudo element is targeted here otherwise the rotation of the span would mess up spacing with the text.
*/
a[href="/Tools/Downgrade"] .icon-update:before {
display: inline-block; /* required otherwise the rotation won't work */
rotate: 180deg;
}
</style>
<?php
// Set the path for the local manifest file

View File

@@ -1,29 +1,7 @@
<?PHP
<?php
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once("$docroot/plugins/dynamix.my.servers/include/state.php");
require_once("$docroot/plugins/dynamix.my.servers/include/translations.php");
/**
* Reboot detection was moved from Update.page to here as to seed the web components on every page rather than just on /Tools/Update
*/
$readme = @file_get_contents("$docroot/plugins/unRAIDServer/README.md", false, null, 0, 20) ?? ''; // read first 20 bytes of README.md
$reboot = preg_match("/^\*\*(REBOOT REQUIRED|DOWNGRADE)/", $readme);
$rebootForDowngrade = $reboot && strpos($readme, 'DOWNGRADE') !== false;
$rebootForUpgrade = $reboot && strpos($readme, 'REBOOT REQUIRED') !== false;
$rebootType = $rebootForDowngrade ? 'downgrade' : ($rebootForUpgrade ? 'upgrade' : '');
/**
* Detect if third-party drivers were part of the upgrade process
*/
$processWaitingThirdParthDrivers = "inotifywait -q /boot/changes.txt -e move_self,delete_self";
// Run the ps command to list processes and check if the process is running
$ps_command = "ps aux | grep -E \"$processWaitingThirdParthDrivers\" | grep -v \"grep -E\"";
$output = shell_exec($ps_command) ?? '';
if (strpos($output, $processWaitingThirdParthDrivers) !== false) {
$rebootType = 'thirdPartyDriversDownloading';
}
?>
<script>
window.LOCALE_DATA = '<?= rawurlencode(json_encode($webComponentTranslations, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE)) ?>';
@@ -43,7 +21,5 @@ if (!document.getElementsByTagName(modalsWebComponent).length) {
<?
echo "
<unraid-i18n-host>
<unraid-user-profile
reboot-type='" . $rebootType . "'
server='" . json_encode($serverState) . "'></unraid-user-profile>
<unraid-user-profile server='" . json_encode($serverState) . "'></unraid-user-profile>
</unraid-i18n-host>";

View File

@@ -0,0 +1,96 @@
<?php
/**
* RebootDetails class is responsible for detecting the type and version of a system reboot required in the context of an unRAID server.
*
* Usage:
* ```
* $rebootDetails = new RebootDetails();
* $rebootType = $rebootDetails->getRebootType();
* ```
*/
class RebootDetails
{
/**
* @var string $rebootType Stores the type of reboot required, which can be 'update', 'downgrade', or 'thirdPartyDriversDownloading'.
*/
private $rebootType = '';
/**
* Constructs a new RebootDetails object and automatically detects the reboot type during initialization.
*/
public function __construct()
{
$this->detectRebootType();
}
/**
* Detects the type of reboot required based on the contents of the unRAID server's README.md file.
* Sets the $rebootType property accordingly.
*/
private function detectRebootType()
{
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
$rebootReadme = @file_get_contents("$docroot/plugins/unRAIDServer/README.md", false, null, 0, 20) ?: '';
$rebootDetected = preg_match("/^\*\*(REBOOT REQUIRED|DOWNGRADE)/", $rebootReadme);
$rebootForDowngrade = $rebootDetected && strpos($rebootReadme, 'DOWNGRADE') !== false;
$rebootForUpdate = $rebootDetected && strpos($rebootReadme, 'REBOOT REQUIRED') !== false;
$this->rebootType = $rebootForDowngrade ? 'downgrade' : ($rebootForUpdate ? 'update' : '');
// Detect if third-party drivers were part of the update process
$processWaitingThirdParthDrivers = "inotifywait -q /boot/changes.txt -e move_self,delete_self";
// Run the ps command to list processes and check if the process is running
$ps_command = "ps aux | grep -E \"$processWaitingThirdParthDrivers\" | grep -v \"grep -E\"";
$output = shell_exec($ps_command) ?? '';
if (strpos($output, $processWaitingThirdParthDrivers) !== false) {
$this->rebootType = 'thirdPartyDriversDownloading';
}
}
/**
* Gets the type of reboot required, which can be 'update', 'downgrade', or 'thirdPartyDriversDownloading'.
*
* @return string The type of reboot required.
*/
public function getRebootType()
{
return $this->rebootType;
}
/**
* Detects and retrieves the version information related to the system reboot based on the contents of the '/boot/changes.txt' file.
*
* @return string The system version information or 'Not found' if not found, or 'File not found' if the file is not present.
*/
public function getRebootVersion()
{
$file_path = '/boot/changes.txt';
// Check if the file exists
if (file_exists($file_path)) {
// Open the file for reading
$file = fopen($file_path, 'r');
// Read the file line by line until we find a line that starts with '# Version'
while (($line = fgets($file)) !== false) {
if (strpos($line, '# Version') === 0) {
// Use a regular expression to extract the full version string
if (preg_match('/# Version\s+(\S+)/', $line, $matches)) {
$fullVersion = $matches[1];
return $fullVersion;
} else {
return 'Not found';
}
break;
}
}
// Close the file
fclose($file);
} else {
return 'File not found';
}
}
}

View File

@@ -1,4 +1,7 @@
<?php
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
// read flashbackup ini file
$flashbackup_ini = '/var/local/emhttp/flashbackup.ini';
$flashbackup_status = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashbackup_ini) : [];
@@ -31,12 +34,21 @@ $configErrorEnum = [
$osVersionBranch = trim(@exec('plugin category /var/log/plugins/unRAIDServer.plg') ?? 'stable');
$registered = !empty($myservers['remote']['apikey']) && $connectPluginInstalled;
/**
* Reboot detection
*/
// Create an instance of the RebootDetails class
$rebootDetails = new RebootDetails();
// Access the detected reboot type
$rebootType = $rebootDetails->getRebootType();
$serverState = [
"apiKey" => $myservers['upc']['apikey'] ?? '',
"apiVersion" => $myservers['api']['version'] ?? '',
"avatar" => (!empty($myservers['remote']['avatar']) && $connectPluginInstalled) ? $myservers['remote']['avatar'] : '',
"config" => [
'valid' => ($var['configValid'] === 'yes'),
/** @todo remove error key value when config is valid */
'error' => isset($configErrorEnum[$var['configValid']]) ? $configErrorEnum[$var['configValid']] : 'UNKNOWN_ERROR',
],
"connectPluginInstalled" => $connectPluginInstalled,
@@ -65,6 +77,7 @@ $serverState = [
"osVersion" => $var['version'],
"osVersionBranch" => $osVersionBranch,
"protocol" => $_SERVER['REQUEST_SCHEME'],
"rebootType" => $rebootType,
"regDev" => @(int)$var['regDev'] ?? 0,
"regGen" => @(int)$var['regGen'],
"regGuid" => @$var['regGUID'] ?? '',

View File

@@ -155,7 +155,6 @@ $webComponentTranslations = [
'<p>Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>If you do not have a backup copy of your license key file you may attempt to recover your key.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>' => '<p>' . _('Your license key file is corrupted or missing.') . ' ' . _('The key file should be located in the /config directory on your USB Flash boot device') . '</p><p>' . _('If you do not have a backup copy of your license key file you may attempt to recover your key with your Unraid.net account') . '</p><p>' . _('If this was an expired Trial installation, you may purchase a license key.') . '</p>',
'Invalid installation' => _('Invalid installation'),
'<p>It is not possible to use a Trial key with an existing Unraid OS installation.</p><p>You may purchase a license key corresponding to this USB Flash device to continue using this installation.</p>' => '<p>' . _('It is not possible to use a Trial key with an existing Unraid OS installation') . '</p><p>' . _('You may purchase a license key corresponding to this USB Flash device to continue using this installation.') . '</p>',
'No USB flash configuration data' => _('No USB flash configuration data'),
'<p>There is a problem with your USB Flash device</p>' => '<p>' . _('There is a problem with your USB Flash device') . '</p>',
'No Flash' => _('No Flash'),

View File

@@ -1,4 +0,0 @@
span.thanks{padding-left:12px;color:#6FA239;font-weight:bold;}
span.thanks.red{color:#F0000C;}
div.device{padding:0 12px;font-weight:normal;font-style:italic;}
div.remark{padding:0 12px;text-align:justify;}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +1,13 @@
{
".nuxt/nuxt-custom-elements/entries/unraid-components.client.css": {
"file": "_nuxt/unraid-components.client-896c373f.css",
"file": "_nuxt/unraid-components.client-060d5fb6.css",
"src": ".nuxt/nuxt-custom-elements/entries/unraid-components.client.css"
},
".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs": {
"css": [
"_nuxt/unraid-components.client-896c373f.css"
"_nuxt/unraid-components.client-060d5fb6.css"
],
"file": "_nuxt/unraid-components.client-41af9187.js",
"file": "_nuxt/unraid-components.client-f56a40ff.js",
"isEntry": true,
"src": ".nuxt/nuxt-custom-elements/entries/unraid-components.client.mjs"
}

View File

@@ -4,20 +4,24 @@ Icon="icon-update"
Tag="upload"
---
<?php
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
// Create an instance of the RebootDetails class
$rebootDetails = new RebootDetails();
// Access the detected reboot type
$rebootVersion = $rebootDetails->getRebootVersion();
/**
* @todo - need a downgrade icon
* @note icon-update is rotated via CSS in myservers1.php
*
* If /boot/previous/bzroot exists, then the user has the option to downgrade to the previous version.
* Parse the text file /boot/previous/changes.txt to get the version number of the previous version.
* Then we move some files around and reboot.
*/
$check = $notify['unraidos'] ? 0 : 1;
$restoreVersion = $restoreBranch = $restoreVersionReleaseDate = 'unknown';
$restoreExists = file_exists('/boot/previous/bzroot');
$restoreChangelogPath = '/boot/previous/changes.txt';
$restoreChangelogContent = file_get_contents($restoreChangelogPath);
$diagnosticsZip = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));
$serverNameEscaped = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));
if (file_exists($restoreChangelogPath)) {
exec("head -n4 $restoreChangelogPath", $rows);
@@ -37,57 +41,29 @@ if (file_exists($restoreChangelogPath)) {
?>
<script>
var diagnosticsFile = "";
var nchan_diagnostics = new NchanSubscriber('/sub/diagnostics', { subscriber: 'websocket' });
const args = {};
const nchan_diagnostics = new NchanSubscriber('/sub/diagnostics', { subscriber: 'websocket' });
const reportUrl = new URL('https://forums.unraid.net/bug-reports/');
let diagnosticsFile = '';
nchan_diagnostics.on('message', function(data) {
if (data == '_DONE_') {
nchan_diagnostics.stop();
$('.sweet-alert').hide('fast').removeClass('nchan');
swal.close();
$('div.spinner').show('slow');
location = diagnosticsFile;
setTimeout(cleanUp,4000);
setTimeout(() => {
cleanupDiagnostics();
reportAfterDiagnosticsDownload();
}, 2000);
} else if (data) {
let box = $('pre#swaltext');
box.html(box.html() + '<br>' + data).scrollTop(box[0].scrollHeight);
}
});
function downgradeAction() {
$.get('/plugins/dynamix.plugin.manager/include/Downgrade.php', { version: '<?=$restoreVersion?>' }, function() { refresh(); });
}
function downgrade() {
swal({
title: "_(Diagnostics)_",
text: "_(Please provide diagnostics when experiencing problems)_<br>_(Post these in the forums)_",
html: true,
type: 'warning',
showCancelButton: true,
confirmButtonText: "<?= _('Diagnostics') ?>",
cancelButtonText: "<?= _('Restore') ?>",
}, function(diag) {
if (diag) {
// get diagnostics and then downgrade
setTimeout(function() {
diagnostics(zipfile());
}, 250);
} else {
// downgrade immediately
downgradeAction();
}
});
}
function cleanUp() {
if (document.hasFocus()) {
$.post('/webGui/include/Download.php', { cmd: 'delete', file: diagnosticsFile }, downgradeAction());
} else {
setTimeout(cleanUp,2000);
}
}
function zipfile() {
function downloadDiagnostics() {
const tzoffset = (new Date()).getTimezoneOffset() * 60000; // offset in milliseconds
const localISOTime = (new Date(Date.now() - tzoffset));
const year = localISOTime.getFullYear();
@@ -96,12 +72,22 @@ function zipfile() {
const hours = String(localISOTime.getHours()).padStart(2, '0');
const minutes = String(localISOTime.getMinutes()).padStart(2, '0');
const dateOutput = `${year}${month}${day}_${hours}${minutes}`;
return '<?=$diagnosticsZip?>-diagnostics-' + dateOutput + '.zip';
}
function diagnostics(file) {
const zipName = '<?=$serverNameEscaped?>-diagnostics-' + dateOutput + '.zip';
nchan_diagnostics.start();
$.post('/webGui/include/Download.php', { cmd:'diag', file: file, anonymize: '' }, function(zip) {
if (zip) {
$.post(
'/webGui/include/Download.php',
{
cmd: 'diag',
file: zipName,
anonymize: '',
},
function(zip) {
if (!zip) {
return nchan_diagnostics.stop();
}
diagnosticsFile = zip;
swal({
title: "_(Downloading)_...",
@@ -112,15 +98,70 @@ function diagnostics(file) {
});
$('.sweet-alert').addClass('nchan');
$('button.confirm').prop('disabled', true);
} else {
nchan_diagnostics.stop();
},
);
}
function reportAfterDiagnosticsDownload() {
$('div.spinner').hide('fast');
swal({
title: "_(Open a bug report)_",
text: "_(Create a bug report on our forums with a description of the issue along with your diagsnotics)_",
html: true,
type: 'warning',
showCancelButton: true,
confirmButtonText: "<?= _('Create Bug Report') ?>",
cancelButtonText: "<?= _('Close') ?>",
}, function(confirm) {
if (!confirm) {
return false;
}
window.open(reportUrl, '_blank');
});
}
function cleanupDiagnostics() {
if (document.hasFocus()) {
return $.post('/webGui/include/Download.php', { cmd: 'delete', file: diagnosticsFile });
}
setTimeout(cleanupDiagnostics, 2000);
}
function startDowngrade() {
$('div.spinner').show('slow');
$.get(
'/plugins/dynamix.plugin.manager/include/Downgrade.php',
{
version: '<?=$restoreVersion?>',
},
function() {
refresh();
}
);
}
function confirmDowngrade() {
swal({
title: "_(Confirm Downgrade)_",
text: "<?= $restoreVersion ?><br>_(A reboot will be required)_",
html: true,
type: 'warning',
showCancelButton: true,
confirmButtonText: "<?= _('Confirm') ?>",
cancelButtonText: "<?= _('Cancel') ?>",
}, function(confirm) {
if (!confirm) {
return false;
}
startDowngrade();
});
}
</script>
<unraid-i18n-host>
<unraid-downgrade-os
reboot-version="<?= $rebootVersion ?>"
restore-version="<?= $restoreExists && $restoreVersion != 'unknown' ? $restoreVersion : '' ?>"
restore-release-date="<?= $restoreExists && $restoreVersionReleaseDate != 'unknown' ? $restoreVersionReleaseDate : '' ?>"></unraid-downgrade-os>
</unraid-i18n-host>
</unraid-i18n-host>

View File

@@ -3,6 +3,13 @@ Title="Update OS"
Icon="icon-update"
Tag="upload"
---
<?php
require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php";
// Create an instance of the RebootDetails class
$rebootDetails = new RebootDetails();
// Access the detected reboot type
$rebootVersion = $rebootDetails->getRebootVersion();
?>
<script>
function cleanUpFlashBackup(zip) {
if (document.hasFocus()) {
@@ -33,5 +40,5 @@ function flashBackup() {
</script>
<unraid-i18n-host>
<unraid-update-os></unraid-update-os>
<unraid-update-os reboot-version="<?= $rebootVersion ?>"></unraid-update-os>
</unraid-i18n-host>