main.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. """FastAPI application entry point."""
  2. import os
  3. import sys
  4. # Make the repository root importable so the single-source plan schema
  5. # (src.plan_schema) resolves when the backend runs from a checkout.
  6. _BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  7. _REPO_ROOT = os.path.normpath(os.path.join(_BACKEND_DIR, "..", ".."))
  8. if _REPO_ROOT not in sys.path:
  9. sys.path.insert(0, _REPO_ROOT)
  10. from fastapi import FastAPI, Request, HTTPException
  11. from fastapi.middleware.cors import CORSMiddleware
  12. from .config import APP_NAME, APP_VERSION, APP_DESCRIPTION, CORS_ORIGINS
  13. from .database import init_db
  14. from .routers import projects, plans, experience, generation, analytics, ai, search, ai_plan, analysis, adaptive, tasks, monitor, reports, executor_monitor
  15. app = FastAPI(
  16. title=APP_NAME,
  17. version=APP_VERSION,
  18. description=APP_DESCRIPTION,
  19. )
  20. # CORS middleware (allow frontend dev server)
  21. app.add_middleware(
  22. CORSMiddleware,
  23. allow_origins=CORS_ORIGINS,
  24. allow_credentials=True,
  25. allow_methods=["*"],
  26. allow_headers=["*"],
  27. )
  28. # E4 fix: optional API key authentication middleware.
  29. # Enabled only when AFM_API_KEY is set in environment.
  30. _API_KEY = os.environ.get("AFM_API_KEY", "").strip()
  31. _PUBLIC_PATHS = ("/api/health", "/docs", "/openapi.json", "/redoc", "/")
  32. @app.middleware("http")
  33. async def api_key_auth(request: Request, call_next):
  34. """Optional API key authentication (E4 fix)."""
  35. if not _API_KEY:
  36. return await call_next(request)
  37. path = request.url.path
  38. if any(path.startswith(p) for p in _PUBLIC_PATHS):
  39. return await call_next(request)
  40. provided = request.headers.get("X-API-Key", "")
  41. if provided != _API_KEY:
  42. raise HTTPException(status_code=401, detail="Invalid or missing API key")
  43. return await call_next(request)
  44. # Include routers
  45. app.include_router(projects.router)
  46. app.include_router(plans.router)
  47. app.include_router(experience.router)
  48. app.include_router(generation.router)
  49. app.include_router(analytics.router)
  50. app.include_router(ai.router)
  51. app.include_router(search.router)
  52. app.include_router(ai_plan.router)
  53. app.include_router(analysis.router)
  54. app.include_router(adaptive.router)
  55. app.include_router(tasks.router)
  56. app.include_router(monitor.router)
  57. app.include_router(reports.router)
  58. app.include_router(executor_monitor.router)
  59. @app.on_event("startup")
  60. def on_startup():
  61. """Initialize database on startup."""
  62. init_db()
  63. @app.get("/api/health")
  64. def health_check():
  65. """Health check endpoint."""
  66. return {"status": "ok", "app": APP_NAME, "version": APP_VERSION}
  67. @app.get("/")
  68. def root():
  69. """Root endpoint - redirect to API docs."""
  70. return {
  71. "message": APP_NAME,
  72. "version": APP_VERSION,
  73. "docs": "/docs",
  74. "api_health": "/api/health",
  75. }