main.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """FastAPI 应用入口(骨架工程)"""
  2. from contextlib import asynccontextmanager
  3. import logging
  4. from fastapi import FastAPI
  5. from fastapi.middleware.cors import CORSMiddleware
  6. from app.core.config import settings
  7. from app.core.database import SessionLocal
  8. from app.core.api_request_log_middleware import ApiRequestLogMiddleware
  9. from app.core.api_crypto_middleware import ApiCryptoMiddleware
  10. from app.models import models # noqa: F401
  11. from app.api import public_router, menu_router, admin_router, workflow_router, basic_info_router, collection_protocol_router, leave_router, operations_router, ethnic_router, sample_reception_router, qc_router, research_donation_router, production_router, pilot_service_router, insurance_company_router, material_router, storage_release_review_router, sample_sms_notification_router, storage_entry_sms_notification_router
  12. @asynccontextmanager
  13. async def lifespan(app: FastAPI):
  14. """应用生命周期管理"""
  15. from app.core import login_crypto
  16. login_crypto.ensure_initialized()
  17. from app.tasks import init_scheduled_tasks
  18. try:
  19. init_scheduled_tasks(SessionLocal)
  20. except Exception as e:
  21. logging.warning("定时任务调度器启动失败: %s", e)
  22. yield
  23. from app.tasks import shutdown_scheduler
  24. try:
  25. shutdown_scheduler()
  26. except Exception:
  27. pass
  28. app = FastAPI(
  29. title=settings.APP_NAME,
  30. description="zkxw 管理端骨架后端(鉴权与菜单)",
  31. version="1.0.0",
  32. docs_url="/docs",
  33. redoc_url="/redoc",
  34. lifespan=lifespan,
  35. )
  36. _cors_kw: dict = {
  37. "allow_origins": settings.BACKEND_CORS_ORIGINS,
  38. "allow_credentials": True,
  39. "allow_methods": ["*"],
  40. "allow_headers": ["*"],
  41. }
  42. if settings.DEBUG:
  43. _cors_kw["allow_origin_regex"] = r"^https?://(localhost|127\.0\.0\.1)(:\d+)?$"
  44. app.add_middleware(CORSMiddleware, **_cors_kw)
  45. app.add_middleware(ApiRequestLogMiddleware)
  46. app.add_middleware(ApiCryptoMiddleware)
  47. app.include_router(public_router, prefix=settings.API_V1_PREFIX)
  48. app.include_router(menu_router, prefix=settings.API_V1_PREFIX)
  49. app.include_router(admin_router, prefix=settings.API_V1_PREFIX)
  50. app.include_router(workflow_router, prefix=settings.API_V1_PREFIX)
  51. app.include_router(basic_info_router, prefix=settings.API_V1_PREFIX)
  52. app.include_router(collection_protocol_router, prefix=settings.API_V1_PREFIX)
  53. app.include_router(leave_router, prefix=settings.API_V1_PREFIX)
  54. app.include_router(operations_router, prefix=settings.API_V1_PREFIX)
  55. app.include_router(ethnic_router, prefix=settings.API_V1_PREFIX)
  56. app.include_router(sample_reception_router, prefix=settings.API_V1_PREFIX)
  57. app.include_router(qc_router, prefix=settings.API_V1_PREFIX)
  58. app.include_router(research_donation_router, prefix=settings.API_V1_PREFIX)
  59. app.include_router(production_router, prefix=settings.API_V1_PREFIX)
  60. app.include_router(pilot_service_router, prefix=settings.API_V1_PREFIX)
  61. app.include_router(insurance_company_router, prefix=settings.API_V1_PREFIX)
  62. app.include_router(material_router, prefix=settings.API_V1_PREFIX)
  63. app.include_router(storage_release_review_router, prefix=settings.API_V1_PREFIX)
  64. app.include_router(sample_sms_notification_router, prefix=settings.API_V1_PREFIX)
  65. app.include_router(storage_entry_sms_notification_router, prefix=settings.API_V1_PREFIX)
  66. logger = logging.getLogger("sqxp")
  67. def _mask_secret(value: str) -> str:
  68. if not value:
  69. return "(empty)"
  70. if len(value) <= 6:
  71. return "*" * len(value)
  72. return f"{value[:3]}***{value[-3:]}"
  73. _api_crypto_boot_msg = (
  74. "API_CRYPTO_BOOT "
  75. f"enabled={settings.API_ENCRYPT_ENABLED} "
  76. f"fail_open={settings.API_ENCRYPT_FAIL_OPEN} "
  77. f"fixed_secret={_mask_secret(settings.API_ENCRYPT_FIXED_SECRET)} "
  78. f"ts_skew_sec={settings.API_ENCRYPT_TS_SKEW_SEC} "
  79. f"nonce_ttl_sec={settings.API_ENCRYPT_NONCE_TTL_SEC} "
  80. f"exclude_count={len([x for x in (settings.API_ENCRYPT_EXCLUDE_PATHS or '').split(',') if x.strip()])}"
  81. )
  82. print(_api_crypto_boot_msg)
  83. logger.info(_api_crypto_boot_msg)
  84. @app.get("/")
  85. def root():
  86. return {"name": settings.APP_NAME, "version": "1.0.0", "docs": "/docs"}
  87. @app.get("/health")
  88. def health_check():
  89. return {"status": "healthy"}