main.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import logging
  2. from contextlib import asynccontextmanager
  3. from fastapi import FastAPI
  4. from starlette.middleware.cors import CORSMiddleware
  5. from tenacity import retry, stop_after_attempt, wait_fixed, before_log, after_log
  6. from app.api.v1.api import api_router
  7. from app.core.config import settings
  8. from app.core.database import Base, engine
  9. from app.core.cache import redis_client
  10. from app.core.logging_config import setup_logging
  11. from app.core.scheduler import scheduler
  12. from app.services.backup_service import BackupService
  13. from app.core.init_data import fix_user_names
  14. from app.core.database import SessionLocal
  15. # Configure Logging
  16. logger = setup_logging()
  17. # Retry Logic Configuration
  18. max_tries = 60 * 5 # 5 minutes
  19. wait_seconds = 1
  20. @retry(
  21. stop=stop_after_attempt(max_tries),
  22. wait=wait_fixed(wait_seconds),
  23. before=before_log(logger, logging.INFO),
  24. after=after_log(logger, logging.WARN),
  25. )
  26. def init_db() -> None:
  27. try:
  28. # Try to create a connection to check if DB is ready
  29. with engine.connect() as conn:
  30. pass
  31. # Create tables
  32. Base.metadata.create_all(bind=engine)
  33. logger.info("Database connection established and tables created.")
  34. except Exception as e:
  35. logger.error(e)
  36. raise e
  37. @retry(
  38. stop=stop_after_attempt(max_tries),
  39. wait=wait_fixed(wait_seconds),
  40. before=before_log(logger, logging.INFO),
  41. after=after_log(logger, logging.WARN),
  42. )
  43. def check_redis() -> None:
  44. try:
  45. redis_client.ping()
  46. logger.info("Redis connection established.")
  47. except Exception as e:
  48. logger.error(e)
  49. raise e
  50. @asynccontextmanager
  51. async def lifespan(app: FastAPI):
  52. # Startup: Wait for services
  53. logger.info("Waiting for Database and Redis...")
  54. init_db()
  55. check_redis()
  56. # Init Scheduler
  57. logger.info("Starting Scheduler...")
  58. scheduler.start()
  59. try:
  60. with SessionLocal() as db:
  61. # Check and fix user names/english_names
  62. fix_user_names(db)
  63. BackupService.init_scheduler(db)
  64. except Exception as e:
  65. logger.error(f"Failed to init scheduler or fix user names: {e}")
  66. yield
  67. # Shutdown logic
  68. logger.info("Shutting down WebSocket manager...")
  69. from app.core.websocket_manager import manager
  70. await manager.shutdown()
  71. scheduler.shutdown()
  72. logger.info("Shutting down...")
  73. app = FastAPI(
  74. title=settings.PROJECT_NAME,
  75. openapi_url=f"{settings.API_V1_STR}/openapi.json",
  76. docs_url=f"{settings.API_V1_STR}/docs",
  77. redoc_url=f"{settings.API_V1_STR}/redoc",
  78. lifespan=lifespan
  79. )
  80. # Allow all CORS for development simplicity if config is restrictive
  81. origins = [
  82. "http://localhost:5173",
  83. "http://127.0.0.1:5173",
  84. "http://localhost:8000",
  85. "http://127.0.0.1:8000"
  86. ]
  87. # Set all CORS enabled origins
  88. if settings.BACKEND_CORS_ORIGINS:
  89. for origin in settings.BACKEND_CORS_ORIGINS:
  90. origins.append(str(origin))
  91. app.add_middleware(
  92. CORSMiddleware,
  93. allow_origins=origins,
  94. allow_credentials=True,
  95. allow_methods=["*"],
  96. allow_headers=["*"],
  97. expose_headers=["X-New-Token"],
  98. )
  99. app.include_router(api_router, prefix=settings.API_V1_STR)
  100. @app.get("/")
  101. def root():
  102. return {"message": "Welcome to Unified Authentication Platform API"}