feat: container monitoring backend core (PR-1)

- Add instance_events and health_checks tables with Alembic migration
- InstanceEventBus: typed pub/sub singleton with wildcard support
- HealthMonitor: async background loop polling containers every 15s
- SSE endpoint GET /events/stream with auth and connection limits
- Lifecycle hooks in tool_instances.py (create/start/stop/restart/delete)
- Structured JSON logging with correlation IDs
- 15 new unit tests (EventBus, HealthMonitor, MonitoringModels)

Quality gates: pytest 15 new passed, ruff clean
This commit is contained in:
2026-05-28 23:09:27 +02:00
parent 0fdbef578f
commit 4a7f24348c
26 changed files with 4142 additions and 11 deletions
+23
View File
@@ -9,6 +9,7 @@ from fastapi.staticfiles import StaticFiles
from src.api.auth import router as auth_router
from src.api.dashboard import router as dashboard_router
from src.api.events import router as events_router
from src.api.git_repositories import router as git_repositories_router
from src.api.health import router as health_router
from src.api.projects import router as projects_router
@@ -30,6 +31,9 @@ from src.logging_config import (
RequestLoggingMiddleware,
configure_logging,
)
from src.services.correlation import CorrelationIdMiddleware
from src.services.event_bus import InstanceEventBus
from src.services.health_monitor import HealthMonitor
# Configure logging early
log_level = os.getenv("LOG_LEVEL", "INFO").upper()
@@ -54,6 +58,7 @@ app.add_middleware(
allow_headers=["*"],
)
app.add_middleware(CorrelationIdMiddleware)
app.add_middleware(RequestLoggingMiddleware)
app.add_middleware(ExceptionLoggingMiddleware)
@@ -103,6 +108,11 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
)
# Global services
_event_bus = InstanceEventBus()
_health_monitor = HealthMonitor(_event_bus)
@app.on_event("startup")
async def on_startup():
logger.info("Starting up Headquarter API...")
@@ -115,9 +125,21 @@ async def on_startup():
sys.exit(1)
# Start background health monitor
_health_monitor.start()
logger.info("Health monitor started")
logger.info("Startup complete.")
@app.on_event("shutdown")
async def on_shutdown():
logger.info("Shutting down Headquarter API...")
_health_monitor.stop()
logger.info("Health monitor stopped")
logger.info("Shutdown complete.")
app.include_router(health_router)
app.include_router(auth_router)
app.include_router(dashboard_router)
@@ -133,4 +155,5 @@ app.include_router(tool_instances_router)
app.include_router(sessions_router)
app.include_router(instance_proxy_router)
app.include_router(terminal_router)
app.include_router(events_router)
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")