Initial commit
This commit is contained in:
201
backend/app/main.py
Normal file
201
backend/app/main.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
ServiceManagerWeb Backend - FastAPI Application
|
||||
|
||||
Mesa de Ayuda B2B multi-tenant con Clean Architecture
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, Request, Response
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from contextlib import asynccontextmanager
|
||||
import structlog
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.core.database import engine, create_tables
|
||||
# Import models to register them with SQLAlchemy
|
||||
from app.models.tenant import Tenant
|
||||
from app.models.system import System
|
||||
from app.models.category import Category
|
||||
from app.models.user import User
|
||||
from app.models.ticket import Ticket
|
||||
|
||||
from app.core.logging import setup_logging
|
||||
from app.api.v1.router import api_router
|
||||
from app.middleware.tenant import TenantMiddleware
|
||||
from app.middleware.correlation_id import CorrelationIDMiddleware
|
||||
|
||||
settings = get_settings()
|
||||
setup_logging()
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Lifecycle manager para la aplicación."""
|
||||
# Startup
|
||||
logger.info("Iniciando ServiceManagerWeb Backend", version=settings.API_VERSION)
|
||||
|
||||
if settings.ENVIRONMENT == "development":
|
||||
await create_tables()
|
||||
logger.info("Tablas de base de datos verificadas")
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
logger.info("Cerrando ServiceManagerWeb Backend")
|
||||
|
||||
|
||||
# Crear aplicación FastAPI
|
||||
app = FastAPI(
|
||||
title="ServiceManagerWeb API",
|
||||
description="Mesa de Ayuda B2B multi-tenant para Aduanasoft",
|
||||
version=settings.API_VERSION,
|
||||
lifespan=lifespan,
|
||||
docs_url=f"/{settings.API_VERSION}/docs" if settings.ENVIRONMENT == "development" else None,
|
||||
redoc_url=f"/{settings.API_VERSION}/redoc" if settings.ENVIRONMENT == "development" else None,
|
||||
openapi_url=f"/{settings.API_VERSION}/openapi.json"
|
||||
)
|
||||
|
||||
# ===================================
|
||||
# MIDDLEWARE
|
||||
# ===================================
|
||||
|
||||
# CORS
|
||||
cors_origins = settings.CORS_ORIGINS.split(",") if isinstance(settings.CORS_ORIGINS, str) else settings.CORS_ORIGINS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Compression
|
||||
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
||||
|
||||
# Custom middleware
|
||||
app.add_middleware(CorrelationIDMiddleware)
|
||||
app.add_middleware(TenantMiddleware)
|
||||
|
||||
# Request logging middleware
|
||||
@app.middleware("http")
|
||||
async def request_logging_middleware(request: Request, call_next):
|
||||
"""Log todas las requests con métricas de performance."""
|
||||
start_time = time.time()
|
||||
correlation_id = getattr(request.state, "correlation_id", str(uuid.uuid4()))
|
||||
|
||||
# Log request
|
||||
logger.info(
|
||||
"Request iniciada",
|
||||
method=request.method,
|
||||
url=str(request.url),
|
||||
correlation_id=correlation_id,
|
||||
user_agent=request.headers.get("user-agent"),
|
||||
remote_addr=request.client.host if request.client else None
|
||||
)
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
# Log response
|
||||
duration = time.time() - start_time
|
||||
logger.info(
|
||||
"Request completada",
|
||||
method=request.method,
|
||||
url=str(request.url),
|
||||
status_code=response.status_code,
|
||||
duration=f"{duration:.3f}s",
|
||||
correlation_id=correlation_id
|
||||
)
|
||||
|
||||
# Add correlation ID to response headers
|
||||
response.headers["X-Correlation-ID"] = correlation_id
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# ===================================
|
||||
# EXCEPTION HANDLERS
|
||||
# ===================================
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
"""Handler global para excepciones no capturadas."""
|
||||
correlation_id = getattr(request.state, "correlation_id", str(uuid.uuid4()))
|
||||
|
||||
logger.error(
|
||||
"Excepción no manejada",
|
||||
error=str(exc),
|
||||
correlation_id=correlation_id,
|
||||
url=str(request.url),
|
||||
method=request.method,
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "INTERNAL_ERROR",
|
||||
"message": "Error interno del servidor"
|
||||
},
|
||||
"correlation_id": correlation_id
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# ===================================
|
||||
# ROUTES
|
||||
# ===================================
|
||||
|
||||
# Health check endpoint
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check para load balancer y monitoring."""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "ServiceManagerWeb API",
|
||||
"version": settings.API_VERSION,
|
||||
"environment": settings.ENVIRONMENT
|
||||
}
|
||||
|
||||
|
||||
# Root endpoint
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Endpoint raíz con información básica."""
|
||||
return {
|
||||
"service": "ServiceManagerWeb API",
|
||||
"version": settings.API_VERSION,
|
||||
"docs": f"/{settings.API_VERSION}/docs",
|
||||
"environment": settings.ENVIRONMENT
|
||||
}
|
||||
|
||||
|
||||
# API routes
|
||||
app.include_router(
|
||||
api_router,
|
||||
prefix=f"/{settings.API_VERSION}",
|
||||
responses={
|
||||
400: {"description": "Bad Request"},
|
||||
401: {"description": "Unauthorized"},
|
||||
403: {"description": "Forbidden"},
|
||||
404: {"description": "Not Found"},
|
||||
422: {"description": "Validation Error"},
|
||||
500: {"description": "Internal Server Error"}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=settings.ENVIRONMENT == "development"
|
||||
)
|
||||
Reference in New Issue
Block a user