main.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #!/usr/bin/env python3
  2. """
  3. Main FastAPI application entry point.
  4. """
  5. from contextlib import asynccontextmanager
  6. from pathlib import Path
  7. import threading
  8. import os
  9. import argparse
  10. from fastapi import FastAPI
  11. from fastapi.staticfiles import StaticFiles
  12. from api.v1 import router as v1_router
  13. from ui import router as ui_router
  14. from core.config import get_settings
  15. from core.startup import initialize_application
  16. from core.logging_config import setup_logging
  17. import ap_git
  18. import metadata_manager
  19. import build_manager
  20. setup_logging()
  21. @asynccontextmanager
  22. async def lifespan(app: FastAPI):
  23. """
  24. Lifespan context manager for startup and shutdown events.
  25. """
  26. # Startup
  27. settings = get_settings()
  28. initialize_application(settings.base_dir)
  29. repo = ap_git.GitRepo.clone_if_needed(
  30. source=settings.ap_git_url,
  31. dest=settings.source_dir,
  32. recurse_submodules=True,
  33. )
  34. vehicles_manager = metadata_manager.VehiclesManager()
  35. ap_src_metadata_fetcher = metadata_manager.APSourceMetadataFetcher(
  36. ap_repo=repo,
  37. caching_enabled=True,
  38. redis_host=settings.redis_host,
  39. redis_port=settings.redis_port,
  40. )
  41. versions_fetcher = metadata_manager.VersionsFetcher(
  42. remotes_json_path=settings.remotes_json_path,
  43. ap_repo=repo
  44. )
  45. versions_fetcher.reload_remotes_json()
  46. build_mgr = build_manager.BuildManager(
  47. outdir=settings.outdir_parent,
  48. redis_host=settings.redis_host,
  49. redis_port=settings.redis_port
  50. )
  51. cleaner = build_manager.BuildArtifactsCleaner()
  52. progress_updater = build_manager.BuildProgressUpdater()
  53. inbuilt_builder = None
  54. inbuilt_builder_thread = None
  55. if settings.enable_inbuilt_builder:
  56. from builder.builder import Builder # noqa: E402
  57. inbuilt_builder = Builder(
  58. workdir=settings.workdir_parent,
  59. source_repo=repo
  60. )
  61. inbuilt_builder_thread = threading.Thread(
  62. target=inbuilt_builder.run,
  63. daemon=True
  64. )
  65. inbuilt_builder_thread.start()
  66. versions_fetcher.start()
  67. cleaner.start()
  68. progress_updater.start()
  69. app.state.repo = repo
  70. app.state.ap_src_metadata_fetcher = ap_src_metadata_fetcher
  71. app.state.versions_fetcher = versions_fetcher
  72. app.state.vehicles_manager = vehicles_manager
  73. app.state.build_manager = build_mgr
  74. app.state.inbuilt_builder = inbuilt_builder
  75. app.state.inbuilt_builder_thread = inbuilt_builder_thread
  76. yield
  77. # Shutdown
  78. versions_fetcher.stop()
  79. cleaner.stop()
  80. progress_updater.stop()
  81. if inbuilt_builder is not None:
  82. inbuilt_builder.shutdown()
  83. if (inbuilt_builder_thread is not None and
  84. inbuilt_builder_thread.is_alive()):
  85. inbuilt_builder_thread.join()
  86. # Create FastAPI application
  87. app = FastAPI(
  88. title="CustomBuild API",
  89. description="API for ArduPilot Custom Firmware Builder",
  90. version="1.0.0",
  91. docs_url="/api/docs",
  92. redoc_url="/api/redoc",
  93. lifespan=lifespan,
  94. )
  95. # Mount static files
  96. WEB_ROOT = Path(__file__).resolve().parent
  97. app.mount(
  98. "/static",
  99. StaticFiles(directory=str(WEB_ROOT / "static")),
  100. name="static"
  101. )
  102. # Include API v1 router
  103. app.include_router(v1_router, prefix="/api")
  104. # Include Web UI router
  105. app.include_router(ui_router)
  106. @app.get("/health")
  107. async def health_check():
  108. """Health check endpoint."""
  109. return {"status": "healthy"}
  110. if __name__ == "__main__":
  111. parser = argparse.ArgumentParser(description="CustomBuild API Server")
  112. parser.add_argument(
  113. "--port",
  114. type=int,
  115. default=int(os.getenv("WEB_PORT", 8080)),
  116. help="Port to run the server on (default: 8080 or WEB_PORT env var)"
  117. )
  118. args = parser.parse_args()
  119. import uvicorn
  120. uvicorn.run(
  121. "main:app",
  122. host="0.0.0.0",
  123. port=args.port,
  124. reload=True
  125. )