main.py 3.4 KB

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