mirror of
https://github.com/unraid/webgui.git
synced 2026-01-07 01:59:52 -06:00
ignore emhttp/plugins/dynamix.my.servers
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -82,7 +82,7 @@ unraid-api/
|
||||
.node-version
|
||||
|
||||
# Node.js built-in's like npm & corepack
|
||||
lib/
|
||||
lib/node_modules
|
||||
|
||||
# unraid-api & node binaries
|
||||
bin/
|
||||
@@ -91,11 +91,15 @@ sbin/unraid-api
|
||||
# Unraid API readme/changelog
|
||||
emhttp/plugins/dynamix.unraid.net
|
||||
|
||||
# API / Web component / Connect functionality
|
||||
emhttp/plugins/dynamix.my.servers
|
||||
|
||||
# rc scripts
|
||||
etc/rc.d/rc.flash_backup
|
||||
etc/rc.d/rc.unraid-api
|
||||
etc/rc.d/rc0.d
|
||||
etc/rc.d/rc6.d
|
||||
|
||||
etc/rc.d/rc6.d/K10flash-backup
|
||||
etc/rc.d/rc6.d/K20unraid-api
|
||||
|
||||
# API helper scripts
|
||||
share/dynamix.unraid.net/
|
||||
|
||||
12
emhttp/plugins/dynamix.my.servers/.gitignore
vendored
12
emhttp/plugins/dynamix.my.servers/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
# Provided by Unraid API
|
||||
Connect.page
|
||||
scripts/
|
||||
unraid-components/
|
||||
|
||||
include/UpdateFlashBackup.php
|
||||
include/sso-login.php
|
||||
include/unraid-api.php
|
||||
include/welcome-modal.php
|
||||
include/myservers1.php
|
||||
include/state.php
|
||||
include/translations.php
|
||||
@@ -1,23 +0,0 @@
|
||||
Menu="About:30"
|
||||
Type="xmenu"
|
||||
Title="Registration"
|
||||
Icon="icon-registration"
|
||||
Tag="pencil"
|
||||
---
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
require_once "$docroot/plugins/dynamix/include/ReplaceKey.php";
|
||||
$replaceKey = new ReplaceKey();
|
||||
$replaceKey->check(true);
|
||||
?>
|
||||
<unraid-i18n-host>
|
||||
<unraid-registration></unraid-registration>
|
||||
</unraid-i18n-host>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
$var = (array)parse_ini_file('state/var.ini'); // required for state.php - don't remove unless you've refactored the code to work without it
|
||||
require_once "$docroot/plugins/dynamix.my.servers/include/state.php";
|
||||
|
||||
$serverState = new ServerState();
|
||||
|
||||
header('Content-type: application/json');
|
||||
|
||||
echo $serverState->getServerStateJson();
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
class ActivationCodeExtractor {
|
||||
public const DIR = '/boot/config/activation';
|
||||
public const FILE_PATTERN = '/activation_code_([A-Za-z0-9]+)\.activationcode/';
|
||||
|
||||
public const DOCROOT = '/usr/local/emhttp';
|
||||
public const WEBGUI_IMAGES_BASE_DIR = '/webGui/images';
|
||||
public const PARTNER_LOGO_FILE_NAME = 'partner-logo.svg';
|
||||
public const DEFAULT_LOGO = self::DOCROOT . self::WEBGUI_IMAGES_BASE_DIR . '/UN-logotype-gradient.svg';
|
||||
|
||||
/** @var array{
|
||||
* code: string,
|
||||
* partnerName: string,
|
||||
* partnerUrl?: string,
|
||||
* sysModel?: string,
|
||||
* comment?: string,
|
||||
* caseIcon: string,
|
||||
* partnerLogo?: boolean,
|
||||
* header?: string,
|
||||
* headermetacolor?: string,
|
||||
* background?: string,
|
||||
* showBannerGradient?: string,
|
||||
* theme?: "azure" | "black" | "gray" | "white
|
||||
* }
|
||||
*/
|
||||
private array $data = [];
|
||||
private string $partnerName = '';
|
||||
private string $partnerUrl = 'https://unraid.net';
|
||||
private string $partnerLogoPath = '';
|
||||
|
||||
/**
|
||||
* Constructor to automatically fetch JSON data from all matching files.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->data = $this->fetchJsonData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch JSON data from all files matching the pattern.
|
||||
*
|
||||
* @return array Array of extracted JSON data.
|
||||
*/
|
||||
private function fetchJsonData(): array {
|
||||
$data = [];
|
||||
|
||||
if (!is_dir(self::DIR)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$files = scandir(self::DIR);
|
||||
|
||||
if ($files === false || count($files) === 0) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
$filePath = self::DIR . DIRECTORY_SEPARATOR . $file;
|
||||
|
||||
if (preg_match(self::FILE_PATTERN, $file, $matches)) {
|
||||
// $activationCode = $matches[1];
|
||||
$fileContent = file_get_contents($filePath);
|
||||
$jsonData = json_decode($fileContent, true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$data = $jsonData;
|
||||
} else {
|
||||
$data = ['error' => 'Invalid JSON format'];
|
||||
}
|
||||
|
||||
break; // Stop after the first match
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['partnerName'])) {
|
||||
$this->partnerName = $data['partnerName'];
|
||||
}
|
||||
|
||||
if (isset($data['partnerUrl'])) {
|
||||
$this->partnerUrl = $data['partnerUrl'];
|
||||
}
|
||||
|
||||
/**
|
||||
* During the plg install, the partner logo asset is copied to the webgui images dir.
|
||||
*/
|
||||
$logo = self::DOCROOT . self::WEBGUI_IMAGES_BASE_DIR . '/' . self::PARTNER_LOGO_FILE_NAME;
|
||||
if (file_exists($logo)) {
|
||||
$this->partnerLogoPath = $logo;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the partner logo path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartnerLogoPath(): string {
|
||||
return $this->partnerLogoPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extracted data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the activation code data as JSON string with converted special characters to HTML entities
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDataForHtmlAttr(): string {
|
||||
$json = json_encode($this->getData());
|
||||
return htmlspecialchars($json, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the partner logo render string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartnerLogoRenderString(): string {
|
||||
if (empty($this->partnerLogoPath)) { // default logo
|
||||
return file_get_contents(self::DEFAULT_LOGO);
|
||||
}
|
||||
|
||||
return file_get_contents($this->partnerLogoPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the partner name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartnerName(): string {
|
||||
return $this->partnerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the partner URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartnerUrl(): string {
|
||||
return $this->partnerUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for debugging
|
||||
* @return void
|
||||
*/
|
||||
public function debug(): void {
|
||||
echo "data: "; var_dump($this->data);
|
||||
echo "partnerName: "; var_dump($this->partnerName);
|
||||
echo "partnerUrl: "; var_dump($this->partnerUrl);
|
||||
echo "partnerLogoPath: "; var_dump($this->partnerLogoPath);
|
||||
|
||||
echo $this->getPartnerLogoRenderString();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
$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");
|
||||
|
||||
$serverState = new ServerState();
|
||||
$wCTranslations = new WebComponentTranslations();
|
||||
?>
|
||||
<script>
|
||||
window.LOCALE_DATA = '<?= $wCTranslations->getTranslationsJson(true) ?>';
|
||||
/**
|
||||
* So we're not needing to modify DefaultLayout with an additional include, we'll add the Modals web component to the bottom of the body.
|
||||
*/
|
||||
const i18nHostWebComponent = 'unraid-i18n-host';
|
||||
const modalsWebComponent = 'unraid-modals';
|
||||
if (!document.getElementsByTagName(modalsWebComponent).length) {
|
||||
const $body = document.getElementsByTagName('body')[0];
|
||||
const $i18nHost = document.createElement(i18nHostWebComponent);
|
||||
const $modals = document.createElement(modalsWebComponent);
|
||||
$body.appendChild($i18nHost);
|
||||
$i18nHost.appendChild($modals);
|
||||
}
|
||||
</script>
|
||||
|
||||
<unraid-i18n-host>
|
||||
<unraid-user-profile server="<?= $serverState->getServerStateJsonForHtmlAttr() ?>"></unraid-user-profile>
|
||||
</unraid-i18n-host>
|
||||
@@ -1,168 +0,0 @@
|
||||
<?php
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* 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->rebootType;
|
||||
* ```
|
||||
*/
|
||||
class RebootDetails
|
||||
{
|
||||
const CURRENT_CHANGES_TXT_PATH = '/boot/changes.txt';
|
||||
const CURRENT_README_RELATIVE_PATH = 'plugins/unRAIDServer/README.md';
|
||||
const CURRENT_VERSION_PATH = '/etc/unraid-version';
|
||||
const PREVIOUS_BZ_ROOT_PATH = '/boot/previous/bzroot';
|
||||
const PREVIOUS_CHANGES_TXT_PATH = '/boot/previous/changes.txt';
|
||||
|
||||
private $currentVersion = '';
|
||||
|
||||
public $rebootType = ''; // 'update', 'downgrade', 'thirdPartyDriversDownloading'
|
||||
public $rebootReleaseDate = '';
|
||||
public $rebootVersion = '';
|
||||
|
||||
public $previousReleaseDate = '';
|
||||
public $previousVersion = '';
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
/**
|
||||
* Read the reboot readme, and see if it says "REBOOT REQUIRED" or "DOWNGRADE"
|
||||
* only relying on the README.md file to save reads from the flash drive.
|
||||
* because we started allowing downgrades from the account.unraid.net Update OS page, we can't
|
||||
* fully rely on the README.md value of being accurate.
|
||||
* For instance if on 6.13.0-beta.2.1 then chose to "Downgrade" to 6.13.0-beta.1.10 from the account app
|
||||
* the README.md file would still say "REBOOT REQUIRED".
|
||||
*/
|
||||
$rebootReadme = @file_get_contents("$docroot/" . self::CURRENT_README_RELATIVE_PATH, false, null, 0, 20) ?: '';
|
||||
$rebootDetected = preg_match("/^\*\*(REBOOT REQUIRED|DOWNGRADE)/", $rebootReadme);
|
||||
if (!$rebootDetected) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* if a reboot is required, then:
|
||||
* get current Unraid version from /etc/unraid-version
|
||||
* then get the version of the last update from self::CURRENT_CHANGES_TXT_PATH
|
||||
* if they're different, then a reboot is required
|
||||
* if the version in self::CURRENT_CHANGES_TXT_PATH is less than the current version, then a downgrade is required
|
||||
* if the version in self::CURRENT_CHANGES_TXT_PATH is greater than the current version, then an update is required
|
||||
*/
|
||||
$this->setCurrentVersion();
|
||||
$this->setRebootDetails();
|
||||
if ($this->currentVersion == '' || $this->rebootVersion == '') {
|
||||
return; // return to prevent potential incorrect outcome
|
||||
}
|
||||
|
||||
$compareVersions = version_compare($this->rebootVersion, $this->currentVersion);
|
||||
switch ($compareVersions) {
|
||||
case -1:
|
||||
$this->setRebootType('downgrade');
|
||||
break;
|
||||
case 0:
|
||||
// we should never get here, but if we do, then no reboot is required and just return
|
||||
return;
|
||||
case 1:
|
||||
$this->setRebootType('update');
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect if third-party drivers were part of the update process
|
||||
$processWaitingThirdPartyDrivers = "inotifywait -q " . self::CURRENT_CHANGES_TXT_PATH . " -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 \"$processWaitingThirdPartyDrivers\" | grep -v \"grep -E\"";
|
||||
$output = shell_exec($ps_command) ?? '';
|
||||
if ($this->rebootType != '' && strpos($output, $processWaitingThirdPartyDrivers) !== false) {
|
||||
$this->setRebootType('thirdPartyDriversDownloading');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private function readChangesTxt(string $file_path = self::CURRENT_CHANGES_TXT_PATH)
|
||||
{
|
||||
// Check if the file exists
|
||||
if (file_exists($file_path)) {
|
||||
exec("head -n4 $file_path", $rows);
|
||||
foreach ($rows as $row) {
|
||||
$i = stripos($row,'version');
|
||||
if ($i !== false) {
|
||||
[$version, $releaseDate] = explode(' ', trim(substr($row, $i+7)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'releaseDate' => $releaseDate ?? 'Not found',
|
||||
'version' => $version ?? 'Not found',
|
||||
];
|
||||
} else {
|
||||
return 'File not found';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current version of the Unraid server for comparison with the reboot version.
|
||||
*/
|
||||
private function setCurrentVersion() {
|
||||
// output ex: version="6.13.0-beta.2.1"
|
||||
$raw = @file_get_contents(self::CURRENT_VERSION_PATH) ?: '';
|
||||
// Regular expression to match the version between the quotes
|
||||
$pattern = '/version="([^"]+)"/';
|
||||
if (preg_match($pattern, $raw, $matches)) {
|
||||
$this->currentVersion = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
private function setRebootDetails()
|
||||
{
|
||||
$rebootDetails = $this->readChangesTxt();
|
||||
$this->rebootReleaseDate = $rebootDetails['releaseDate'];
|
||||
$this->rebootVersion = $rebootDetails['version'];
|
||||
}
|
||||
|
||||
private function setRebootType($rebootType)
|
||||
{
|
||||
$this->rebootType = $rebootType;
|
||||
}
|
||||
|
||||
/**
|
||||
* If self::PREVIOUS_BZ_ROOT_PATH 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.
|
||||
*/
|
||||
public function setPrevious()
|
||||
{
|
||||
if (@file_exists(self::PREVIOUS_BZ_ROOT_PATH) && @file_exists(self::PREVIOUS_CHANGES_TXT_PATH)) {
|
||||
$parseOutput = $this->readChangesTxt(self::PREVIOUS_CHANGES_TXT_PATH);
|
||||
$this->previousVersion = $parseOutput['version'];
|
||||
$this->previousReleaseDate = $parseOutput['releaseDate'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
<?php
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||
|
||||
class WebComponentsExtractor
|
||||
{
|
||||
private const PREFIXED_PATH = '/plugins/dynamix.my.servers/unraid-components/';
|
||||
private const RICH_COMPONENTS_ENTRY = 'unraid-components.client.mjs';
|
||||
private const UI_ENTRY = 'src/register.ts';
|
||||
private const UI_STYLES_ENTRY = 'style.css';
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
private function findManifestFiles(string $manifestName): array
|
||||
{
|
||||
$basePath = '/usr/local/emhttp' . self::PREFIXED_PATH;
|
||||
$escapedBasePath = escapeshellarg($basePath);
|
||||
$escapedManifestName = escapeshellarg($manifestName);
|
||||
$command = "find {$escapedBasePath} -name {$escapedManifestName}";
|
||||
exec($command, $files);
|
||||
return $files;
|
||||
}
|
||||
|
||||
public function getAssetPath(string $asset, string $subfolder = ''): string
|
||||
{
|
||||
return self::PREFIXED_PATH . ($subfolder ? $subfolder . '/' : '') . $asset;
|
||||
}
|
||||
|
||||
private function getRelativePath(string $fullPath): string
|
||||
{
|
||||
$basePath = '/usr/local/emhttp' . self::PREFIXED_PATH;
|
||||
$relative = str_replace($basePath, '', $fullPath);
|
||||
return dirname($relative);
|
||||
}
|
||||
|
||||
public function getManifestContents(string $manifestPath): array
|
||||
{
|
||||
$contents = @file_get_contents($manifestPath);
|
||||
return $contents ? json_decode($contents, true) : [];
|
||||
}
|
||||
|
||||
private function getRichComponentsFile(): string
|
||||
{
|
||||
$manifestFiles = $this->findManifestFiles('manifest.json');
|
||||
|
||||
foreach ($manifestFiles as $manifestPath) {
|
||||
$manifest = $this->getManifestContents($manifestPath);
|
||||
$subfolder = $this->getRelativePath($manifestPath);
|
||||
|
||||
foreach ($manifest as $key => $value) {
|
||||
if (strpos($key, self::RICH_COMPONENTS_ENTRY) !== false && isset($value["file"])) {
|
||||
return ($subfolder ? $subfolder . '/' : '') . $value["file"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getRichComponentsScript(): string
|
||||
{
|
||||
$jsFile = $this->getRichComponentsFile();
|
||||
if (empty($jsFile)) {
|
||||
return '<script>console.error("%cNo matching key containing \'' . self::RICH_COMPONENTS_ENTRY . '\' found.", "font-weight: bold; color: white; background-color: red");</script>';
|
||||
}
|
||||
return '<script src="' . $this->getAssetPath($jsFile) . '"></script>';
|
||||
}
|
||||
|
||||
private function getUnraidUiScriptHtml(): string
|
||||
{
|
||||
$manifestFiles = $this->findManifestFiles('ui.manifest.json');
|
||||
|
||||
if (empty($manifestFiles)) {
|
||||
error_log("No ui.manifest.json found");
|
||||
return '';
|
||||
}
|
||||
|
||||
$manifestPath = $manifestFiles[0]; // Use the first found manifest
|
||||
$manifest = $this->getManifestContents($manifestPath);
|
||||
$subfolder = $this->getRelativePath($manifestPath);
|
||||
|
||||
if (!isset($manifest[self::UI_ENTRY]) || !isset($manifest[self::UI_STYLES_ENTRY])) {
|
||||
error_log("Required entries not found in ui.manifest.json");
|
||||
return '';
|
||||
}
|
||||
|
||||
$jsFile = ($subfolder ? $subfolder . '/' : '') . $manifest[self::UI_ENTRY]['file'];
|
||||
$cssFile = ($subfolder ? $subfolder . '/' : '') . $manifest[self::UI_STYLES_ENTRY]['file'];
|
||||
|
||||
return '<script defer type="module">
|
||||
import { registerAllComponents } from "' . $this->getAssetPath($jsFile) . '";
|
||||
registerAllComponents({ pathToSharedCss: "' . $this->getAssetPath($cssFile) . '" });
|
||||
</script>';
|
||||
}
|
||||
|
||||
public function getScriptTagHtml(): string
|
||||
{
|
||||
try {
|
||||
return $this->getRichComponentsScript() . $this->getUnraidUiScriptHtml();
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error in WebComponentsExtractor::getScriptTagHtml: " . $e->getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user