From bb851e4be2e60d55892123d446759e59594130f2 Mon Sep 17 00:00:00 2001 From: airano Date: Wed, 18 Feb 2026 21:11:49 +0330 Subject: [PATCH] fix(qa): health monitor SiteManager, WP plugins, favicon, download ZIPs - Health monitor now uses SiteManager for complete site discovery - SEO Bridge: GPLv2+ license, Tested up to 6.9, fix escaping - OpenPanel: fix wp_unslash, reduce tags, update branding - Add logo.svg favicon, static file serving, plugin ZIPs Co-Authored-By: Claude Opus 4.6 --- README.md | 3 +- core/health.py | 77 +++++++++++++++--- core/templates/base.html | 1 + core/templates/dashboard/base.html | 1 + core/templates/dashboard/login.html | 1 + core/templates/static/logo.svg | 1 + server.py | 8 ++ wordpress-plugin/openpanel.zip | Bin 11720 -> 11702 bytes wordpress-plugin/openpanel/openpanel.php | 8 +- wordpress-plugin/openpanel/readme.txt | 6 +- wordpress-plugin/seo-api-bridge.zip | Bin 8006 -> 9465 bytes wordpress-plugin/seo-api-bridge/README.md | 4 +- wordpress-plugin/seo-api-bridge/readme.txt | 6 +- .../seo-api-bridge/seo-api-bridge.php | 6 +- 14 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 core/templates/static/logo.svg diff --git a/README.md b/README.md index 516fef9..e62bae9 100644 --- a/README.md +++ b/README.md @@ -268,9 +268,10 @@ Some MCP Hub tools require companion WordPress plugins: | Tools | Requirement | |-------|-------------| -| SEO tools (`wordpress_get_post_seo`, etc.) | [SEO API Bridge](wordpress-plugin/seo-api-bridge/) + Rank Math or Yoast SEO | +| SEO tools (`wordpress_get_post_seo`, etc.) | [SEO API Bridge](wordpress-plugin/seo-api-bridge/) ([Download ZIP](wordpress-plugin/seo-api-bridge.zip)) + Rank Math or Yoast SEO | | WP-CLI tools (15 tools: `wp_cache_*`, `wp_db_*`, etc.) | Docker socket + `CONTAINER` env var | | WordPress Advanced database/system tools | Docker socket + `CONTAINER` env var | +| OpenPanel analytics integration | [OpenPanel](wordpress-plugin/openpanel/) ([Download ZIP](wordpress-plugin/openpanel.zip)) | | WooCommerce tools | WooCommerce plugin (separate `WOOCOMMERCE_` config) | **Docker socket** is needed for WP-CLI and WordPress Advanced system tools. Add to your docker-compose: diff --git a/core/health.py b/core/health.py index 9296049..f828f91 100644 --- a/core/health.py +++ b/core/health.py @@ -9,8 +9,7 @@ This module provides comprehensive health monitoring capabilities including: - Dependency health checks - System uptime tracking -Author: Coolify MCP Team -Version: 7.2 +Author: MCP Hub Team """ import json @@ -24,6 +23,7 @@ from typing import Any from core.audit_log import AuditLogger from core.project_manager import ProjectManager +from core.site_manager import SiteManager logger = logging.getLogger(__name__) @@ -133,6 +133,7 @@ class HealthMonitor: audit_logger: AuditLogger | None = None, metrics_retention_hours: int = 24, max_metrics_per_project: int = 1000, + site_manager: SiteManager | None = None, ): """ Initialize health monitor. @@ -142,8 +143,10 @@ class HealthMonitor: audit_logger: Optional audit logger for logging health events metrics_retention_hours: Hours to retain historical metrics max_metrics_per_project: Maximum metrics to store per project + site_manager: Optional SiteManager for comprehensive site discovery """ self.project_manager = project_manager + self.site_manager = site_manager self.audit_logger = audit_logger self.metrics_retention_hours = metrics_retention_hours self.max_metrics_per_project = max_metrics_per_project @@ -401,9 +404,17 @@ class HealthMonitor: start_time = time.time() try: - # Get plugin instance + # Get plugin instance from ProjectManager plugin = self.project_manager.projects.get(project_id) - if not plugin: + + if plugin: + # Perform health check via plugin instance + health_result = await plugin.health_check() + elif self.site_manager: + # Fallback: site exists in SiteManager but not ProjectManager + # (e.g. WooCommerce uses CONSUMER_KEY, not USERNAME/APP_PASSWORD) + health_result = await self._basic_site_health_check(project_id) + else: return ProjectHealthStatus( project_id=project_id, healthy=False, @@ -413,9 +424,6 @@ class HealthMonitor: recent_errors=["Project not found"], alerts=["CRITICAL: Project not found"], ) - - # Perform health check - health_result = await plugin.health_check() response_time_ms = (time.time() - start_time) * 1000 # Handle both dict and string (JSON) responses @@ -495,6 +503,43 @@ class HealthMonitor: alerts=[f"CRITICAL: Health check failed - {error_msg}"], ) + async def _basic_site_health_check(self, project_id: str) -> dict[str, Any]: + """Basic HTTP health check for sites not in ProjectManager.""" + import aiohttp + + if not self.site_manager: + return {"healthy": False, "message": "SiteManager not available"} + + # Parse plugin_type and site_id from full_id (e.g. "woocommerce_site1") + parts = project_id.split("_", 1) + if len(parts) < 2: + return {"healthy": False, "message": f"Invalid project_id format: {project_id}"} + + plugin_type = parts[0] + site_id = parts[1] + + try: + config = self.site_manager.get_site_config(plugin_type, site_id) + except (KeyError, ValueError): + return {"healthy": False, "message": f"Site not found: {project_id}"} + + url = config.url + if not url: + return {"healthy": False, "message": "No URL configured for site"} + + try: + async with aiohttp.ClientSession() as session: + async with session.get( + url, timeout=aiohttp.ClientTimeout(total=10), ssl=False + ) as resp: + return { + "healthy": resp.status < 500, + "status_code": resp.status, + "message": f"HTTP {resp.status} from {url}", + } + except Exception as e: + return {"healthy": False, "message": f"Connection failed: {e}"} + async def check_all_projects_health(self, include_metrics: bool = True) -> dict[str, Any]: """ Check health of all projects. @@ -507,8 +552,14 @@ class HealthMonitor: """ health_statuses = {} + # Collect all known project/site IDs from both sources + all_project_ids = set(self.project_manager.projects.keys()) + if self.site_manager: + for site_info in self.site_manager.list_all_sites(): + all_project_ids.add(site_info["full_id"]) + # Check each project - for project_id in self.project_manager.projects.keys(): + for project_id in sorted(all_project_ids): status = await self.check_project_health(project_id, include_metrics) health_statuses[project_id] = status.to_dict() @@ -673,7 +724,10 @@ def get_health_monitor() -> HealthMonitor | None: def initialize_health_monitor( - project_manager: ProjectManager, audit_logger: AuditLogger | None = None, **kwargs + project_manager: ProjectManager, + audit_logger: AuditLogger | None = None, + site_manager: SiteManager | None = None, + **kwargs, ) -> HealthMonitor: """ Initialize the global health monitor. @@ -681,11 +735,14 @@ def initialize_health_monitor( Args: project_manager: Project manager instance audit_logger: Optional audit logger + site_manager: Optional SiteManager for comprehensive site discovery **kwargs: Additional configuration options Returns: HealthMonitor instance """ global _health_monitor - _health_monitor = HealthMonitor(project_manager, audit_logger, **kwargs) + _health_monitor = HealthMonitor( + project_manager, audit_logger, site_manager=site_manager, **kwargs + ) return _health_monitor diff --git a/core/templates/base.html b/core/templates/base.html index d804513..cf370c3 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -4,6 +4,7 @@ {% block title %}MCP Hub{% endblock %} + diff --git a/core/templates/dashboard/base.html b/core/templates/dashboard/base.html index b0bceb9..406a3eb 100644 --- a/core/templates/dashboard/base.html +++ b/core/templates/dashboard/base.html @@ -4,6 +4,7 @@ {% block title %}{{ t.dashboard }}{% endblock %} - MCP Hub + diff --git a/core/templates/dashboard/login.html b/core/templates/dashboard/login.html index 0dafedb..0c75ece 100644 --- a/core/templates/dashboard/login.html +++ b/core/templates/dashboard/login.html @@ -4,6 +4,7 @@ {{ t.login_title }} - MCP Hub + diff --git a/core/templates/static/logo.svg b/core/templates/static/logo.svg new file mode 100644 index 0000000..c366a64 --- /dev/null +++ b/core/templates/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/server.py b/server.py index 3b54376..c019357 100644 --- a/server.py +++ b/server.py @@ -1055,6 +1055,7 @@ from core import initialize_health_monitor health_monitor = initialize_health_monitor( project_manager=project_manager, audit_logger=audit_logger, + site_manager=site_manager, metrics_retention_hours=24, max_metrics_per_project=1000, ) @@ -4441,6 +4442,13 @@ def create_multi_endpoint_app(transport: str = "streamable-http"): safe_name = project_id.replace("-", "_").replace(".", "_") routes.append(Mount(mount_path, app=project_app, name=f"mcp_project_{safe_name}")) + # Static files (favicon, logo) + _static_dir = os.path.join(os.path.dirname(__file__), "core", "templates", "static") + if os.path.isdir(_static_dir): + from starlette.staticfiles import StaticFiles + + routes.append(Mount("/static", app=StaticFiles(directory=_static_dir), name="static")) + # Main admin endpoint (must be last - catches all remaining routes) routes.append(Mount("/", app=main_app, name="mcp_admin")) diff --git a/wordpress-plugin/openpanel.zip b/wordpress-plugin/openpanel.zip index 41234313c36f58ea8a4fe28220fbcb6416405eaa..2ed37211898c3849641793c24943bae1bf308369 100644 GIT binary patch literal 11702 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 b?;!u7d6Z-!q5ky>;-3xuPoi|Xe|i50R|QEK delta 11535 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 H&;9-v|E~zz diff --git a/wordpress-plugin/openpanel/openpanel.php b/wordpress-plugin/openpanel/openpanel.php index 08610ab..e3507b9 100644 --- a/wordpress-plugin/openpanel/openpanel.php +++ b/wordpress-plugin/openpanel/openpanel.php @@ -3,11 +3,13 @@ * Plugin Name: OpenPanel * Description: Activate OpenPanel to start tracking your website. Supports both Cloud and Self-Hosted instances. * Version: 1.1.1 - * Author: OpenPanel / Airano + * Plugin URI: https://github.com/airano-ir/mcphub + * Author: OpenPanel / MCP Hub + * Author URI: https://github.com/airano-ir * License: GPLv2 or later * Requires at least: 5.8 * Requires PHP: 7.4 - * Tested up to: 6.8 + * Tested up to: 6.9 * Text Domain: openpanel */ @@ -479,7 +481,7 @@ final class OP_WP_Proxy { // Content-Type is a special header NOT prefixed with HTTP_ in PHP // This is critical for OpenPanel API which requires application/json if (!empty($_SERVER['CONTENT_TYPE'])) { - $headers['Content-Type'] = sanitize_text_field($_SERVER['CONTENT_TYPE']); + $headers['Content-Type'] = sanitize_text_field(wp_unslash($_SERVER['CONTENT_TYPE'])); } foreach ($_SERVER as $name => $value) { diff --git a/wordpress-plugin/openpanel/readme.txt b/wordpress-plugin/openpanel/readme.txt index 21f166c..be9cad1 100644 --- a/wordpress-plugin/openpanel/readme.txt +++ b/wordpress-plugin/openpanel/readme.txt @@ -1,14 +1,14 @@ === OpenPanel === Contributors: openpanel, airano -Tags: analytics, web analytics, privacy-friendly, tracking, proxy, self-hosted +Tags: analytics, privacy-friendly, tracking, self-hosted, proxy Requires at least: 5.8 -Tested up to: 6.8 +Tested up to: 6.9 Requires PHP: 7.4 Stable tag: 1.1.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html -OpenPanel WordPress plugin - Privacy-friendly analytics with ad-blocker resistance. Supports both OpenPanel Cloud and Self-Hosted instances. Inline tracking scripts and proxy API calls through your domain. +Privacy-friendly analytics with ad-blocker resistance. Supports OpenPanel Cloud and Self-Hosted instances. == Description == diff --git a/wordpress-plugin/seo-api-bridge.zip b/wordpress-plugin/seo-api-bridge.zip index 9d13a25a2d0858aa1fa9ec2a6477d3a258f6cba4..346125c4905a256d4a1e910524a61012bbda16c0 100644 GIT binary patch literal 9465 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 N?#@3Ef#zS?e*iPD!7%^; literal 8006 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 echo '

'; - echo '

SEO API Bridge v' . self::VERSION . ': Successfully registered meta fields for ' . implode(' and ', $active_plugins) . '.

'; - echo '

Supported post types: ' . $supported_types . '

'; + echo '

SEO API Bridge v' . esc_html( self::VERSION ) . ': ' . esc_html( sprintf( 'Successfully registered meta fields for %s.', implode( ' and ', $active_plugins ) ) ) . '

'; + echo '

Supported post types: ' . esc_html( $supported_types ) . '

'; if ($woocommerce_active) { echo '

WooCommerce: Detected and supported. Product SEO fields are available via REST API.

';