Plataforma integral para la venta de cartas singles (Magic, Pokémon, Yu-Gi-Oh, One Piece) con flujo de datos TCGCSV-first, precios dinámicos, inventario masivo y panel de administración completo.
La base activa del proyecto hoy corre con frontend en Vercel, backend en Render, caché en Upstash y base de datos PostgreSQL en Neon. El material histórico de despliegues, migraciones y workflows antiguos quedó resguardado en carpetas legacy para revisión o recuperación rápida.
.
├── backend/ # API REST Node.js/Express + TypeScript
│ ├── src/
│ │ ├── index.ts # Entry point
│ │ ├── models/ # Tipos y modelos
│ │ ├── services/ # Lógica de negocio (Pricing, TCG, Cards, etc.)
│ │ ├── routes/ # Rutas API
│ │ ├── controllers/ # Controladores
│ │ ├── middleware/ # Auth, validación, etc.
│ │ └── utils/ # BD, Redis, helpers
│ ├── prisma/
│ │ └── schema.prisma # Modelo de datos
│ ├── package.json
│ └── tsconfig.json
│
├── frontend/ # App React + Vite
│ ├── src/
│ │ ├── main.tsx
│ │ ├── App.tsx
│ │ ├── components/ # Componentes reutilizables
│ │ ├── pages/ # Páginas (Catálogo, Admin, Checkout)
│ │ ├── services/ # Clientes API
│ │ ├── hooks/ # React hooks personalizados
│ │ └── types/ # Tipos TypeScript
│ ├── index.html
│ ├── package.json
│ └── tsconfig.json
│
├── .env.example # Variables de entorno (ejemplo)
└── README.md # Este archivo
- Búsqueda por nombre, código y tags
- Filtrado por TCG (Magic, Pokémon, Yu-Gi-Oh, One Piece)
- Separación por edición y código de carta
- Visualización de stock disponible
- Importación masiva de sets/cartas con TCGCSV como fuente principal y conectores secundarios por TCG
- Sincronización TCGCSV-first para catálogo y precios de referencia
- Fuente única: TCGCSV
- Conversión automática a CLP (tipo de cambio actualizado)
- Margen configurable y control de volatilidad
- Historial de cambios de precio y auditoría
- Actualización automática (cron configurable)
- Importación masiva por CSV/XLSX
- Edición rápida de cantidades
- Alertas de stock bajo
- Vista de inventario total y valor
- Historial de importaciones y errores
- Carrito persistente por sesión
- Bloqueo de unidades para evitar sobreventa
- Generación de órdenes
- Integración de pagos (futuro)
- Dashboard con KPIs (valor total, margen, stock bajo)
- Gestión de precios, márgenes, TCGs y ediciones
- Carga de catálogo e inventario
- Auditoría de cambios y reportes
- Usa el panel "Catalog Sync Console" para:
- Dry run: simula cambios antes de aplicar
- Crear listings: activa/desactiva creación de inventario
- Bootstrap catálogo: carga masiva inicial
- Sync sets nuevos: detecta e importa sets nuevos
- Política de fuentes: TCGCSV-only
- Alta de set nuevo: dry run → sync sets nuevos → revisar → ejecutar real
- Sincronización de precios: manual o cron, revisar volatilidad
- Restock: importar CSV/XLSX, validar y confirmar
- Checkout: ventas descuentan stock automáticamente
- Soporta search, import sets, sync automático, precios USD
- Flujo principal: TCGCSV
- Día 0: bootstrap catálogo, revisar cobertura
- Semanal: sync sets nuevos, revisar precios/stock
- Diario: importar restock, procesar ventas
- Node.js 18+, Express.js, TypeScript
- Prisma (PostgreSQL), Redis (cache), node-cron (jobs)
- React 18, Vite, TypeScript
- Axios, CSS puro (opcional Tailwind/styled-components)
- Node.js 18.0.0 o superior
- npm o yarn
- PostgreSQL 14+
- Redis (opcional, pero recomendado para producción)
git clone <repo-url>
cd test-netdeckercd backend
npm install
# Crear archivo .env basado en .env.example
cp .env.example .env
# Editar .env con tus credenciales
# DATABASE_URL=postgresql://user:password@localhost:5432/tcg_singles_db
# REDIS_URL=redis://localhost:6379
# Ejecutar migraciones
npm run prisma:push
# Inicializar datos por defecto (TCGs)
npm run prisma:seedcd ../frontend
npm installcd backend
npm run devEl servidor estará disponible en http://localhost:3333
cd frontend
npm run devLa app estará disponible en http://localhost:3000
En desarrollo, el frontend redirige automáticamente las peticiones /api/* a http://localhost:3333/api/* mediante el proxy de Vite.
Si parece que la sincronización automática de precios no corre en local, revisa lo siguiente:
- Verifica que el backend arrancó una sola vez
- Si ves
EADDRINUSE(puerto ocupado), el proceso falla antes de iniciar los cron jobs. - Asegúrate de no tener dos backends escuchando en el mismo puerto.
- Confirma que el scheduler quedó activo en logs
- Debes ver:
[PriceSyncJob] Scheduled with cron expression: ...
- Recuerda la frecuencia configurada
PRICE_SYNC_CRON="0 */6 * * *"ejecuta cada 6 horas, por lo que en desarrollo puede parecer que "no corre".
- Modo seguro para desarrollo (recomendado)
PRICE_SYNC_DEV_SAFE_MODE="true"(por defecto endevelopment) hace cron más observable:inventoryOnly=truefetchExternalPrices=false
- Esto evita corridas largas por APIs externas y reduce
Previous run still in progress.
- Cómo desactivar el modo seguro
- Si quieres comportamiento completo también en local:
PRICE_SYNC_DEV_SAFE_MODE="false"
- Inspección rápida de corridas
- Endpoint útil para diagnóstico:
GET /api/listings/sync-prices/runs?limit=5
- Revisa
status(running,completed,failed) y tiemposstartedAt/completedAt.
GET /api/tcgs- Obtener todos los TCGsGET /api/tcgs/:id- Obtener TCG específico
GET /api/cards/search?name=xxx- Buscar cartas por nombreGET /api/cards/:id- Obtener carta específicaGET /api/cards/edition/:editionId- Cartas por ediciónGET /api/cards/tcg/:tcgId- Cartas por TCG
GET /api/listings/available- Cartas disponibles (con stock)GET /api/listings/low-stock- Alertas de stock bajoGET /api/listings/inventory-value- Valor total del inventarioGET /api/listings/:id- Listing específicoGET /api/listings/card/:cardId- Listings de una carta
POST /api/inventory/update-quantity- Actualizar cantidadPOST /api/inventory/bulk-update- Carga masiva de cantidadesPOST /api/inventory/decrease- Reducir cantidad (compra)POST /api/inventory/import-csv- Carga masiva desde CSV (upsert catálogo + stock)GET /api/inventory/import-csv/template- Descarga plantilla CSV
GET /api/cart/:sessionId- Obtener carritoPOST /api/cart/:sessionId/add- Agregar al carritoPATCH /api/cart/:sessionId/item/:itemId- Actualizar cantidad de itemDELETE /api/cart/:sessionId/item/:itemId- Quitar item del carritoPOST /api/cart/:sessionId/checkout- Finalizar compra
Los datos están organizados en las siguientes entidades principales:
- Categoría de juego (Magic, Pokémon, Yu-Gi-Oh, One Piece)
- Contiene múltiples ediciones
- Versión/set de un TCG (ej. Magic 2025, Pokémon SV05)
- Agrupa cartas de una misma colección
- Carta individual con identificador único por TCG/Edición/Código
- Metadata: nombre, rarity, tags, imagen, descripción
- Inventario de una carta en una condición específica
- Contiene: cantidad, precio de referencia, margen, precio final en CLP
- Auditoría de cambios de precio
- Registra origen del cambio (sync, manual, etc.) y % de cambio
finalPrice (CLP) = referencePrice (USD) × marginMultiplier × exchangeRate (USD/CLP)
Ejemplos:
- Carta con precio TCGplayer $10 USD, margen 1.2 (20%), tasa 850 CLP/USD
- Precio final: $10 × 1.2 × 850 = $10.200 CLP
- Implementar endpoints base de checkout y órdenes
- Integración de catálogo/precios centralizada en TCGCSV
- Job de sincronización automática de precios (cada 6 horas)
- Soporte completo para One Piece
- Integración con pagos (Stripe, Mercado Pago)
- Panel admin con autenticación
- Notificaciones de cambios de precio
- Búsqueda avanzada con filtros múltiples
- Recomendaciones y trending cards
- Integración con múltiples fuentes de precio adicionales
Ver .env.example en backend/ para la lista completa.
Principales:
DATABASE_URL- Conexión PostgreSQLREDIS_URL- Conexión RedisPORT- Puerto de la API (default: 3333)NODE_ENV- Entorno (development/production)EXCHANGE_RATE_API_URL- URL para obtener tasas de cambioPRICE_SYNC_ENABLED- Habilitar sync automático de preciosPRICE_SYNC_CRON- Configuración de cron job para sync (ej: "0 */6 * * *" = cada 6 horas)
-
Yu-Gi-Oh — edición duplicada por carta
ygoCardToExternalusaba el campocard_sets[].set_code(código de carta como "LOB-EN001") como código de edición, creando una edición diferente por cada carta.
Ahora se extrae el prefijo de set ("LOB") y cuando haysetFilterse usa siempre como código autoritativo. -
Precio CLP incorrecto en listings creados por importación
ElExternalImportServicecreaba listings conexchangeRate: 1.0, resultando en precios CLP equivalentes a USD.
Ahora se obtiene la tasa real USD→CLP al crear/actualizar cada listing. -
Página
/importarmostraba nada en tab "CSV Stock"
El componenteImportPagetrataba la respuesta paginada{ items, total, page }como un array directamente.
Ahora extrae correctamenteitemsdel objeto de respuesta. -
Resultado de validación CSV no se mostraba
La validación mapeaba mal la respuesta del backend{ result: { total, success, failed, errors } }al formato esperado{ valid, errors, totalRows }.
Ahora extrae y mapea correctamente los campos. -
Dashboard mostraba alertas de stock bajo/sin stock para cartas nunca compradas
Las alertas de stock ahora solo aplican a listings coneverHadStock: true(campo nuevo en Listing).
El campo se pone atrueautomáticamente cuando la cantidad supera 0 por primera vez.
- Ver imagen de carta: Hacer clic en el nombre de cualquier carta en el inventario muestra su imagen en un modal.
- Reset de catálogo: Botón "Resetear Catálogo" en la pestaña CSV Stock (Zona de Peligro) que borra todos los datos de catálogo preservando TCGs y tipo de cambio.
- También disponible vía API:
POST /api/admin/catalog/resetcon{ confirm: true } - Y como script de consola:
cd backend && npm run catalog:reset(requiere--confirm)
- También disponible vía API:
Después de hacer pull de este branch, ejecutar:
cd backend
npm run prisma:push # Aplica el nuevo campo everHadStock a la tabla Listing- Agregar buscador de cartas con autocompletar por TCG dentro del Inventario
- Mejorar UI de precio (mostrar USD y CLP en la misma tabla con el tipo de cambio actual)
- Auto-actualización de sets YGO y One Piece sin importación manual
- Autenticación admin básica
- Confirmación visual al actualizar precios masivamente
Para una guía completa ver DEPLOYMENT_CLOUDFLARE.md.
Resumen rápido:
- Build command (Pages):
npm run build(raíz del repo). Esto generafrontend/dist. - Output directory:
frontend/dist. - Crear D1 DB y añadir binding
TCG_D1en Pages/Wrangler. - Si trabajas en Windows y tienes problemas con
prisma generate, revisabackend/PRISMA_WINDOWS.md.
MIT
Para reportar bugs o solicitar features, abrir un Issue.
Versión: 0.1.0 (MVP - En desarrollo)