File size: 6,283 Bytes
a83c934
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
"""FastAPI application entry point

Main application setup with middleware, CORS, and route configuration.
"""
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
# Temporarily disabled for debugging
from secure import Secure

# Secure middleware configuration
# Temporarily disabled for debugging
secure_headers = Secure()
from fastapi.responses import JSONResponse
# Temporarily disabled for debugging
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

from src.config.settings import settings
from src.config.database import init_qdrant_collection, close_database_connections
from src.utils.logger import setup_logging, get_logger
from src.api.routes import health, auth, chat

# Setup logging
setup_logging(
    level=settings.log_level,
    use_json=settings.is_production
)

logger = get_logger(__name__)


# Rate limiter configuration (using the one from rate_limit middleware)
# limiter = Limiter(
#     key_func=get_remote_address,
#     default_limits=[f"{settings.rate_limit_per_minute}/minute"]
# )


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """Application lifespan manager

    Handles startup and shutdown events.
    """
    # Startup
    logger.info("Starting up application...")
    logger.info(f"Environment: {settings.environment}")

    # Initialize Qdrant collection
    try:
        await init_qdrant_collection()
    except Exception as e:
        logger.error(f"Failed to initialize Qdrant collection: {e}")

    logger.info("Application startup complete")

    yield

    # Shutdown
    logger.info("Shutting down application...")
    await close_database_connections()
    logger.info("Application shutdown complete")


# Create FastAPI application
app = FastAPI(
    title="RAG Chatbot API",
    description="Retrieval-Augmented Generation chatbot for humanoid robotics textbook",
    version="1.0.0",
    docs_url="/api/docs" if not settings.is_production else None,
    redoc_url="/api/redoc" if not settings.is_production else None,
    lifespan=lifespan
)

# Temporarily disabled for debugging
from src.api.middleware.logging_middleware import LoggingMiddleware
from src.api.middleware.rate_limit import limiter, rate_limit_exceeded_handler

# Add rate limiter to app state
# Temporarily disabled for debugging
# app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, rate_limit_exceeded_handler)

# Add logging middleware
# Temporarily disabled due to middleware stack build error
app.add_middleware(LoggingMiddleware)

# Add secure headers middleware
# Temporarily disabled for debugging
@app.middleware("http")
async def secure_headers_middleware(request: Request, call_next):
    response = await call_next(request)

    try:
        # If the Secure instance exposes an integration helper named "framework"
        # that supports FastAPI/Starlette, use it.
        if getattr(secure_headers, "framework", None) is not None:
            fw = secure_headers.framework
            # defensive: some versions expose attributes differently
            if hasattr(fw, "fastapi"):
                fw.fastapi(response)
            elif hasattr(fw, "starlette"):
                fw.starlette(response)
            else:
                # fallback to a generic method if one exists
                if hasattr(secure_headers, "apply"):
                    secure_headers.apply(response)
                elif hasattr(secure_headers, "add"):
                    secure_headers.add(response)
                else:
                    raise AttributeError("Secure instance has no recognized integration methods")
        else:
            # library not integrated or missing; apply safe default headers manually
            # These are conservative, common security headers.
            response.headers.setdefault("X-Content-Type-Options", "nosniff")
            response.headers.setdefault("X-Frame-Options", "DENY")
            response.headers.setdefault("Referrer-Policy", "no-referrer")
            response.headers.setdefault("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload")
            response.headers.setdefault("Permissions-Policy", "geolocation=()")
            response.headers.setdefault("X-XSS-Protection", "1; mode=block")
    except Exception:
        # log the failure but do not crash the request pipeline
        logger.exception("Failed to apply secure headers")

    return response

# CORS middleware configuration
# Temporarily disabled for debugging
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.allowed_origins,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allow_headers=["*"],
    expose_headers=["X-Request-ID"],
)


# Exception handlers
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """Global exception handler for unhandled errors

    Args:
        request: FastAPI request
        exc: Exception that was raised

    Returns:
        JSONResponse with error details
    """
    logger.error(
        f"Unhandled exception: {exc}",
        extra={
            "path": request.url.path,
            "method": request.method,
            "client": request.client.host if request.client else "unknown"
        }
    )

    return JSONResponse(
        status_code=500,
        content={
            "error": "Internal server error",
            "message": "An unexpected error occurred" if settings.is_production else str(exc)
        }
    )


# Include routers
app.include_router(health.router, prefix="/api")
app.include_router(auth.router, prefix="/api")
app.include_router(chat.router, prefix="/api")


# Root endpoint
@app.get("/", tags=["root"])
async def root() -> dict[str, str]:
    """Root endpoint

    Returns:
        Welcome message
    """
    return {
        "message": "RAG Chatbot API",
        "status": "running",
        "docs": "/api/docs" if not settings.is_production else "disabled"
    }