init_app.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. from collections.abc import AsyncGenerator
  2. from typing import Any
  3. from fastapi import Depends, FastAPI
  4. from fastapi.concurrency import asynccontextmanager
  5. from fastapi.openapi.docs import (
  6. get_redoc_html,
  7. get_swagger_ui_html,
  8. get_swagger_ui_oauth2_redirect_html,
  9. )
  10. from fastapi.responses import HTMLResponse
  11. from fastapi.staticfiles import StaticFiles
  12. from fastapi_limiter import FastAPILimiter
  13. from fastapi_limiter.depends import RateLimiter, WebSocketRateLimiter
  14. from app.config.setting import settings
  15. from app.core.docs import get_custom_ui_html
  16. from app.core.exceptions import handle_exception
  17. from app.core.http_limit import http_limit_callback, ws_limit_callback
  18. from app.core.logger import log
  19. from app.utils.common_util import import_module, import_modules_async
  20. from app.utils.console import console_close, console_run
  21. from .initialize import InitializeData
  22. @asynccontextmanager
  23. async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]:
  24. """
  25. 自定义 FastAPI 应用生命周期。
  26. 参数:
  27. - app (FastAPI): FastAPI 应用实例。
  28. 返回:
  29. - AsyncGenerator[Any, Any]: 生命周期上下文生成器。
  30. """
  31. from app.api.v1.module_system.dict.service import DictDataService
  32. from app.api.v1.module_system.params.service import ParamsService
  33. from app.core.ap_scheduler import SchedulerUtil
  34. try:
  35. await InitializeData().init_db()
  36. log.info(f"✅ {settings.DATABASE_TYPE}数据库初始化完成")
  37. await import_modules_async(
  38. modules=settings.EVENT_LIST, desc="全局事件", app=app, status=True
  39. )
  40. log.info("✅ 全局事件模块加载完成")
  41. await ParamsService().init_config_service(redis=app.state.redis)
  42. log.info("✅ Redis系统配置初始化完成")
  43. await DictDataService().init_dict_service(redis=app.state.redis)
  44. log.info("✅ Redis数据字典初始化完成")
  45. # await SchedulerUtil.init_scheduler(redis=app.state.redis)
  46. # log.info("✅ 定时任务调度器初始化完成")
  47. await FastAPILimiter.init(
  48. redis=app.state.redis,
  49. prefix=settings.REQUEST_LIMITER_REDIS_PREFIX,
  50. http_callback=http_limit_callback,
  51. ws_callback=ws_limit_callback,
  52. )
  53. log.info("✅ 请求限流器初始化完成")
  54. # 导入并显示最终的启动信息面板
  55. from app.common.enums import EnvironmentEnum
  56. console_run(
  57. host=settings.SERVER_HOST,
  58. port=settings.SERVER_PORT,
  59. reload=settings.ENVIRONMENT == EnvironmentEnum.DEV,
  60. database_ready=True,
  61. redis_ready=True,
  62. scheduler_ready=SchedulerUtil.is_running(),
  63. limiter_ready=True,
  64. )
  65. except Exception as e:
  66. log.error(f"❌ 应用初始化失败: {e!s}")
  67. raise SystemExit(1)
  68. yield
  69. try:
  70. await SchedulerUtil.shutdown(wait=False)
  71. log.info("✅ 定时任务调度器已关闭")
  72. await FastAPILimiter.close()
  73. log.info("✅ 请求限制器已关闭")
  74. await import_modules_async(modules=settings.EVENT_LIST, desc="全局事件", app=app, status=False)
  75. log.info("✅ 全局事件模块卸载完成")
  76. console_close()
  77. except Exception as e:
  78. log.error(f"❌ 应用关闭过程中发生错误: {e!s}")
  79. def register_middlewares(app: FastAPI) -> None:
  80. """
  81. 注册全局中间件。
  82. 参数:
  83. - app (FastAPI): FastAPI 应用实例。
  84. 返回:
  85. - None
  86. """
  87. for middleware in settings.MIDDLEWARE_LIST[::-1]:
  88. if not middleware:
  89. continue
  90. middleware = import_module(middleware, desc="中间件")
  91. app.add_middleware(middleware)
  92. def register_exceptions(app: FastAPI) -> None:
  93. """
  94. 统一注册异常处理器。
  95. 参数:
  96. - app (FastAPI): FastAPI 应用实例。
  97. 返回:
  98. - None
  99. """
  100. handle_exception(app)
  101. def register_routers(app: FastAPI) -> None:
  102. """
  103. 注册根路由。
  104. 参数:
  105. - app (FastAPI): FastAPI 应用实例。
  106. 返回:
  107. - None
  108. """
  109. from app.api.v1.module_application import application_router
  110. from app.api.v1.module_common import common_router
  111. from app.api.v1.module_monitor import monitor_router
  112. from app.api.v1.module_system import system_router
  113. app.include_router(common_router, dependencies=[Depends(RateLimiter(times=5, seconds=10))])
  114. app.include_router(application_router, dependencies=[Depends(RateLimiter(times=5, seconds=10))])
  115. app.include_router(system_router, dependencies=[Depends(RateLimiter(times=5, seconds=10))])
  116. app.include_router(monitor_router, dependencies=[Depends(RateLimiter(times=5, seconds=10))])
  117. # 先将动态路由注册到应用,使用速率限制器
  118. from app.core.discover import get_dynamic_router
  119. # 获取动态路由实例
  120. app.include_router(
  121. router=get_dynamic_router(),
  122. dependencies=[Depends(RateLimiter(times=5, seconds=10))],
  123. )
  124. def register_files(app: FastAPI) -> None:
  125. """
  126. 注册静态资源挂载和文件相关配置。
  127. 参数:
  128. - app (FastAPI): FastAPI 应用实例。
  129. 返回:
  130. - None
  131. """
  132. # 挂载静态文件目录
  133. if settings.STATIC_ENABLE:
  134. # 确保静态资源根目录存在
  135. settings.STATIC_ROOT.mkdir(parents=True, exist_ok=True)
  136. app.mount(
  137. path=settings.STATIC_URL,
  138. app=StaticFiles(directory=settings.STATIC_ROOT),
  139. name=settings.STATIC_DIR,
  140. )
  141. def reset_api_docs(app: FastAPI) -> None:
  142. """
  143. 使用本地静态资源自定义 API 文档页面(Swagger UI 与 ReDoc)。
  144. 参数:
  145. - app (FastAPI): FastAPI 应用实例。
  146. 返回:
  147. - None
  148. """
  149. @app.get(str(app.swagger_ui_oauth2_redirect_url), include_in_schema=False)
  150. async def swagger_ui_redirect():
  151. return get_swagger_ui_oauth2_redirect_html()
  152. @app.get(settings.DOCS_URL, include_in_schema=False)
  153. async def custom_swagger_ui_html() -> HTMLResponse:
  154. return get_swagger_ui_html(
  155. openapi_url=str(app.root_path) + str(app.openapi_url),
  156. title=app.title + " - Swagger UI",
  157. oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
  158. swagger_js_url=settings.SWAGGER_JS_URL,
  159. swagger_css_url=settings.SWAGGER_CSS_URL,
  160. swagger_favicon_url=settings.FAVICON_URL,
  161. )
  162. @app.get(settings.REDOC_URL, include_in_schema=False)
  163. async def custom_redoc_html():
  164. return get_redoc_html(
  165. openapi_url=str(app.root_path) + str(app.openapi_url),
  166. title=app.title + " - ReDoc",
  167. redoc_js_url=settings.REDOC_JS_URL,
  168. redoc_favicon_url=settings.FAVICON_URL,
  169. )
  170. @app.get(settings.LJDOC_URL, include_in_schema=False)
  171. async def custom_ui_html():
  172. return get_custom_ui_html(
  173. openapi_url=str(app.root_path) + str(app.openapi_url),
  174. title=app.title + " - LangJin UI",
  175. swagger_js_url=settings.CUSTOM_JS_URL,
  176. swagger_css_url=settings.CUSTOM_CSS_URL,
  177. swagger_favicon_url=settings.FAVICON_URL
  178. )