jira-ai-fixer/app/main.py

96 lines
3.0 KiB
Python

"""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")