"""JIRA AI Fixer - Enterprise Issue Analysis Platform.""" from contextlib import asynccontextmanager from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from starlette.middleware.base import BaseHTTPMiddleware import os from app.core.config import settings from app.core.database import init_db from app.api import api_router class HTTPSRedirectMiddleware(BaseHTTPMiddleware): """Force HTTPS in redirects when behind reverse proxy.""" async def dispatch(self, request: Request, call_next): response = await call_next(request) if response.status_code in (301, 302, 303, 307, 308): location = response.headers.get("location", "") if location.startswith("http://"): response.headers["location"] = location.replace("http://", "https://", 1) return response @asynccontextmanager async def lifespan(app: FastAPI): await init_db() yield app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="Enterprise AI-powered issue analysis and automated fix generation", docs_url="/api/docs", redoc_url="/api/redoc", openapi_url="/api/openapi.json", lifespan=lifespan ) app.add_middleware(HTTPSRedirectMiddleware) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # FIRST: API routes (highest priority) app.include_router(api_router, prefix="/api") # Health check (explicit, not in router) @app.get("/api/health") async def health(): return { "status": "healthy", "service": "jira-ai-fixer", "version": settings.APP_VERSION } # SECOND: Static files FRONTEND_DIR = "/app/frontend" ASSETS_DIR = f"{FRONTEND_DIR}/assets" if os.path.exists(ASSETS_DIR): app.mount("/assets", StaticFiles(directory=ASSETS_DIR), name="assets") # THIRD: Frontend routes (AFTER API) @app.get("/") async def serve_root(): if os.path.exists(f"{FRONTEND_DIR}/index.html"): return FileResponse(f"{FRONTEND_DIR}/index.html") return { "service": settings.APP_NAME, "version": settings.APP_VERSION, "docs": "/api/docs", "health": "/api/health" } # LAST: SPA catch-all (exclude api/*) @app.get("/{path:path}", include_in_schema=False) async def serve_spa(path: str): # NEVER capture API routes if path.startswith("api"): raise HTTPException(status_code=404, detail="API route not found") # Try to serve static file file_path = f"{FRONTEND_DIR}/{path}" if os.path.exists(file_path) and os.path.isfile(file_path): return FileResponse(file_path) # Fallback to index.html for SPA routing if os.path.exists(f"{FRONTEND_DIR}/index.html"): return FileResponse(f"{FRONTEND_DIR}/index.html") raise HTTPException(status_code=404, detail="Not found")