Files
deals24togo_be/app/main.py
belviskhoremk c4d836a0f9 Initial commit
2026-03-06 22:57:58 +00:00

96 lines
2.7 KiB
Python

"""FastAPI application factory."""
from __future__ import annotations
from contextlib import asynccontextmanager
import sentry_sdk
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.util import get_remote_address
from app.api.v1.router import api_router
from app.core.config import get_settings
from app.core.exceptions import AppException
from app.core.logging import get_logger, setup_logging
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Startup / shutdown events."""
setup_logging()
settings = get_settings()
# Sentry
if settings.SENTRY_DSN:
sentry_sdk.init(
dsn=settings.SENTRY_DSN,
traces_sample_rate=0.1,
environment=settings.APP_ENV,
)
logger.info(
"application_started",
app_name=settings.APP_NAME,
env=settings.APP_ENV,
)
yield
logger.info("application_shutdown")
def create_app() -> FastAPI:
settings = get_settings()
app = FastAPI(
title=f"{settings.APP_NAME} API",
description="Marketplace API for Deals24Togo — listings, agencies, categories, and more.",
version="1.0.0",
docs_url="/docs" if settings.DEBUG else None,
redoc_url="/redoc" if settings.DEBUG else None,
lifespan=lifespan,
)
# ── CORS ──
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allowed_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── Rate limiter ──
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
# ── Exception handlers ──
@app.exception_handler(AppException)
async def app_exception_handler(_request: Request, exc: AppException):
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail},
)
@app.exception_handler(Exception)
async def global_exception_handler(_request: Request, exc: Exception):
logger.error("unhandled_exception", error=str(exc), exc_info=True)
return JSONResponse(
status_code=500,
content={"detail": "Internal server error"},
)
# ── Routes ──
app.include_router(api_router)
@app.get("/health", tags=["Health"])
def health_check():
return {"status": "healthy", "app": settings.APP_NAME, "env": settings.APP_ENV}
return app