main.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """
  2. FastAPI application entry point.
  3. """
  4. from fastapi import FastAPI
  5. from fastapi.middleware.cors import CORSMiddleware
  6. from app.config import settings
  7. from app.core.http_metrics import HTTPMetricsMiddleware
  8. # Create FastAPI app
  9. app = FastAPI(
  10. title=settings.PROJECT_NAME,
  11. version="0.1.0",
  12. description="MyBeacon Backend API - Modular BLE/WiFi monitoring platform",
  13. docs_url="/docs",
  14. redoc_url="/redoc",
  15. )
  16. # Configure CORS
  17. app.add_middleware(
  18. CORSMiddleware,
  19. allow_origins=settings.cors_origins_list,
  20. allow_credentials=True,
  21. allow_methods=["*"],
  22. allow_headers=["*"],
  23. )
  24. # Add HTTP metrics middleware
  25. app.add_middleware(HTTPMetricsMiddleware)
  26. @app.get("/")
  27. async def root():
  28. """Root endpoint - API info."""
  29. return {
  30. "name": settings.PROJECT_NAME,
  31. "version": "0.1.0",
  32. "status": "running",
  33. }
  34. @app.get("/health")
  35. async def health_check():
  36. """Health check endpoint."""
  37. return {"status": "healthy"}
  38. # Include routers
  39. from app.api.v1 import router as api_v1_router
  40. app.include_router(api_v1_router, prefix=settings.API_V1_PREFIX)
  41. # Background task for tunnel watchdog with DB updates
  42. async def tunnel_watchdog_with_db():
  43. """
  44. Tunnel watchdog: cleanup orphaned ttyd and stale sessions
  45. Runs independently of in-memory state and survives restarts
  46. """
  47. import asyncio
  48. from app.services.tunnel_service import tunnel_service
  49. from app.core.database import async_session_maker
  50. from app.models.device import Device
  51. from sqlalchemy import select
  52. from sqlalchemy.orm import attributes
  53. while True:
  54. await asyncio.sleep(60) # Every minute
  55. # Run watchdog and get list of tunnels to disable
  56. tunnels_to_disable = await tunnel_service.watchdog_cleanup()
  57. if tunnels_to_disable:
  58. print(f"[watchdog] Disabling {len(tunnels_to_disable)} tunnels in device config")
  59. async with async_session_maker() as db:
  60. for device_mac, tunnel_type in tunnels_to_disable:
  61. # Find device by MAC address
  62. result = await db.execute(
  63. select(Device).where(Device.mac_address == device_mac)
  64. )
  65. device = result.scalar_one_or_none()
  66. if device and device.config:
  67. tunnel_key = f"{tunnel_type}_tunnel"
  68. if tunnel_key in device.config:
  69. # Disable tunnel
  70. device.config[tunnel_key]["enabled"] = False
  71. attributes.flag_modified(device, "config")
  72. print(f"[watchdog] Disabled {tunnel_type} tunnel for {device_mac}")
  73. await db.commit()
  74. # Startup event
  75. @app.on_event("startup")
  76. async def startup_event():
  77. """Initialize services on startup"""
  78. # Start tunnel watchdog background task
  79. import asyncio
  80. asyncio.create_task(tunnel_watchdog_with_db())
  81. print("[startup] Tunnel watchdog started (checks every 60s)")
  82. # Start host monitoring background task
  83. from app.services.host_monitor import host_monitor
  84. asyncio.create_task(host_monitor.run_monitoring_loop())
  85. print("[startup] Host monitoring task started")
  86. # Shutdown event
  87. @app.on_event("shutdown")
  88. async def shutdown_event():
  89. """Cleanup on shutdown"""
  90. from app.services.host_monitor import host_monitor
  91. await host_monitor.stop()
  92. print("[shutdown] Host monitoring stopped")