| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- """
- FastAPI application entry point.
- """
- from fastapi import FastAPI
- from fastapi.middleware.cors import CORSMiddleware
- from app.config import settings
- from app.core.http_metrics import HTTPMetricsMiddleware
- # Create FastAPI app
- app = FastAPI(
- title=settings.PROJECT_NAME,
- version="0.1.0",
- description="MyBeacon Backend API - Modular BLE/WiFi monitoring platform",
- docs_url="/docs",
- redoc_url="/redoc",
- )
- # Configure CORS
- app.add_middleware(
- CORSMiddleware,
- allow_origins=settings.cors_origins_list,
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
- )
- # Add HTTP metrics middleware
- app.add_middleware(HTTPMetricsMiddleware)
- @app.get("/")
- async def root():
- """Root endpoint - API info."""
- return {
- "name": settings.PROJECT_NAME,
- "version": "0.1.0",
- "status": "running",
- }
- @app.get("/health")
- async def health_check():
- """Health check endpoint."""
- return {"status": "healthy"}
- # Include routers
- from app.api.v1 import router as api_v1_router
- app.include_router(api_v1_router, prefix=settings.API_V1_PREFIX)
- # Background task for tunnel watchdog with DB updates
- async def tunnel_watchdog_with_db():
- """
- Tunnel watchdog: cleanup orphaned ttyd and stale sessions
- Runs independently of in-memory state and survives restarts
- """
- import asyncio
- from app.services.tunnel_service import tunnel_service
- from app.core.database import async_session_maker
- from app.models.device import Device
- from sqlalchemy import select
- from sqlalchemy.orm import attributes
- while True:
- await asyncio.sleep(60) # Every minute
- # Run watchdog and get list of tunnels to disable
- tunnels_to_disable = await tunnel_service.watchdog_cleanup()
- if tunnels_to_disable:
- print(f"[watchdog] Disabling {len(tunnels_to_disable)} tunnels in device config")
- async with async_session_maker() as db:
- for device_mac, tunnel_type in tunnels_to_disable:
- # Find device by MAC address
- result = await db.execute(
- select(Device).where(Device.mac_address == device_mac)
- )
- device = result.scalar_one_or_none()
- if device and device.config:
- tunnel_key = f"{tunnel_type}_tunnel"
- if tunnel_key in device.config:
- # Disable tunnel
- device.config[tunnel_key]["enabled"] = False
- attributes.flag_modified(device, "config")
- print(f"[watchdog] Disabled {tunnel_type} tunnel for {device_mac}")
- await db.commit()
- # Startup event
- @app.on_event("startup")
- async def startup_event():
- """Initialize services on startup"""
- # Start tunnel watchdog background task
- import asyncio
- asyncio.create_task(tunnel_watchdog_with_db())
- print("[startup] Tunnel watchdog started (checks every 60s)")
- # Start host monitoring background task
- from app.services.host_monitor import host_monitor
- asyncio.create_task(host_monitor.run_monitoring_loop())
- print("[startup] Host monitoring task started")
- # Shutdown event
- @app.on_event("shutdown")
- async def shutdown_event():
- """Cleanup on shutdown"""
- from app.services.host_monitor import host_monitor
- await host_monitor.stop()
- print("[shutdown] Host monitoring stopped")
|