Files
service_manager/backend/app/main.py
icamarillo 91ff49cdec feat(backend): Update models and endpoints configuration
- Enhanced ticket and comment models with proper relationships
- Updated client_profile model for better data handling
- Improved auth endpoint with better error handling
- Updated main app configuration and imports
- Added new dependencies to requirements.txt
- Enhanced tickets endpoint with attachment support
2026-02-09 13:28:40 -07:00

204 lines
5.6 KiB
Python

"""
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.models.comment import TicketComment
from app.models.attachment import TicketAttachment
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"
)