coder16
update
57090be
|
Raw
History Blame Contribute Delete
10.1 kB
---
title: Directorio Online API
emoji: 📒
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false
---
# Directorio Online — Backend (FastAPI + MongoDB)
API REST genérica y fácil de extender para un directorio de negocios
organizado por **categorías**, con **items** (menús, servicios o
productos), **reseñas** e **imágenes**. Construida con **FastAPI** y
**MongoDB** (driver `pymongo` con su API asíncrona), autenticación
**JWT (OAuth2)** y validación con **Pydantic v2**.
## Modelo de datos y relaciones
```
Categoria
│ (puede anidarse a sí misma vía parent_id)
└── Negocio (N)
Negocio
├── Item (N) (tipo: menu | servicio | producto)
├── Reseña (N)
└── Imagen (N) (embebida)
Item
└── Imagen (N) (embebida)
```
Los identificadores son el `ObjectId` de MongoDB, expuesto siempre como
cadena en el campo `id` de las respuestas.
## Estructura del proyecto
```
app/
├── core/ Configuración FastAPI: settings, seguridad (JWT),
│ middlewares (CORS), lifespan y manejadores de excepciones.
├── database/ Conexión y cliente de MongoDB, índices y utilidades.
├── exceptions/ Excepciones propias de la aplicación.
├── models/ Modelos de dominio (lo que se guarda en la BD).
├── schemas/ Esquemas de entrada/salida con validaciones
│ (contrato entre rutas y servicios).
├── services/ Lógica de negocio (CRUD genérico + reglas por entidad).
├── rutas/ Endpoints de comunicación con los clientes.
└── 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)
El repositorio está listo para desplegarse como un **Space de tipo Docker**:
- El `README.md` incluye las **metadatos** del Space en su cabecera YAML
(`sdk: docker`, `app_port: 7860`).
- El `Dockerfile` ejecuta el contenedor como el usuario `UID 1000` (requisito
de HuggingFace) y arranca `uvicorn` en el puerto `7860`.
Pasos:
1. Crea un Space → **SDK: Docker***Blank*.
2. Sube este repositorio al Space (o conéctalo a GitHub).
3. En **Settings → Variables and secrets**, define las variables del
archivo `.env.example`:
- Como **Secret**: `SECRET_KEY` y `MONGODB_URI`.
- Como **Variable**: el resto (`MONGODB_DB_NAME`, `CORS_ORIGINS`, etc.).
4. El Space construye la imagen y la API queda expuesta en la URL del Space
(`https://<usuario>-<space>.hf.space`), con Swagger en `/docs`.
> **MongoDB:** HuggingFace no provee base de datos, así que `MONGODB_URI`
> debe apuntar a una instancia accesible desde internet (p. ej. MongoDB
> Atlas). El driver ya incluye `dnspython` (extra `pymongo[srv]`) para los
> URIs `mongodb+srv://`.
## Autenticación
- La **lectura** (GET) es pública.
- La **escritura** (POST/PUT/DELETE) requiere un token JWT.
```bash
# 1) Registrar un usuario
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","email":"admin@correo.com","password":"supersecreta"}'
# 2) Iniciar sesión (formulario OAuth2; "username" admite usuario o correo)
curl -X POST http://localhost:8000/api/v1/auth/login \
-d "username=admin&password=supersecreta"
# 3) Usar el token en las operaciones de escritura
curl -X POST http://localhost:8000/api/v1/categorias \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"nombre":"Restaurantes"}'
```
## Endpoints principales
| Método | Ruta | Auth | Descripción |
|--------|-------------------------------|:----:|-----------------------------------|
| POST | `/api/v1/auth/register` | — | Registrar usuario |
| POST | `/api/v1/auth/login` | — | Iniciar sesión (devuelve JWT) |
| GET | `/api/v1/auth/me` | ✓ | Usuario autenticado |
| GET | `/api/v1/categorias` | — | Listar categorías |
| POST | `/api/v1/categorias` | ✓ | Crear categoría |
| GET | `/api/v1/categorias/{id}` | — | Obtener categoría |
| PUT | `/api/v1/categorias/{id}` | ✓ | Actualizar categoría |
| DELETE | `/api/v1/categorias/{id}` | ✓ | Eliminar categoría |
| GET | `/api/v1/negocios` | — | Listar negocios (filtros) |
| POST | `/api/v1/negocios` | ✓ | Crear negocio |
| GET | `/api/v1/negocios/{id}` | — | Obtener negocio |
| PUT | `/api/v1/negocios/{id}` | ✓ | Actualizar negocio |
| DELETE | `/api/v1/negocios/{id}` | ✓ | Eliminar negocio (cascada) |
| GET | `/api/v1/items` | — | Listar items (filtros) |
| POST | `/api/v1/items` | ✓ | Crear item |
| GET | `/api/v1/items/{id}` | — | Obtener item |
| PUT | `/api/v1/items/{id}` | ✓ | Actualizar item |
| DELETE | `/api/v1/items/{id}` | ✓ | Eliminar item |
| GET | `/api/v1/resenas` | — | Listar reseñas (filtros) |
| POST | `/api/v1/resenas` | ✓ | Crear reseña |
| GET | `/api/v1/resenas/{id}` | — | Obtener reseña |
| PUT | `/api/v1/resenas/{id}` | ✓ | Actualizar reseña |
| DELETE | `/api/v1/resenas/{id}` | ✓ | Eliminar reseña |
### Filtros y paginación
Todos los listados aceptan `skip` (≥0) y `limit` (1–100) y devuelven una
respuesta paginada `{ total, skip, limit, items }`.
- `GET /negocios?categoria_id=...&activo=true&buscar=pepe`
- `GET /items?negocio_id=...&tipo=menu&activo=true`
- `GET /resenas?negocio_id=...`
- `GET /categorias?parent_id=...`
## Reglas de negocio
- **Integridad referencial:** al crear un negocio se valida que la
categoría exista; al crear items y reseñas se valida que el negocio
exista.
- **Borrado en cascada:** eliminar un negocio elimina también sus items
y reseñas.
- **Categorías:** no se puede eliminar una categoría que tenga
subcategorías o negocios asociados.
- **Slug único:** el `slug` de la categoría es único; se autogenera a
partir del nombre si no se proporciona.
## Calidad de código
El código sigue PEP 8 (límite de línea de 99 columnas, configurado en
`setup.cfg`). Para verificarlo:
```bash
pip install pycodestyle pyflakes
pyflakes app/
pycodestyle app/
```