from contextlib import asynccontextmanager from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from apscheduler.schedulers.asyncio import AsyncIOScheduler from app.core.config import settings from app.api.eta import router as eta_router from app.api.auth import router as auth_router from app.api.addresses import router as addresses_router from app.api.colonias import router as colonias_router from app.api.simulation import router as simulation_router from app.services import simulation, notifications scheduler = AsyncIOScheduler() @asynccontextmanager async def lifespan(app: FastAPI): print("Iniciando aplicación: Backend Sistema de Recolección...") simulation.load_data() simulation.start_simulation_state() notifications.init_firebase(settings.FIREBASE_CREDENTIALS_PATH) scheduler.add_job( simulation.tick, "interval", seconds=settings.SIMULATION_TICK_SECONDS, id="simulation_tick", ) scheduler.start() print(f"Simulador iniciado. Tick cada {settings.SIMULATION_TICK_SECONDS}s.") yield print("Apagando aplicación y deteniendo simulador...") scheduler.shutdown() app = FastAPI( title="API — Recolección Inteligente y Privada", description="Backend para el sistema de recolección de residuos con privacidad por diseño.", version="1.0.0", lifespan=lifespan, ) # CORS — necesario para el simulador web y el cliente Flutter app.add_middleware( CORSMiddleware, allow_origins=["*"], # En producción limitar a dominios reales allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth_router) app.include_router(addresses_router) app.include_router(eta_router) app.include_router(colonias_router) app.include_router(simulation_router) @app.get("/") def read_root(): return { "status": "ok", "message": "Backend operativo. Regla innegociable #1: NUNCA se devuelven coordenadas al ciudadano.", } @app.get("/health") def health_check(): return {"status": "healthy"}