{"id":50956521,"url":"https://github.com/ronalc90/santiago","last_synced_at":"2026-06-18T07:32:32.266Z","repository":{"id":361413371,"uuid":"1254163039","full_name":"ronalc90/santiago","owner":"ronalc90","description":"Plataforma interna de e-commerce/dropshipping: spy de anuncios, generador de landings con IA y dashboard.","archived":false,"fork":false,"pushed_at":"2026-06-10T03:38:44.000Z","size":638,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T05:16:27.040Z","etag":null,"topics":["ai","bullmq","dashboard","dropshipping","ecommerce","nextjs","postgresql","prisma","railway","redis","tailwindcss","typescript","vercel"],"latest_commit_sha":null,"homepage":"https://winspy-chi.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ronalc90.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T08:05:56.000Z","updated_at":"2026-06-10T03:38:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ronalc90/santiago","commit_stats":null,"previous_names":["ronalc90/santiago"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ronalc90/santiago","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronalc90%2Fsantiago","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronalc90%2Fsantiago/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronalc90%2Fsantiago/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronalc90%2Fsantiago/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ronalc90","download_url":"https://codeload.github.com/ronalc90/santiago/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronalc90%2Fsantiago/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34481313,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","bullmq","dashboard","dropshipping","ecommerce","nextjs","postgresql","prisma","railway","redis","tailwindcss","typescript","vercel"],"created_at":"2026-06-18T07:32:31.164Z","updated_at":"2026-06-18T07:32:32.259Z","avatar_url":"https://github.com/ronalc90.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WinSpy — Plataforma interna de e-commerce / dropshipping\n\nPlataforma web interna que unifica tres módulos en una sola app:\n\n1. **Spy de anuncios** — repositorio y análisis de productos ganadores detectados en Meta Ad Library.\n2. **Generador de landings** — genera 9 imágenes de página de ventas con IA (Google Gemini), texto en español.\n3. **Dashboard** — KPIs, pipeline de producto y gestión de tiendas/productos.\n\nUso interno para 2 socios, pero el código está preparado para escalar a multiusuario (modelo `User` con `role`).\n\n---\n\n## Stack\n\n| Capa | Tecnología |\n|------|-----------|\n| Frontend | Next.js 14 (App Router) + TypeScript + Tailwind CSS + shadcn/ui (modo oscuro) |\n| Backend | Next.js API routes (route handlers) + TypeScript |\n| Base de datos | PostgreSQL + Prisma ORM (migraciones incluidas) |\n| Auth | email/password con sesiones en BD (cookie httpOnly), roles ADMIN/MEMBER |\n| Cola de trabajos | BullMQ + Redis (worker en proceso separado) |\n| Imágenes | Google Gemini (configurable) con compresión a WebP (`sharp`) |\n| Almacenamiento | Adaptador local (dev) o S3/R2 (prod), seleccionable por env |\n\n### Arquitectura por capas\n\n```\napp/            UI (Server/Client Components) + route handlers (API)\nlib/services/   Lógica de negocio (scoring, ads, landing, settings)  ← testeable\nlib/validation/ Esquemas zod (validación de entrada)\nlib/auth/       Sesiones, guards, hashing\nlib/images/     Generador de imágenes (interfaz + Gemini + mock) y compresión\nlib/storage/    Adaptador de almacenamiento (interfaz + local + s3)\nlib/queue/      Cola BullMQ (productor)\nlib/db.ts       Acceso a datos (Prisma)\nworker/         Consumidor de la cola (proceso separado)\nprisma/         Esquema, migraciones y seed\ntests/          Tests de la lógica crítica (vitest)\n```\n\nLa restricción clave del negocio se respeta: **el scraping en vivo de Meta lo hace una skill externa en Claude in Chrome**. El backend solo **recibe** los datos por el endpoint de ingesta o el importador manual; nunca scrapea Facebook.\n\n---\n\n## Requisitos\n\n- Node.js 20+ (probado en Node 22)\n- Docker + Docker Compose (para Postgres y Redis)\n- npm\n\n---\n\n## Instalación y arranque (desarrollo)\n\n```bash\n# 1) Variables de entorno\ncp .env.example .env       # (ya hay un .env listo para dev; revísalo)\n\n# 2) Levantar Postgres y Redis\ndocker compose up -d\n\n# 3) Instalar dependencias\nnpm install\n\n# 4) Migraciones + cliente Prisma + datos de ejemplo\nnpm run db:migrate         # crea las tablas (primera vez: nombra la migración, p.ej. \"init\")\nnpm run db:seed            # SOLO desarrollo: carga tiendas, anuncios y una landing demo\n\n# 5) Levantar la app (terminal 1)\nnpm run dev                # http://localhost:3000\n\n# 6) Levantar el worker de imágenes (terminal 2)\nnpm run worker\n```\n\n### Usuario admin\n\nEl usuario admin se crea con las variables `ADMIN_EMAIL` y `ADMIN_PASSWORD` (no hay\ncredenciales fijas en el repo):\n\n- En **desarrollo**, `ADMIN_EMAIL` tiene un default (`socio1@winspy.local`).\n- `ADMIN_PASSWORD` **no** tiene default: si no la defines, se genera una clave aleatoria\n  fuerte y se imprime **una sola vez** por consola.\n- En **producción**, define ambas y usa una `ADMIN_PASSWORD` fuerte. Detalle en `DEPLOY.md`.\n\n\u003e El seed (`npm run db:seed`) es solo para desarrollo: carga datos demo. No lo corras\n\u003e contra producción.\n\n---\n\n## Variables de entorno\n\nVer `.env.example` (documentado). Las más importantes:\n\n| Variable | Descripción |\n|----------|-------------|\n| `DATABASE_URL` | Cadena de conexión a PostgreSQL |\n| `REDIS_URL` | Conexión a Redis (cola BullMQ) |\n| `AUTH_SECRET` | Secreto de sesiones (genera: `openssl rand -base64 32`) |\n| `INGEST_API_TOKEN` | Token que la skill del spy envía en el header `x-ingest-token` |\n| `IMAGE_PROVIDER` | `mock` (placeholders, sin costo) o `gemini` (API real) |\n| `GEMINI_API_KEY` | Clave de la API de Gemini |\n| `GEMINI_IMAGE_MODEL` | Modelo de generación de imágenes (nano banana). **Confírmalo** con tu cuenta |\n| `GEMINI_TEXT_MODEL` | Modelo de texto/visión para analizar la imagen de referencia |\n| `STORAGE_DRIVER` | `local` o `s3` |\n\n\u003e **Validación:** `lib/config/env.ts` valida todas las variables al arrancar y falla con mensajes claros si falta algo. Ningún módulo lee `process.env` directamente.\n\n### Probar la conexión a Gemini\n\n```bash\nnpx tsx scripts/test-gemini.ts\n```\n\nConfirma que la key responde y que el nombre del modelo es válido **antes** de poner `IMAGE_PROVIDER=gemini`. Para imágenes, ajusta `GEMINI_IMAGE_MODEL` al modelo de imagen (nano banana) al que tu key tenga acceso.\n\n---\n\n## Ingesta de datos del spy\n\nLa skill de Claude in Chrome envía los anuncios al endpoint de ingesta:\n\n```bash\ncurl -X POST http://localhost:3000/api/ads/ingest \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-ingest-token: \u003cINGEST_API_TOKEN\u003e\" \\\n  -d '{\n    \"ads\": [{\n      \"store_name\": \"GadgetPro CO\",\n      \"country\": \"CO\",\n      \"ad_id\": \"AD-CO-9001\",\n      \"ad_library_url\": \"https://www.facebook.com/ads/library/?id=AD-CO-9001\",\n      \"copy_text\": \"Producto ganador…\",\n      \"days_active\": 12,\n      \"estimated_spend\": 18000,\n      \"creative_url\": \"https://…/img.jpg\"\n    }]\n  }'\n```\n\nTambién puedes **importar manualmente** (pegar JSON o CSV, o subir archivo) desde la UI: Spy → *Importar resultados del spy*. La **deduplicación es por `ad_id`** (reingestar el mismo anuncio actualiza métricas y lo marca como histórico).\n\n---\n\n## Lógica de negocio\n\n- **Winner Score** = `gasto estimado / días activos` (intensidad de inversión diaria).\n- **Clasificación** automática y configurable desde *Ajustes*:\n  - 🔴 **LANZAR** (score ≥ umbral alto)\n  - 🟡 **CONSIDERAR** (score medio)\n  - 🟢 **MONITOREAR** (señal temprana)\n  - ⚪ **SATURADO** (demasiados días activo)\n- Al guardar nuevas reglas, **todos los anuncios se reclasifican** automáticamente.\n\n---\n\n## Scripts npm\n\n| Comando | Qué hace |\n|---------|----------|\n| `npm run dev` | App en desarrollo |\n| `npm run worker` | Worker de generación de imágenes |\n| `npm run build` | `prisma generate` + build de producción |\n| `npm start` | App en producción |\n| `npm test` | Tests (vitest) |\n| `npm run db:migrate` | Migraciones de desarrollo |\n| `npm run db:deploy` | Migraciones en producción |\n| `npm run db:seed` | Datos de ejemplo |\n| `npm run db:studio` | Prisma Studio (explorar la BD) |\n| `npm run db:reset` | Resetea la BD (¡borra datos!) |\n| `npm run typecheck` | `tsc --noEmit` |\n| `npm run lint` | ESLint |\n\n---\n\n## Tests\n\n```bash\nnpm test\n```\n\nCubren la lógica crítica: cálculo del Winner Score, clasificación por reglas (incluyendo límites y reglas personalizadas), señal de demanda en otro país, validación/normalización de la ingesta y la especificación de las 9 imágenes (incl. compliance TikTok). 23 tests.\n\n---\n\n## Despliegue a producción (Vercel + worker en Railway)\n\nVercel corre en serverless y **no puede ejecutar el worker persistente** de BullMQ. Por eso:\n\n1. **Web/API → Vercel.** Importa el repo, define las variables de entorno (usa Postgres gestionado tipo Neon y Redis tipo Upstash). Vercel ejecuta `npm run build`.\n2. **Worker → Railway/Render.** Despliega el mismo repo como servicio con comando de inicio `npm run worker`, apuntando al **mismo** `DATABASE_URL` y `REDIS_URL`. El worker llama a Apify y a Gemini, así que necesita además `AUTH_SECRET`, `INGEST_API_TOKEN` (sin estas dos **crashea al arrancar**), el bloque `AD_SOURCE_*`/`APIFY_*`, `GEMINI_*` y `S3_*`. Ver la tabla completa en `DEPLOY.md`.\n3. **Almacenamiento → S3/R2.** Pon `STORAGE_DRIVER=s3`, instala `@aws-sdk/client-s3` y rellena las `S3_*`. (El adaptador local solo sirve para dev.)\n4. Migraciones en prod: `npm run db:deploy`. El admin se crea con `ADMIN_EMAIL`/`ADMIN_PASSWORD`; el seed demo (`npm run db:seed`) **no** se corre en producción.\n\nLa cola está detrás de una abstracción (`lib/queue`), así que se puede cambiar el destino sin tocar la lógica de negocio.\n\n---\n\n## Seguridad\n\n- Validación de entrada con **zod** en todos los endpoints.\n- Secretos solo por entorno; nada hardcodeado. El `.env` está en `.gitignore`.\n- Contraseñas con **bcrypt**; sesiones opacas en BD con expiración.\n- Ingesta protegida por token con comparación en **tiempo constante**.\n- Rutas protegidas por middleware + verificación real en cada Server Component / route handler.\n- Manejo de errores con `try/catch` y mensajes claros en toda llamada externa (Gemini, ingesta, importación).\n\n---\n\n## Notas de verificación\n\n- Esquema Prisma validado y cliente generado correctamente.\n- Lógica de negocio con typecheck `strict` en verde y 23 tests pasando.\n- Revisión de código de la UI/API (imports, exports, tipos de props, Server/Client Components) sin errores que rompan el build.\n- El `next build` final se ejecuta en tu máquina al correr `npm install \u0026\u0026 npm run dev`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronalc90%2Fsantiago","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fronalc90%2Fsantiago","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronalc90%2Fsantiago/lists"}