LeadRadar es una plataforma para captacion de leads locales con Next.js, Prisma y PostgreSQL. Incluye busqueda geolocalizada, deduplicacion, scoring, campanas de email, plantillas por categoria, IA por lead y procesamiento asincrono con worker.
- Next.js 14 (App Router) + React 18 + TypeScript
- Prisma + PostgreSQL
- NextAuth v5
- pg-boss (cola sobre PostgreSQL)
- Resend (envio/tracking de emails)
- Tailwind CSS
- Turborepo
- Node.js 18+ (recomendado 22)
- npm 9+
- PostgreSQL
Crear apps/web/.env.local usando apps/web/.env.example.
Variables principales:
DATABASE_URLDIRECT_URLAPP_BASE_URLNEXTAUTH_SECRETNEXTAUTH_URLPASSWORD_RESET_TOKEN_TTL_MINUTESSERPAPI_API_KEYRESEND_API_KEYRESEND_WEBHOOK_SECRETRESEND_FROM_EMAILRESEND_REPLY_TOAI_BASE_URLAI_MODELAI_API_KEYNEXT_PUBLIC_MAPBOX_TOKEN
Variables operativas recomendadas (evitan saturar conexiones):
PGBOSS_MAX_CONNECTIONS=1WORKER_PGBOSS_MAX_CONNECTIONS=1WORKER_CONCURRENCY=1PRISMA_LOG_QUERIES=false
Configuracion recomendada con Supabase:
DATABASE_URL: URL pooled para runtime.- Vercel / serverless: usar Supavisor transaction mode con
connection_limit=1. - Worker persistente: usar direct connection o session mode si necesitas pooler IPv4.
- Vercel / serverless: usar Supavisor transaction mode con
DIRECT_URL: URL para Prisma CLI (migrate deploy).- usar direct connection si tu entorno soporta IPv6.
- si no, usar session mode de Supavisor.
APP_BASE_URL: URL publica canonica usada en emails de bienvenida y recuperacion.- ejemplo en produccion:
https://tu-dominio.com - no debe apuntar a
localhost
- ejemplo en produccion:
- Instalar dependencias:
npm install- Generar cliente Prisma:
npm run db:generate --workspace=apps/web- Aplicar migraciones:
npx prisma migrate deploy --schema=apps/web/prisma/schema.prisma- Levantar app web (terminal 1):
npm run dev- Levantar worker (terminal 2):
npm run worker:start --workspace=apps/webSin worker, los batches de enriquecimiento quedan en PENDING.
Desde la raiz:
npm run dev
npm run lint
npm run type-check
npm run test
npm run buildDesde apps/web:
npm run worker:start
npm run worker:once
npm run leads:backfill-dedupe
npm run db:generate
npm run db:migrate
npm run build
npm run start:hostingerAl ejecutar npm run build dentro de apps/web, Next genera:
.next/standalone.next/staticdist-hostinger
dist-hostinger queda preparado para despliegue manual en un hosting Node como Hostinger.
En Campaigns -> Nueva campana:
Enriquecer emails faltantes: procesa solo la categoria seleccionada.Enriquecer todas las categorias: procesa todo tu dataset elegible.
La UI muestra:
- barra de progreso por
batchId - consola tipo terminal con eventos por lead
- estados
PENDING | PROCESSING | DONE | FAILED
Criterio de elegibilidad:
- lead sin email
- lead con
website
POST /api/auth/register- crea usuario y envia email de bienvenida si Resend esta configurado
POST /api/auth/forgot-password- genera token de recuperacion y envia email de reset
POST /api/auth/reset-password- valida token y actualiza la contrasena
POST /api/v1/search- devuelve leads inmediatamente (compatible)
metaincluye:fetched,insideRadius,created,updated,deduped,queuedForEnrichment, etc.
GET /api/v1/leads- orden por
leadScore desc, createdAt desc
- orden por
GET /api/v1/leads/stats- total / con email / sin email / candidatos a enriquecimiento
POST /api/v1/leads/enrich-emails- crea lote (
batchId) de enriquecimiento
- crea lote (
GET /api/v1/leads/enrich-emails/[batchId]/progress- progreso consolidado del lote
POST /api/v1/webhooks/resend- tracking de eventos de email firmado
Levantar todo:
docker compose up --buildServicios:
db(PostgreSQL)web(Next.js)worker(jobs de leads y reconciliacion de campanas)
Nota: en Docker revisa WORKER_CONCURRENCY si tu plan de BD tiene limite bajo de conexiones.
Este repo incluye un entrypoint que permite usar la misma imagen para ambos servicios.
Variables importantes:
SERVICE_ROLE=weboSERVICE_ROLE=workerRUN_MIGRATIONS=true(por defecto)DATABASE_URL(oWORKER_DATABASE_URLpara worker)
Comportamiento:
- al iniciar, ejecuta
prisma migrate deploysiRUN_MIGRATIONS=true - luego arranca
next start(web) oworker:start(worker)
Nota: las migraciones crean/actualizan tablas existentes, pero no crean la base de datos si no existe.
Si no quieres depender de Vercel, puedes desplegar la web manualmente en un hosting Node.
- Genera el paquete:
cd apps/web
npm run build-
Se creara
apps/web/dist-hostinger. -
Sube todo el contenido de
dist-hostingeral servidor de Hostinger. -
Configura el comando de inicio:
node start.js- Configura en Hostinger las variables de entorno necesarias:
DATABASE_URLDIRECT_URLAPP_BASE_URLNEXTAUTH_URLAUTH_URLNEXTAUTH_SECRETAUTH_SECRETNEXT_PUBLIC_MAPBOX_TOKENSERPAPI_API_KEYRESEND_API_KEYRESEND_FROM_EMAILRESEND_REPLY_TO
- Aplica migraciones antes del upload desde tu entorno local o CI si la base de datos no esta actualizada:
cd apps/web
npx prisma migrate deploy --schema=prisma/schema.prismaNotas:
dist-hostinger/HOSTINGER_DEPLOY.txtincluye un recordatorio del comando de arranque.dist-hostinger/.env.examplete sirve como base para configurar variables en Hostinger.- Este despliegue sirve para la web. El worker sigue siendo un proceso separado si quieres enriquecimiento y reconciliacion asincrona.
Causa: Prisma Client generado en modo Data Proxy (--no-engine).
Solucion:
npm run db:generate --workspace=apps/webCausa: codigo actualizado pero migraciones sin aplicar.
Solucion:
npx prisma migrate deploy --schema=apps/web/prisma/schema.prismaCausa: demasiadas conexiones concurrentes (web + worker + polling).
Solucion recomendada:
PGBOSS_MAX_CONNECTIONS=1WORKER_PGBOSS_MAX_CONNECTIONS=1WORKER_CONCURRENCY=1- dejar un solo worker ejecutandose
- usar
DATABASE_URLpooled para runtime - usar
DIRECT_URLpara migraciones Prisma - no compartir la base de produccion con
localhostsalvo para pruebas puntuales
Checklist:
- worker activo (
npm run worker:start --workspace=apps/web) DATABASE_URLcorrecto en web y worker- migraciones aplicadas
- no superar conexiones maximas de la BD
Causa habitual: credenciales incorrectas en el servicio worker (o password no codificado en URL).
Checklist:
- definir en Render
WORKER_DATABASE_URL(oDATABASE_URL) en el servicio del worker - no envolver el valor con comillas (
"o') - si el password tiene caracteres especiales (
@,:,/,#,%), usar URL encoding - confirmar que web y worker apuntan a la misma base de datos
- redeploy del worker tras actualizar variables