MCP: se retira por completo (endpoint, paquete api/app/mcp/ y dependencia). Tres razones, en orden de peso: - Nadie lo usa. - Llevaba roto desde antes de este trabajo. Con credencial valida devolvia 500: el handler construia un StreamableHTTPServerTransport nuevo por peticion, sin manejo de sesion. Lo que estaba expuesto a internet era la puerta abierta de un cuarto averiado. - Su SDK sin fijar tumbo el API entero en produccion al saltar a 2.0.0. Retirarlo es una mitigacion mas fuerte que autenticarlo, que fue lo que hizo el commit anterior. Recuperable con `git show edb500f5:api/app/mcp/server.py`. Se limpia tambien la ruta "/mcp" que el endpoint raiz seguia anunciando, y las menciones a MCP del docstring y la descripcion de Swagger. Dependencias: once de las doce eran rangos `>=` sin techo, o sea que cada reconstruccion era una tirada de dados contra PyPI. `pydantic>=2.0` habria aceptado pydantic 3 con la misma alegria con la que `mcp>=1.0.0` acepto 2.0.0. Ahora todas van fijadas a la version exacta que corre sana en produccion, capturada con pip freeze del contenedor healthy. El lado Next.js ya era reproducible via package-lock.json; por eso el web nunca se cayo durante el incidente y el api si. Docs actualizados: AGENTS.md, README.md, api/COTIZADOR_API_SKILL.md y el spec, que ademas registra en su seccion 0 las tres desviaciones de Fase 0 respecto a lo disenado (el Despliegue B cancelado, la retirada del MCP y la deriva de dependencia). Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
123 lines
3.8 KiB
Python
123 lines
3.8 KiB
Python
"""Cotizador E3 — FastAPI Application
|
|
|
|
REST API for digital marketing quotation management.
|
|
Optimized for n8n workflows and other integrations.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from contextlib import asynccontextmanager
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.config import settings
|
|
from app.database import close_db, init_db
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
await init_db()
|
|
yield
|
|
await close_db()
|
|
|
|
|
|
app = FastAPI(
|
|
title="Cotizador E3 API",
|
|
description=(
|
|
"API REST para el sistema de cotizaciones de marketing digital de Consultoría E3. "
|
|
"Optimizada para integración con n8n y otros consumidores. "
|
|
"Autenticación por X-API-Key o Bearer JWT."
|
|
),
|
|
version="1.0.0",
|
|
lifespan=lifespan,
|
|
docs_url="/docs",
|
|
redoc_url="/redoc",
|
|
openapi_url="/openapi.json",
|
|
openapi_tags=[
|
|
{"name": "Health", "description": "Health check y información de la API"},
|
|
{"name": "Auth", "description": "Autenticación (API Key + JWT)"},
|
|
{"name": "Clientes", "description": "Gestión de clientes"},
|
|
{"name": "Catálogo", "description": "Catálogo de servicios de marketing digital"},
|
|
{"name": "Categorías", "description": "Categorías de servicios"},
|
|
{"name": "Cotizaciones", "description": "Cotizaciones — CRUD completo con servicios y plan CRM"},
|
|
{"name": "Paquetes", "description": "Paquetes de servicios con fases"},
|
|
{"name": "Configuración", "description": "Configuración de la empresa (key-value)"},
|
|
{"name": "Bonos", "description": "Bonos disponibles para cotizaciones"},
|
|
{"name": "Financiamiento", "description": "Planes y cálculo de financiamiento"},
|
|
{"name": "Export", "description": "Exportación de PDF, Excel y CSV"},
|
|
{"name": "Import", "description": "Importación masiva de servicios"},
|
|
],
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
@app.exception_handler(Exception)
|
|
async def global_exception_handler(request: Request, exc: Exception):
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"error": "Error interno del servidor", "detail": str(exc)},
|
|
)
|
|
|
|
|
|
# Register routers
|
|
from app.routers import (
|
|
auth,
|
|
bonos,
|
|
catalogo,
|
|
categorias,
|
|
clientes,
|
|
configuracion,
|
|
cotizaciones,
|
|
export_,
|
|
financiamiento,
|
|
health,
|
|
import_,
|
|
paquetes,
|
|
)
|
|
|
|
app.include_router(health.router)
|
|
app.include_router(auth.router)
|
|
app.include_router(clientes.router)
|
|
app.include_router(catalogo.router)
|
|
app.include_router(categorias.router)
|
|
app.include_router(cotizaciones.router)
|
|
app.include_router(paquetes.router)
|
|
app.include_router(configuracion.router)
|
|
app.include_router(bonos.router)
|
|
app.include_router(financiamiento.router)
|
|
app.include_router(export_.router)
|
|
app.include_router(import_.router)
|
|
|
|
# El servidor MCP se retiro (2026-07-28). No se estaba usando, su transporte
|
|
# llevaba tiempo roto (instanciaba un StreamableHTTPServerTransport por peticion,
|
|
# sin manejo de sesion, y devolvia 500 con credencial valida) y su SDK sin fijar
|
|
# tumbo el API entero en produccion al saltar a 2.0.0. Si algun dia se retoma,
|
|
# el codigo esta en el historial: `git show edb500f5:api/app/mcp/server.py`.
|
|
|
|
|
|
@app.get("/", include_in_schema=False)
|
|
async def root():
|
|
return {
|
|
"name": "Cotizador E3 API",
|
|
"version": "1.0.0",
|
|
"docs": "/docs",
|
|
"openapi": "/openapi.json",
|
|
"health": "/health",
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run("main:app", host=settings.API_HOST, port=settings.API_PORT, reload=True)
|