diff --git a/README.md b/README.md index 0e984b66addead6f9beb822516b573197d9018f3..b1c9d893d6be2b4cd9ce48b6864ed9eef3524564 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,85 @@ app/ └── main.py Punto de entrada (crea la app). ``` +### Cómo extender + +Añadir una nueva entidad es directo gracias al `BaseService` +(`app/services/base.py`), que implementa el CRUD genérico: + +1. Define el modelo en `models/` y los esquemas en `schemas/`. +2. Crea un servicio que herede de `BaseService` (fija `collection_name`). +3. Añade el router en `rutas/` e inclúyelo en `rutas/__init__.py`. +4. (Opcional) Declara índices en `database/indexes.py`. + +## Requisitos + +- Python 3.11+ +- MongoDB 4.4+ en ejecución (local o remoto) + +## Instalación + +```bash +python -m venv .venv +source .venv/bin/activate # En Windows: .venv\Scripts\activate +pip install -r requirements.txt +``` + +## Configuración + +Copia `.env.example` a `.env` y ajusta los valores: + +```bash +cp .env.example .env +``` + +Genera una clave secreta segura para `SECRET_KEY`: + +```bash +python -c "import secrets; print(secrets.token_urlsafe(48))" +``` + +| Variable | Descripción | Por defecto | +|-------------------------------|-----------------------------------------------|-----------------------------| +| `APP_NAME` | Nombre de la aplicación | `Directorio Online API` | +| `APP_VERSION` | Versión | `1.0.0` | +| `APP_DESCRIPTION` | Descripción de la API | (ver `.env.example`) | +| `DEBUG` | Modo depuración | `false` | +| `API_PREFIX` | Prefijo de las rutas | `/api/v1` | +| `PORT` | Puerto de escucha del contenedor | `7860` | +| `MONGODB_URI` | URI de conexión a MongoDB | `mongodb://localhost:27017` | +| `MONGODB_DB_NAME` | Nombre de la base de datos | `directorio_online` | +| `SECRET_KEY` | Clave para firmar los JWT (**obligatoria**) | — | +| `ALGORITHM` | Algoritmo de firma del JWT | `HS256` | +| `ACCESS_TOKEN_EXPIRE_MINUTES` | Validez del token de acceso (minutos) | `60` | +| `CORS_ORIGINS` | Orígenes permitidos (lista por comas o `*`) | `*` | + +## Ejecución + +```bash +uvicorn app.main:app --reload +``` + +- Documentación interactiva (Swagger): `http://localhost:8000/docs` +- Documentación alternativa (ReDoc): `http://localhost:8000/redoc` +- Comprobación de salud: `http://localhost:8000/health` + +## Docker + +La aplicación se empaqueta con el `Dockerfile` incluido. El contenedor +escucha en el puerto indicado por la variable `PORT` (por defecto `7860`). + +```bash +# Construir la imagen +docker build -t directorio-online-backend . + +# Ejecutar (las variables se pasan con -e o con --env-file) +docker run --rm -p 7860:7860 \ + -e SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')" \ + -e MONGODB_URI="mongodb+srv://usuario:password@cluster.mongodb.net" \ + -e MONGODB_DB_NAME="directorio_online" \ + directorio-online-backend +``` + La API quedará disponible en `http://localhost:7860` (Swagger en `/docs`). ## Despliegue en HuggingFace Spaces (Docker) diff --git a/app/__pycache__/__init__.cpython-314.pyc b/app/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48a932d70ae73c534e183bcc18a66294881d0f0d Binary files /dev/null and b/app/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/__pycache__/main.cpython-314.pyc b/app/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36559e5408b132a4e67a7a6e4811374d0f52a4f4 Binary files /dev/null and b/app/__pycache__/main.cpython-314.pyc differ diff --git a/app/core/__pycache__/__init__.cpython-314.pyc b/app/core/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..988ff7296762849de06fb3527ff0aeb28e0bea18 Binary files /dev/null and b/app/core/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/core/__pycache__/app.cpython-314.pyc b/app/core/__pycache__/app.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ca0b340f7e3921d07ff15d1711c031cc9f918a3 Binary files /dev/null and b/app/core/__pycache__/app.cpython-314.pyc differ diff --git a/app/core/__pycache__/config.cpython-314.pyc b/app/core/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5906cfcd86c4de8d626e61642c2a686ebcbdb05 Binary files /dev/null and b/app/core/__pycache__/config.cpython-314.pyc differ diff --git a/app/core/__pycache__/exception_handlers.cpython-314.pyc b/app/core/__pycache__/exception_handlers.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0056999c403c0a489029518b0fad22e137375e6b Binary files /dev/null and b/app/core/__pycache__/exception_handlers.cpython-314.pyc differ diff --git a/app/core/__pycache__/lifespan.cpython-314.pyc b/app/core/__pycache__/lifespan.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce3c17ada70973e46af3c62a46d4ecfdbe393a75 Binary files /dev/null and b/app/core/__pycache__/lifespan.cpython-314.pyc differ diff --git a/app/core/__pycache__/middleware.cpython-314.pyc b/app/core/__pycache__/middleware.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9af55c6113e73c63c794739bfdff3b03e420cd40 Binary files /dev/null and b/app/core/__pycache__/middleware.cpython-314.pyc differ diff --git a/app/core/__pycache__/security.cpython-314.pyc b/app/core/__pycache__/security.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0e00e372bae645b126e986f3d331a80f1f46bf8 Binary files /dev/null and b/app/core/__pycache__/security.cpython-314.pyc differ diff --git a/app/core/app.py b/app/core/app.py index 3d1adfda38e4e1e3926f3cbb37a7c2b7c9a8f3b9..fd2fc994761bb1c04d149f1e9acfb63e59e52b8b 100644 --- a/app/core/app.py +++ b/app/core/app.py @@ -27,7 +27,19 @@ def create_app() -> FastAPI: @app.get("/health", tags=["Salud"], summary="Estado del servicio") async def health() -> dict[str, str]: - """Endpoint de comprobación de salud.""" + """Liveness: responde siempre y rápido (no depende de la base de datos).""" return {"status": "ok"} + @app.get( + "/health/db", + tags=["Salud"], + summary="Estado de la conexión a MongoDB", + ) + async def health_db() -> dict[str, str]: + """Readiness: comprueba si MongoDB responde en este momento.""" + from app.database.mongodb import mongodb + + conectado = await mongodb.ping() + return {"database": "connected" if conectado else "disconnected"} + return app diff --git a/app/core/config.py b/app/core/config.py index 5049b89429437a61106efaf9fc13c433615d3767..4f72c630fc982357e6ed0839d03a3ee24719c884 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -32,6 +32,8 @@ class Settings(BaseSettings): # --- Base de datos (MongoDB) --- MONGODB_URI: str = "mongodb://localhost:27017" MONGODB_DB_NAME: str = "directorio_online" + # Tiempo máximo (ms) para seleccionar un servidor de MongoDB. + MONGODB_TIMEOUT_MS: int = 5000 # --- Seguridad / JWT --- SECRET_KEY: str diff --git a/app/core/lifespan.py b/app/core/lifespan.py index 5f01d6f2f76e46bbd44aacd72b59d02848c2efe1..d811579adf44c1367dc84eff337971ab85d1f7d8 100644 --- a/app/core/lifespan.py +++ b/app/core/lifespan.py @@ -1,11 +1,13 @@ """Ciclo de vida de la aplicación. -Abre la conexión con MongoDB y asegura los índices al arrancar; cierra -la conexión de forma ordenada al apagar. +Crea el cliente de MongoDB y, si responde, asegura los índices. Si la base +de datos no está disponible al arrancar, la aplicación arranca igualmente +(no se cae) y la conexión se reintentará en cada petición. """ from __future__ import annotations +import logging from contextlib import asynccontextmanager from typing import AsyncIterator @@ -14,12 +16,24 @@ from fastapi import FastAPI from app.database.indexes import ensure_indexes from app.database.mongodb import mongodb +logger = logging.getLogger(__name__) + @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: """Gestiona los recursos de la aplicación durante su ciclo de vida.""" await mongodb.connect() - await ensure_indexes() + if await mongodb.ping(): + try: + await ensure_indexes() + except Exception as exc: # noqa: BLE001 + logger.warning("No se pudieron crear los índices: %s", exc) + else: + logger.warning( + "La aplicación arranca SIN conexión a MongoDB. Se reintentará " + "en cada petición. Revisa MONGODB_URI y el IP Access List de " + "Atlas (debe incluir 0.0.0.0/0 para hosts en la nube)." + ) try: yield finally: diff --git a/app/database/__pycache__/__init__.cpython-314.pyc b/app/database/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3286e2324fb06ffd1de4a2da683b759a2e5967c7 Binary files /dev/null and b/app/database/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/database/__pycache__/collections.cpython-314.pyc b/app/database/__pycache__/collections.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44cc347ed062acc9e3dda676819c5403e18251f5 Binary files /dev/null and b/app/database/__pycache__/collections.cpython-314.pyc differ diff --git a/app/database/__pycache__/indexes.cpython-314.pyc b/app/database/__pycache__/indexes.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54c13b007a1c3706e41664a71898fc2ac1a8085d Binary files /dev/null and b/app/database/__pycache__/indexes.cpython-314.pyc differ diff --git a/app/database/__pycache__/mongodb.cpython-314.pyc b/app/database/__pycache__/mongodb.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..734c433fe408e4d95af64b8bcd0c25d6ee9536cf Binary files /dev/null and b/app/database/__pycache__/mongodb.cpython-314.pyc differ diff --git a/app/database/__pycache__/objectid.cpython-314.pyc b/app/database/__pycache__/objectid.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02ba02f9531a92ea6f15c655d837f342f125b739 Binary files /dev/null and b/app/database/__pycache__/objectid.cpython-314.pyc differ diff --git a/app/database/mongodb.py b/app/database/mongodb.py index e35abad17c48750d0ea9cca0e8f99bce98e7f886..6b43a4edc97e15c742c8a025c09bc0ff1cff2620 100644 --- a/app/database/mongodb.py +++ b/app/database/mongodb.py @@ -3,11 +3,16 @@ Se utiliza el driver oficial ``pymongo`` con su API asíncrona (:class:`pymongo.AsyncMongoClient`), disponible de forma estable a partir de PyMongo 4.13. + +El cliente se crea de forma perezosa: la conexión real se abre en la +primera operación, por lo que la aplicación puede arrancar aunque la +base de datos no esté disponible todavía (no se bloquea el arranque). """ from __future__ import annotations import logging +import socket from pymongo import AsyncMongoClient from pymongo.asynchronous.collection import AsyncCollection @@ -22,23 +27,72 @@ class MongoDB: """Contenedor del cliente y la base de datos de MongoDB. Mantiene una única instancia del cliente durante el ciclo de vida de - la aplicación. La conexión se abre en el ``lifespan`` y se cierra al - apagar el servidor. + la aplicación. """ client: AsyncMongoClient | None = None database: AsyncDatabase | None = None async def connect(self) -> None: - """Abre la conexión con MongoDB y verifica su disponibilidad.""" + """Crea el cliente de MongoDB sin bloquear el arranque. + + No se hace ``ping`` aquí a propósito: el cliente es perezoso y la + conexión se abre en la primera operación. Así la app arranca aunque + Mongo no responda todavía. + """ if self.client is not None: return - logger.info("Conectando a MongoDB en %s", settings.MONGODB_URI) - self.client = AsyncMongoClient(settings.MONGODB_URI, tz_aware=True) + self.client = AsyncMongoClient( + settings.MONGODB_URI, + tz_aware=True, + serverSelectionTimeoutMS=settings.MONGODB_TIMEOUT_MS, + ) self.database = self.client[settings.MONGODB_DB_NAME] - # Verifica que la conexión es válida. - await self.client.admin.command("ping") - logger.info("Conexión a MongoDB establecida (db=%s)", settings.MONGODB_DB_NAME) + + async def ping(self) -> bool: + """Comprueba si MongoDB responde. No lanza excepción.""" + if self.client is None: + return False + try: + await self.client.admin.command("ping") + except Exception as exc: # noqa: BLE001 + logger.warning("MongoDB no responde: %s", exc) + self._diagnostico_red() + return False + logger.info("MongoDB conectado (db=%s).", settings.MONGODB_DB_NAME) + return True + + def _diagnostico_red(self) -> None: + """Registra un diagnóstico de red hacia los nodos de MongoDB. + + Resuelve el DNS y prueba un socket TCP crudo a cada nodo para + distinguir un bloqueo de red de un problema del driver/credenciales. + """ + try: + nodos = self.client.topology_description.server_descriptions() + except Exception: # noqa: BLE001 + return + for host, port in nodos: + try: + ip = socket.gethostbyname(host) + except OSError as exc: + logger.warning("DIAG DNS %s -> FALLA (%s)", host, exc) + continue + sock = socket.socket() + sock.settimeout(5) + try: + sock.connect((host, port)) + logger.warning("DIAG TCP %s:%s (%s) -> OK", host, port, ip) + except OSError as exc: + logger.warning( + "DIAG TCP %s:%s (%s) -> BLOQUEADO/timeout (%s)", + host, + port, + ip, + type(exc).__name__, + ) + finally: + sock.close() async def close(self) -> None: """Cierra la conexión con MongoDB.""" diff --git a/app/exceptions/__pycache__/__init__.cpython-314.pyc b/app/exceptions/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74f9f9ce9dfef1ee71ecc55cab9248f1597e3049 Binary files /dev/null and b/app/exceptions/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/exceptions/__pycache__/base.cpython-314.pyc b/app/exceptions/__pycache__/base.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b69fda46608564dd476e644e4124bba21ad87e19 Binary files /dev/null and b/app/exceptions/__pycache__/base.cpython-314.pyc differ diff --git a/app/models/__pycache__/__init__.cpython-314.pyc b/app/models/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fab589926b7b7430ad99e8fa7feb4e61c6eaa528 Binary files /dev/null and b/app/models/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/models/__pycache__/categoria.cpython-314.pyc b/app/models/__pycache__/categoria.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab76f249260031fbbe7a282c4d63a054104c092a Binary files /dev/null and b/app/models/__pycache__/categoria.cpython-314.pyc differ diff --git a/app/models/__pycache__/common.cpython-314.pyc b/app/models/__pycache__/common.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cf6c469f97c4955b09f271863e86bc99edf83a0 Binary files /dev/null and b/app/models/__pycache__/common.cpython-314.pyc differ diff --git a/app/models/__pycache__/item.cpython-314.pyc b/app/models/__pycache__/item.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..098503af196a1391f640ab2c2da61067bdc3d320 Binary files /dev/null and b/app/models/__pycache__/item.cpython-314.pyc differ diff --git a/app/models/__pycache__/negocio.cpython-314.pyc b/app/models/__pycache__/negocio.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5d1ac423d0c9cc406f61a2d9491a2fb9323a84a Binary files /dev/null and b/app/models/__pycache__/negocio.cpython-314.pyc differ diff --git a/app/models/__pycache__/resena.cpython-314.pyc b/app/models/__pycache__/resena.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffabd3dd13cd9da425dc97de272e9f145d9c6c6c Binary files /dev/null and b/app/models/__pycache__/resena.cpython-314.pyc differ diff --git a/app/models/__pycache__/user.cpython-314.pyc b/app/models/__pycache__/user.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0d75ee0784aeac458a64e16e5967d436c821b80 Binary files /dev/null and b/app/models/__pycache__/user.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/__init__.cpython-314.pyc b/app/rutas/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c07f47ad90a56d2f783a463433d029889294f41 Binary files /dev/null and b/app/rutas/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/auth.cpython-314.pyc b/app/rutas/__pycache__/auth.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f177ef57e1c17d16f44ac57978e8f469bec58ac5 Binary files /dev/null and b/app/rutas/__pycache__/auth.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/categorias.cpython-314.pyc b/app/rutas/__pycache__/categorias.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..551e2fb31075d0c25dee7a58c33675b3b2859628 Binary files /dev/null and b/app/rutas/__pycache__/categorias.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/deps.cpython-314.pyc b/app/rutas/__pycache__/deps.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8402cf8060b1bdcdf79a8793bd2ce13dc5ed4065 Binary files /dev/null and b/app/rutas/__pycache__/deps.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/items.cpython-314.pyc b/app/rutas/__pycache__/items.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1443e5dd09e14e3caa43d652494b95464cd71305 Binary files /dev/null and b/app/rutas/__pycache__/items.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/negocios.cpython-314.pyc b/app/rutas/__pycache__/negocios.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62283a751edfdefc80c111aae585fee83993cf66 Binary files /dev/null and b/app/rutas/__pycache__/negocios.cpython-314.pyc differ diff --git a/app/rutas/__pycache__/resenas.cpython-314.pyc b/app/rutas/__pycache__/resenas.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..018fd52d6627ccc60c374344e7f9934b8136ecdc Binary files /dev/null and b/app/rutas/__pycache__/resenas.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/__init__.cpython-314.pyc b/app/schemas/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35d29ab88d72dfadf7e86402e26ed720b551607a Binary files /dev/null and b/app/schemas/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/auth.cpython-314.pyc b/app/schemas/__pycache__/auth.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..818512c35fa624248db9a16703b5681cef769eb7 Binary files /dev/null and b/app/schemas/__pycache__/auth.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/categoria.cpython-314.pyc b/app/schemas/__pycache__/categoria.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fee2121d307139d0c8d8c56f7f91463fc5827c97 Binary files /dev/null and b/app/schemas/__pycache__/categoria.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/common.cpython-314.pyc b/app/schemas/__pycache__/common.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58827520f3131e881c6e24475a6ba284957a3283 Binary files /dev/null and b/app/schemas/__pycache__/common.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/item.cpython-314.pyc b/app/schemas/__pycache__/item.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0a3a5e3853c2356a4f9f7e4d83ca058ab47fa64 Binary files /dev/null and b/app/schemas/__pycache__/item.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/negocio.cpython-314.pyc b/app/schemas/__pycache__/negocio.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62c0bffb1c19f23880ae9ed2dc552a64057efc7a Binary files /dev/null and b/app/schemas/__pycache__/negocio.cpython-314.pyc differ diff --git a/app/schemas/__pycache__/resena.cpython-314.pyc b/app/schemas/__pycache__/resena.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac2fe804e3b20ff774c5a56d9c9781f50f6c8a82 Binary files /dev/null and b/app/schemas/__pycache__/resena.cpython-314.pyc differ diff --git a/app/services/__pycache__/__init__.cpython-314.pyc b/app/services/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf9fe4f62eb15c5136989d10d4204b259e9c7030 Binary files /dev/null and b/app/services/__pycache__/__init__.cpython-314.pyc differ diff --git a/app/services/__pycache__/auth.cpython-314.pyc b/app/services/__pycache__/auth.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0edb207e1c766fa570d7d54b989bcc953018b54f Binary files /dev/null and b/app/services/__pycache__/auth.cpython-314.pyc differ diff --git a/app/services/__pycache__/base.cpython-314.pyc b/app/services/__pycache__/base.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb93814bda6d4cd434eb65b3d59130d79160059a Binary files /dev/null and b/app/services/__pycache__/base.cpython-314.pyc differ diff --git a/app/services/__pycache__/categoria.cpython-314.pyc b/app/services/__pycache__/categoria.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4a0f338a5cfd936bd11107e6c6aba13d3ff2a89 Binary files /dev/null and b/app/services/__pycache__/categoria.cpython-314.pyc differ diff --git a/app/services/__pycache__/item.cpython-314.pyc b/app/services/__pycache__/item.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d66fc3990813ca02fbe7607969aff85d5dbadbe1 Binary files /dev/null and b/app/services/__pycache__/item.cpython-314.pyc differ diff --git a/app/services/__pycache__/negocio.cpython-314.pyc b/app/services/__pycache__/negocio.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cea3a47dcd893eeccd5ca2794a58d086cd12921 Binary files /dev/null and b/app/services/__pycache__/negocio.cpython-314.pyc differ diff --git a/app/services/__pycache__/resena.cpython-314.pyc b/app/services/__pycache__/resena.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..946998b7c89660803e746e4110ab2f006e2d5adf Binary files /dev/null and b/app/services/__pycache__/resena.cpython-314.pyc differ diff --git a/app/services/__pycache__/utils.cpython-314.pyc b/app/services/__pycache__/utils.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42ee33e69b7815bd71c0538e5a868b88a3bc720d Binary files /dev/null and b/app/services/__pycache__/utils.cpython-314.pyc differ diff --git a/requirements.txt b/requirements.txt index 98789c1536aff15fca9af1a8a3b353ce9ae954bc..95fd5bc3c4b3c7a97ef443c410c61d172a9da9b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,8 +4,10 @@ pymongo==4.15.0 # dnspython es necesario para resolver los URIs mongodb+srv:// (MongoDB # Atlas), el caso habitual al desplegar en HuggingFace Spaces. dnspython==2.7.0 -pydantic==2.11.9 -pydantic-settings==2.10.1 +# pydantic >= 2.12 trae binarios (wheels) para Python 3.14; el rango evita +# tener que compilar pydantic-core desde fuente en versiones nuevas de Python. +pydantic>=2.12,<3 +pydantic-settings>=2.10,<3 pyjwt==2.10.1 bcrypt==4.3.0 python-multipart==0.0.20