from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse, Response import logging from app.config import settings from app.routers import auth, chatbots, documents, chat, marketplace, billing, models, analytics, inbox, leads, upload from app.routers.documents import router_url_sources from app.routers.leads import leads_public_router from app.routers.channels import router as channels_router, webhook_router as channels_webhook_router # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Contexta API starting up...") logger.info(f"Environment: {settings.app_env}") logger.info(f"Allowed origins: {settings.allowed_origins_list}") yield logger.info("Contexta API shutting down...") # ── App ────────────────────────────────────────────────────────────────────────── app = FastAPI( title="Contexta API", description="AI Chatbot Platform - Create, deploy and share custom AI chatbots powered by your data", version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan, ) # ── Middleware ───────────────────────────────────────────────────────────────── app.add_middleware( CORSMiddleware, allow_origins=settings.allowed_origins_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ── Routers ──────────────────────────────────────────────────────────────────── app.include_router(auth.router, prefix="/api/v1") app.include_router(chatbots.router, prefix="/api/v1") app.include_router(documents.router, prefix="/api/v1") app.include_router(chat.router, prefix="/api/v1") app.include_router(marketplace.router, prefix="/api/v1") app.include_router(billing.router, prefix="/api/v1") app.include_router(models.router, prefix="/api/v1") app.include_router(analytics.router, prefix="/api/v1") app.include_router(inbox.router, prefix="/api/v1") app.include_router(leads.router, prefix="/api/v1") app.include_router(upload.router, prefix="/api/v1") app.include_router(router_url_sources, prefix="/api/v1") app.include_router(leads_public_router, prefix="/api/v1") app.include_router(channels_router, prefix="/api/v1") app.include_router(channels_webhook_router, prefix="/api/v1") # ── Widget ───────────────────────────────────────────────────────────────────── @app.get("/widget.js") async def serve_widget(): from app.services.widget import generate_widget_js return Response(generate_widget_js(settings.app_url), media_type="application/javascript") # ── Health & Info ────────────────────────────────────────────────────────────── @app.get("/") async def root(): return { "name": "Contexta API", "version": "1.0.0", "status": "running", "docs": "/docs", } @app.get("/health") async def health(): return {"status": "healthy", "environment": settings.app_env} # ── Sentry ───────────────────────────────────────────────────────────────────── if settings.sentry_dsn: import sentry_sdk sentry_sdk.init(dsn=settings.sentry_dsn, traces_sample_rate=0.1) logger.info("Sentry initialized") if __name__ == "__main__": import uvicorn uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)