Tienda en línea de ropa casual y elegante para dama y caballero en Guatemala. El backend es una API REST en FastAPI con SQLAlchemy sobre MySQL, con control de acceso por roles y permisos y una bitácora de auditoría. El frontend, en React, incluye la tienda y un panel de administración.
Proyecto del curso de Análisis en la Universidad Mariano Gálvez de Guatemala (2026).
| Inicio | Catálogo |
|---|---|
![]() |
![]() |
| Detalle de producto | Panel de administración |
![]() |
![]() |
- Catálogo con búsqueda y filtros, alimentado por la API.
- Detalle de producto con colores, tallas y existencias disponibles.
- Registro e inicio de sesión de clientes.
- Carrito lateral con cálculo de IVA del 12 % en quetzales.
- Indicadores de ventas registradas, pedidos activos, productos con pocas existencias y total de productos.
- Gestión de productos: alta, edición (incluidos precio y existencias) y activación o desactivación. La API también ofrece endpoints dedicados para cambiar el precio y reponer existencias.
- Seguimiento de pedidos por estado: pendiente, en proceso, enviado, entregado o cancelado.
- Promociones por porcentaje o monto fijo, con compra mínima y vigencia.
- Reporte de ventas por mes y año con ticket promedio.
- Acceso del usuario maestro por una ruta oculta y configurable.
- 36 operaciones REST en español: autenticación, acceso maestro, productos, promociones, carrito, pedidos, pagos, recibos y administración.
- Flujo de compra completo: carrito → pedido (calcula el IVA y descuenta existencias) → pago → recibo con NIT → seguimiento o cancelación del pedido.
- Bitácora de auditoría de inicios y cierres de sesión, altas y cambios.
- Documentación interactiva generada por FastAPI en
/docs.
- Contraseñas con PBKDF2-SHA256 de 260 000 iteraciones y sal aleatoria, comparadas en tiempo constante con
hmac.compare_digest. - Tokens JWT (HS256) con vigencia de 2 horas.
- Roles y permisos en base de datos, verificados en cada endpoint administrativo con la dependencia
require_permission:
| Rol | Permisos |
|---|---|
cliente |
Ninguno administrativo; solo accede a su perfil, su carrito, sus pedidos y sus recibos |
admin_inventario |
Crear y actualizar productos, cambiar precios, reponer existencias, gestionar descuentos, administrar pedidos y ver reportes |
maestro |
Los 11 permisos, incluidos ver la auditoría y administrar roles |
- Usuario maestro separado: el inicio de sesión normal lo rechaza y solo entra por su propia ruta, configurable con
MASTER_ROUTEyVITE_RUTA_MAESTRA. - Rutas protegidas en el frontend según el permiso que requiere cada pantalla.
- Configuración por variables de entorno y CORS limitado a los orígenes configurados.
flowchart LR
ui["Frontend<br/>React + Vite"] -->|"HTTP + JWT"| api["API REST<br/>FastAPI"]
api --> dep["Dependencias<br/>autenticación y permisos"]
api --> orm["SQLAlchemy 2.0"]
orm -->|"PyMySQL"| db[("MySQL 8<br/>(Docker)")]
Al iniciar, el backend crea las tablas que falten y garantiza que existan los roles, los permisos y el usuario maestro.
database/schema.sql crea 19 tablas con 23 claves foráneas, 6 restricciones CHECK, estados definidos con ENUM y una bitácora que guarda los cambios en columnas JSON. Los seeds cargan los roles y permisos, el usuario maestro, dos categorías y ocho productos de ejemplo con sus imágenes, tallas y colores.
erDiagram
role ||--o{ role_permission : "role_id"
permission ||--o{ role_permission : "permission_id"
role ||--o{ user : "role_id"
user ||--o{ address : "user_id"
category ||--o{ product : "category_id"
product ||--o{ product_image : "product_id"
product ||--o{ product_size : "product_id"
product ||--o{ product_color : "product_id"
product ||--o{ product_promotion : "product_id"
promotion ||--o{ product_promotion : "promotion_id"
user ||--o{ cart : "user_id"
cart ||--o{ cart_detail : "cart_id"
product ||--o{ cart_detail : "product_id"
product_size ||--o{ cart_detail : "product_size_id"
product_color ||--o{ cart_detail : "product_color_id"
user ||--o{ user_order : "user_id"
cart ||--o{ user_order : "cart_id"
address ||--o{ user_order : "shipping_address_id"
user_order ||--o{ order_detail : "order_id"
product ||--o{ order_detail : "product_id"
user_order ||--o{ payment : "order_id"
user_order ||--o{ receipt : "order_id"
user ||--o{ audit_log : "user_id"
| Parte | Estado |
|---|---|
| API y base de datos | Flujo de compra y administración completos |
| Frontend: autenticación, catálogo, detalle de producto y panel de administración | Conectados a la API |
| Frontend: página del carrito, checkout, historial de pedidos, perfil y banners | Prototipo visual con datos de demostración; todavía no usan los endpoints de carrito, pedidos y pagos |
| Grupo | Rutas |
|---|---|
| Salud | GET /api/salud |
| Autenticación | POST /api/autenticacion/registro, POST /login, POST /logout, GET y PUT /perfil, POST /cambiar-contrasena |
| Acceso maestro | POST /api/maestro/login |
| Productos | GET y POST /api/productos, GET y PUT /{id}, PATCH /{id}/precio, PATCH /{id}/reestock, PATCH /{id}/estado |
| Promociones | GET y POST /api/promociones, PUT y DELETE /{id} |
| Carrito | GET y DELETE /api/carrito, POST /items, PATCH y DELETE /items/{id}, POST /cupon |
| Pedidos, pagos y recibos | GET y POST /api/pedidos, GET /{id}, PATCH /{id}/cancelar, POST /api/pagos, GET /api/recibos/{id} |
| Administración | GET /api/admin/dashboard, GET /pedidos, PATCH /pedidos/{id}/estado, GET /reportes, GET /auditoria |
Urban-Fashion-Lab/
├── backend/
│ ├── app/
│ │ ├── core/ # Configuración, conexión y seguridad
│ │ ├── models/ # Entidades de SQLAlchemy
│ │ ├── routers/ # Endpoints por módulo
│ │ ├── schemas/ # Validación con Pydantic
│ │ ├── bootstrap.py # Roles, permisos y usuario maestro
│ │ ├── dependencies.py # Usuario actual y permisos
│ │ └── main.py
│ ├── tests/
│ └── requirements.txt
├── database/
│ ├── schema.sql
│ ├── migrations/
│ └── seeds/
├── frontend/
│ └── src/
│ ├── components/ # Catálogo, carrito, checkout, admin y comunes
│ ├── context/ # Sesión y carrito
│ ├── pages/ # Tienda y panel de administración
│ ├── routes/ # Rutas y protección por permisos
│ └── services/ # Cliente de la API
└── docs/capturas/
- Python 3.13
- Node.js 18 o superior
- MySQL 8, por ejemplo con Docker
Con Docker:
docker run -d --name urban-fashion-mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:8.4Cuando el contenedor termine de iniciar, crea las tablas y carga los datos:
docker exec -i urban-fashion-mysql mysql -uroot -proot < database/schema.sql
docker exec -i urban-fashion-mysql mysql -uroot -proot < database/seeds/01_categories.sql
docker exec -i urban-fashion-mysql mysql -uroot -proot < database/seeds/02_users.sql
docker exec -i urban-fashion-mysql mysql -uroot -proot < database/seeds/03_products.sqlSi ya tienes MySQL instalado, usa mysql -u root -p < archivo.sql con los mismos archivos y en el mismo orden.
cd backend
python -m venv .venv
source .venv/bin/activate # En Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # Ajusta la contraseña en DATABASE_URL
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000La documentación de la API queda en http://127.0.0.1:8000/docs.
cd frontend
cp .env.example .env
npm install
npm run devLa tienda se abre en http://localhost:3000.
El seed crea un usuario maestro para entrar al panel desde /acceso-interno-ufl:
- Correo:
maestro@urbanfashionlab.local - Contraseña:
Admin123!
Cambia esa contraseña antes de usar el sistema en un ambiente real.
cd backend
PYTHONPATH=. pytest tests -q # En PowerShell: $env:PYTHONPATH='.'; pytest tests -qLas pruebas verifican que las rutas en español estén registradas y que funcionen el hash de contraseñas y los tokens JWT.
- Conectar la página del carrito, el checkout, el historial de pedidos y el perfil a los endpoints que ya existen en la API.
- Aplicar las promociones en el carrito: el endpoint de cupones todavía no calcula descuentos.
- Devolver las existencias al cancelar un pedido y validarlas de nuevo al confirmarlo.
- Mostrar en el frontend los mensajes de error del backend, que FastAPI envía en el campo
detail. - Contar solo los productos activos en el indicador del panel.
- Reemplazar
@app.on_eventydatetime.utcnow(), obsoletos en versiones recientes, porlifespany fechas con zona horaria. - Añadir pruebas de integración para el flujo de compra.
Erica Franco · @Erica4fr




