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 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-18 21:11:49 +03:30
parent 91979ee18d
commit bb851e4be2
14 changed files with 97 additions and 25 deletions

View File

@@ -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