import os import uuid import uvicorn import urllib3 import logging from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from contextlib import asynccontextmanager from dotenv import load_dotenv from logger_config import setup_logging from context import set_request_id, get_request_id, set_current_user from core.session import validate_and_refresh_session, get_employee_name, get_emp_id from routers.auth import router as auth_router from routers.business_partners import router as bp_router from routers.items import router as items_router from routers.sales import router as sales_router from routers.purchases import router as purchases_router from routers.catalogs import router as catalogs_router from routers.batch import router as batch_router from routers.permissions import router as permissions_router from routers.apigateway import router as apigateway_router from routers.reports import router as reports_router from routers.series import router as series_router from routers.user_tables import router as user_tables_router from routers.inventory_transfer_requests import router as inventory_transfer_requests_router from routers.cotizacion_web import router as cotizacion_web_router # ============================================================================ load_dotenv(dotenv_path=".env") logger = logging.getLogger(__name__) LOG_ENVIRONMENT = os.getenv("LOG_ENVIRONMENT", "production") # ============================================================================ @asynccontextmanager async def lifespan(app: FastAPI): setup_logging(environment=LOG_ENVIRONMENT) yield app = FastAPI( title="SAP Business One Integration API", description="API para integrar Socios de negocios con SAP Business One Service Layer.", version="1.1.0", lifespan=lifespan ) app.include_router(auth_router) app.include_router(bp_router) app.include_router(items_router) app.include_router(sales_router) app.include_router(purchases_router) app.include_router(catalogs_router) app.include_router(batch_router) app.include_router(permissions_router) app.include_router(apigateway_router) app.include_router(reports_router) app.include_router(series_router) app.include_router(user_tables_router) app.include_router(inventory_transfer_requests_router) app.include_router(cotizacion_web_router) # ============================================================================ @app.middleware("http") async def correlation_id_middleware(request: Request, call_next): req_id = request.headers.get("X-Request-ID", str(uuid.uuid4())) set_request_id(req_id) response = await call_next(request) response.headers["X-Request-ID"] = get_request_id() return response # ============================================================================ @app.middleware("http") async def session_middleware(request: Request, call_next): if request.url.path == "/api/Login": return await call_next(request) if request.url.path.startswith("/api/"): token = request.headers.get("X-Session-Token") if not token: logger.warning(f"Request sin token: {request.method} {request.url.path}") return JSONResponse( status_code=401, content={"detail": "Se requiere autenticación. Inicie sesión primero."} ) webUser = validate_and_refresh_session(token) if not webUser: logger.warning(f"Token inválido o expirado en: {request.method} {request.url.path}") return JSONResponse( status_code=401, content={"detail": "Sesión expirada. Debe iniciar sesión nuevamente."} ) set_current_user(webUser, get_employee_name(webUser), get_emp_id(webUser)) request.state.webUser = webUser return await call_next(request) # ============================================================================ app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) urllib3.disable_warnings() # ============================================================================ if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug", access_log=True)