chore: Remove legacy store modules and add new API key and reporting services (#1536)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added developer CLI tools for toggling GraphQL sandbox and modal
testing utilities.
* Introduced a "Show Activation Modal" developer component for UI
testing.
  * Added system initial setup detection and related GraphQL queries.
* Enhanced login and welcome pages with dynamic server info and initial
setup state.
  * Improved SSO button with internationalization and error handling.
* Added internal CLI admin API key management service and internal
GraphQL client service.
* Introduced comprehensive API report generation service for system and
service status.
* Added CLI commands and GraphQL mutations/queries for plugin and SSO
user management.
* Added new modal target components and improved teleport target
detection.

* **Enhancements**
* Refined modal dialog targeting and teleportation for flexible UI
placement.
* Updated modal components and stores for improved activation/welcome
modal control.
  * Improved plugin and SSO user management via CLI through GraphQL API.
* Refactored partner logo components to use props instead of store
dependencies.
  * Enhanced styling and accessibility for buttons and modals.
* Streamlined Tailwind CSS integration with shared styles and updated
theme variables.
* Improved GraphQL module configuration to avoid directive conflicts in
tests.
  * Adjusted Vite config for better dependency handling in test mode.
  * Improved error handling and logging in CLI commands and services.
* Reordered imports and refined component class bindings for UI
consistency.

* **Bug Fixes**
* Resolved issues with duplicate script tags and component registration
in the web UI.
* Fixed modal close button visibility and activation modal state
handling.
* Added error handling and logging improvements across CLI commands and
services.
  * Fixed newline issues in last-download-time fixture files.

* **Chores**
* Added and updated numerous tests for CLI commands, services, and UI
components.
* Updated translation files and localization resources for new UI
messages.
* Adjusted environment, configuration, and dependency files for improved
development and test workflows.
  * Cleaned up unused imports and mocks in tests.
  * Reorganized exports and barrel files in shared and UI modules.
  * Added integration and dependency resolution tests for core modules.

* **Removals & Refactoring**
* Removed legacy Redux state management, configuration, and UPnP logic
from the backend.
* Eliminated deprecated GraphQL subscriptions and client code related to
registration and mothership.
* Removed direct store manipulation and replaced with service-based
approaches in CLI commands.
  * Deleted unused or redundant test files and configuration listeners.
* Refactored SSO user service to consolidate add/remove operations into
a single update method.
* Simplified API key services with new methods for automatic key
management.
* Replaced direct plugin and SSO user service calls with GraphQL client
interactions in CLI commands.
* Removed complex theme fallback and dark mode CSS rules, replacing with
streamlined static theme variables.
* Cleaned up Tailwind CSS configuration and removed deprecated local
styles.
* Removed multiple internal utility files and replaced with simplified
or centralized implementations.
* Removed deprecated local configuration and synchronization files and
listeners.
  * Removed UPnP helper functions and job management classes.
* Refactored server resolver to dynamically construct local server data
internally.
* Removed CORS handler and replaced with simplified or externalized
logic.
* Removed store synchronization and registration event pubsub handling.
* Removed GraphQL client creation utilities for internal API
communication.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Eli Bosley
2025-07-25 15:07:37 -04:00
committed by GitHub
parent 4ff6a1aaa0
commit 3b00fec5fd
193 changed files with 7457 additions and 4757 deletions
@@ -12,5 +12,5 @@
<?php
require_once("$docroot/plugins/dynamix.my.servers/include/web-components-extractor.php");
$wcExtractor = new WebComponentsExtractor();
$wcExtractor = WebComponentsExtractor::getInstance();
echo $wcExtractor->getScriptTagHtml();
@@ -13,7 +13,7 @@ require_once("$docroot/plugins/dynamix.my.servers/include/web-components-extract
$serverState = new ServerState();
$wcExtractor = new WebComponentsExtractor();
$wcExtractor = WebComponentsExtractor::getInstance();
echo $wcExtractor->getScriptTagHtml();
?>
@@ -403,6 +403,17 @@ class WebComponentTranslations
'Your Trial has expired' => _('Your Trial has expired'),
'Your Trial key has been extended!' => _('Your Trial key has been extended!'),
'Unraid OS {0} Changelog' => sprintf(_('Unraid OS %s Changelog'), '{0}'),
'or' => _('or'),
'Logging in...' => _('Logging in...'),
'Try Again' => _('Try Again'),
'Log In With Unraid.net' => _('Log In With Unraid.net'),
'Invalid Unraid.net credentials' => _('Invalid Unraid.net credentials'),
'This Unraid.net account is not authorized to access this server' => _('This Unraid.net account is not authorized to access this server'),
'SSO login is not enabled on this server' => _('SSO login is not enabled on this server'),
'Login session expired. Please try again' => _('Login session expired. Please try again'),
'Network error. Please check your connection' => _('Network error. Please check your connection'),
'SSO login failed. Please try again' => _('SSO login failed. Please try again'),
'Error fetching token' => _('Error fetching token'),
];
}
@@ -9,7 +9,17 @@ class WebComponentsExtractor
private const UI_ENTRY = 'src/register.ts';
private const UI_STYLES_ENTRY = 'style.css';
public function __construct() {}
private static ?WebComponentsExtractor $instance = null;
private function __construct() {}
public static function getInstance(): WebComponentsExtractor
{
if (self::$instance === null) {
self::$instance = new WebComponentsExtractor();
}
return self::$instance;
}
private function findManifestFiles(string $manifestName): array
{
@@ -72,7 +82,20 @@ class WebComponentsExtractor
if (empty($jsFile)) {
return '<script>console.error("%cNo matching key containing \'' . self::RICH_COMPONENTS_ENTRY . '\' or \'' . self::RICH_COMPONENTS_ENTRY_JS . '\' found.", "font-weight: bold; color: white; background-color: red");</script>';
}
return '<script src="' . $this->getAssetPath($jsFile) . '"></script>';
// Add a unique identifier to prevent duplicate script loading
$scriptId = 'unraid-rich-components-script';
return '<script id="' . $scriptId . '" src="' . $this->getAssetPath($jsFile) . '"></script>
<script>
// Remove duplicate script tags to prevent multiple loads
(function() {
var scripts = document.querySelectorAll(\'script[id="' . $scriptId . '"]\');
if (scripts.length > 1) {
for (var i = 1; i < scripts.length; i++) {
scripts[i].remove();
}
}
})();
</script>';
}
private function getUnraidUiScriptHtml(): string
@@ -95,19 +118,65 @@ class WebComponentsExtractor
$jsFile = ($subfolder ? $subfolder . '/' : '') . $manifest[self::UI_ENTRY]['file'];
$cssFile = ($subfolder ? $subfolder . '/' : '') . $manifest[self::UI_STYLES_ENTRY]['file'];
// Read the CSS file content
$cssPath = '/usr/local/emhttp' . $this->getAssetPath($cssFile);
$cssContent = @file_get_contents($cssPath);
if ($cssContent === false) {
error_log("Failed to read CSS file: " . $cssPath);
$cssContent = '';
}
// Escape the CSS content for JavaScript
$escapedCssContent = json_encode($cssContent);
return '<script defer type="module">
import { registerAllComponents } from "' . $this->getAssetPath($jsFile) . '";
registerAllComponents({ pathToSharedCss: "' . $this->getAssetPath($cssFile) . '" });
// Use a data attribute to ensure this only runs once per page
return '<script data-unraid-ui-register defer type="module">
(async function() {
// Check if components have already been registered
if (window.__unraidUiComponentsRegistered) {
return;
}
// Mark as registered immediately to prevent race conditions
window.__unraidUiComponentsRegistered = true;
try {
const { registerAllComponents } = await import("' . $this->getAssetPath($jsFile) . '");
registerAllComponents({ sharedCssContent: ' . $escapedCssContent . ' });
} catch (error) {
console.error("[Unraid UI] Failed to register components:", error);
// Reset flag on error so it can be retried
window.__unraidUiComponentsRegistered = false;
}
// Clean up duplicate script tags
const scripts = document.querySelectorAll(\'script[data-unraid-ui-register]\');
if (scripts.length > 1) {
for (let i = 1; i < scripts.length; i++) {
scripts[i].remove();
}
}
})();
</script>';
}
public function getScriptTagHtml(): string
{
// Use a static flag to ensure scripts are only output once per request
static $scriptsOutput = false;
if ($scriptsOutput) {
return '<!-- Web components scripts already loaded -->';
}
try {
$scriptsOutput = true;
return $this->getRichComponentsScript() . $this->getUnraidUiScriptHtml();
} catch (\Exception $e) {
error_log("Error in WebComponentsExtractor::getScriptTagHtml: " . $e->getMessage());
$scriptsOutput = false; // Reset on error
return "";
}
}
@@ -10,9 +10,9 @@
require_once("$docroot/plugins/dynamix.my.servers/include/state.php");
require_once("$docroot/plugins/dynamix.my.servers/include/web-components-extractor.php");
$wcExtractor = new WebComponentsExtractor();
$wcExtractor = WebComponentsExtractor::getInstance();
echo $wcExtractor->getScriptTagHtml();
?>
<!-- Welcome modal -->
<unraid-welcome-modal></unraid-welcome-modal>