feat: v3.1.0 — Live Platform Foundation (Track E.1-E.3)
Major release introducing the Live Platform architecture:
- SQLite database backend with async operations and migrations (E.1)
- AES-256-GCM credential encryption with HKDF key derivation (E.1)
- OAuth Social Login with GitHub and Google (E.2)
- Site management API with encrypted credential storage (E.3)
- Per-user MCP endpoints at /u/{user_id}/{alias}/mcp (E.3)
- User API keys with bcrypt hashing (E.3)
- Auto-generated config snippets for 5 MCP clients (E.3)
- Dashboard: dark/light mode, RBAC, My Sites, Connect page
- Active background health checks
- 452 tests (up from 303), all passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ This module provides comprehensive health monitoring capabilities including:
|
||||
Author: MCP Hub Team
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
@@ -175,6 +176,11 @@ class HealthMonitor:
|
||||
# Request rate tracking (for requests per minute)
|
||||
self.request_timestamps: deque = deque(maxlen=1000)
|
||||
|
||||
# Active background checks
|
||||
self.latest_health_status: dict[str, ProjectHealthStatus] = {}
|
||||
self._bg_task: asyncio.Task | None = None
|
||||
self._is_running = False
|
||||
|
||||
logger.info("HealthMonitor initialized")
|
||||
|
||||
def _setup_default_thresholds(self):
|
||||
@@ -470,7 +476,7 @@ class HealthMonitor:
|
||||
}
|
||||
alerts = self._check_alerts(project_id, alert_check_data)
|
||||
|
||||
return ProjectHealthStatus(
|
||||
status = ProjectHealthStatus(
|
||||
project_id=project_id,
|
||||
healthy=is_healthy,
|
||||
last_check=datetime.now(UTC),
|
||||
@@ -480,6 +486,8 @@ class HealthMonitor:
|
||||
alerts=alerts,
|
||||
details=health_result,
|
||||
)
|
||||
self.latest_health_status[project_id] = status
|
||||
return status
|
||||
|
||||
except Exception as e:
|
||||
response_time_ms = (time.time() - start_time) * 1000
|
||||
@@ -493,7 +501,7 @@ class HealthMonitor:
|
||||
error_message=error_msg,
|
||||
)
|
||||
|
||||
return ProjectHealthStatus(
|
||||
status = ProjectHealthStatus(
|
||||
project_id=project_id,
|
||||
healthy=False,
|
||||
last_check=datetime.now(UTC),
|
||||
@@ -502,6 +510,8 @@ class HealthMonitor:
|
||||
recent_errors=[error_msg],
|
||||
alerts=[f"CRITICAL: Health check failed - {error_msg}"],
|
||||
)
|
||||
self.latest_health_status[project_id] = status
|
||||
return status
|
||||
|
||||
def _find_site_info(self, project_id: str) -> dict[str, Any] | None:
|
||||
"""Find site info from SiteManager by full_id."""
|
||||
@@ -762,7 +772,45 @@ class HealthMonitor:
|
||||
self.failed_requests = 0
|
||||
self.response_times.clear()
|
||||
self.request_timestamps.clear()
|
||||
self.latest_health_status.clear()
|
||||
logger.warning("All metrics have been reset")
|
||||
|
||||
async def start_background_checks(self, interval_seconds: int = 60):
|
||||
"""Start background health checks for all projects."""
|
||||
if self._is_running:
|
||||
return
|
||||
|
||||
self._is_running = True
|
||||
logger.info(f"Starting background health checks every {interval_seconds} seconds")
|
||||
|
||||
async def _loop():
|
||||
# Initial wait to let server start up fully
|
||||
await asyncio.sleep(5)
|
||||
while self._is_running:
|
||||
try:
|
||||
await self.check_all_projects_health(include_metrics=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in background health check loop: {e}")
|
||||
|
||||
# Sleep interval, check _is_running periodically
|
||||
for _ in range(interval_seconds):
|
||||
if not self._is_running:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
self._bg_task = asyncio.create_task(_loop())
|
||||
|
||||
async def stop_background_checks(self):
|
||||
"""Stop background health checks."""
|
||||
self._is_running = False
|
||||
if self._bg_task:
|
||||
self._bg_task.cancel()
|
||||
try:
|
||||
await self._bg_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
self._bg_task = None
|
||||
logger.info("Background health checks stopped")
|
||||
|
||||
|
||||
# Singleton instance
|
||||
|
||||
Reference in New Issue
Block a user