Skip to content

Repository files navigation

Pegas devsChile()

Vitrina de ofertas de trabajo tech en Chile. Las pegas se obtienen parseando newsletters de LinkedIn Jobs (y otras fuentes), se almacenan en PostgreSQL y se publican como sitio estático.

Arquitectura

Gmail (newsletters LinkedIn) ─┐
GetOnBoard (API pública v0) ├─→ n8n (parser + dedup) → PostgreSQL → Static Site (nginx)
WorkingNomads (API pública) ─┘ │
pegas.devschile.cl

Componentes

ComponenteDescripciónStack
FrontendSitio estático con buscador y filtrosHTML/CSS/JS vanilla, nginx:alpine
BackendAPI de datosPostgreSQL 16 (Coolify)
IngestiónLee emails y APIs de portales, parsea, guarda en BDn8n workflow
BuildGenera data.json desde la BDNode.js (Dockerfile multi-stage)

Fuentes de pegas

FuenteMétodoFrecuenciaFiltro
LinkedInParseo de newsletter por email (2 casillas Gmail)Cada 6h (Gmail Trigger)Todas las categorías tech detectadas por keyword
GetOnBoardAPI pública v0 (sin auth), n8n/test-getonbrd.js valida el filtroCada 6h (Schedule Trigger)Categorías dev/tech (programming, mobile-developer, sysadmin-devops-qa, data-science-analytics, machine-learning-ai, cybersecurity) + solo countries incluye Chile o Remote
WorkingNomadsAPI pública /api/exposed_jobs/ (sin auth)Cada 6h (junto a GetOnBoard)location=latin-america,chile; todas remotas (tags: remote)

Fuentes evaluadas y descartadas por ahora (ver plan.md/resumen.md para detalle y razones): RemoteOK (global/US-centric, exige backlink por ToS), We Work Remotely y Remotive (sin foco LatAm), Laborum/Computrabajo/BuscoJobs Chile/beBee/JobLeads (sin API ni RSS públicos — beBee y JobLeads bloquean /api/ por robots.txt), FinderHR (es un headhunter manual, no un job board), Himalayas (API pública real pero volumen masivo y global, requiere filtro geográfico más fino antes de sumarla). Otras evaluadas en julio 2026: it-hunter.cl (bloqueado por Cloudflare, challenge JS), AcidLabs y OPTION/careers-page.com (portales de una sola empresa, sin API pública descubierta — AcidLabs es HTML plano de Odoo, OPTION es un SPA Vue/Manatal), INACAP/emplea.inacap.cl (SPA de Reqlut con token de sesión en la URL, sin resultados sin JS). Google no es una fuente: no tiene API pública de empleos, solo agrega schema.org/JobPosting de otros sitios — ver roadmap para la idea de agregar ese schema a nuestras propias pegas.

Flujo de datos

  1. Trigger → Gmail Trigger (LinkedIn) o Schedule Trigger (GetOnBoard + WorkingNomads), los tres cada 6h
  2. Parser/Fetch → Extrae o normaliza título, empleador, link, descripción, categoría, sueldo, tags
  3. Deduplicación → Verifica contra PostgreSQL (UNIQUE en url, ON CONFLICT DO NOTHING)
  4. INSERT → Guarda nueva pega en la BD (nodo único compartido por las tres fuentes)
  5. Redeploy en tiempo real → Si hubo pegas nuevas en esa corrida, n8n dispara restart en Coolify de inmediato, regenerando data.json
  6. Digest de Slack (2x/día) → A las 9:00 y 18:00, un trigger aparte junta en la BD todas las pegas nuevas desde el último aviso (de cualquier fuente, tracking vía static data del workflow) y manda un solo mensaje a #trabajos con hasta 5 listadas
  7. Frontendindex.html carga data/data.json y renderiza con filtros

Estructura del repositorio

├── index.html # Frontend estático
├── css/style.css # Estilos
├── js/app.js # Lógica: fetch, filtros, render
├── scripts/
│ ├── generate-json.js # Lee PostgreSQL → data.json
│ └── init-db.js # CREATE TABLE IF NOT EXISTS
├── schema.sql # Esquema de la BD
├── n8n/
│ ├── workflow.json # Workflow de n8n (exportado)
│ ├── parser-code.js # Parser LinkedIn standalone (para tests)
│ └── test-getonbrd.js # Valida en vivo el filtro Chile/Remoto de GetOnBoard
├── Dockerfile # Multi-stage: build + nginx:alpine
├── nginx.conf # Config nginx
└── README.md

Base de datos

Tabla pegas:

ColumnaTipoDescripción
idSERIAL PK
urlTEXT UNIQUELink original a la oferta — clave de deduplicación
tituloTEXTTítulo de la oferta
empleadorTEXTEmpresa
descripcionTEXTDescripción extraída
categoriaTEXTCategoría (para filtros)
ubicacionTEXTUbicación geográfica
sueldoTEXTSueldo/rango salarial detectado, si existe
tagsTEXTTags separados por coma (ej. remote)
fecha_publicacionTIMESTAMPFecha de la oferta (real si la fuente la entrega, si no la de ingesta)
fuenteTEXTOrigen: linkedin, getonbrd, etc.
email_origenTEXTCasilla de email de donde se parseó (null si no aplica)
activoBOOLEANSoft delete (default TRUE)
fecha_creacionTIMESTAMPFecha de ingreso al sistema

Desarrollo

# Instalar dependencias
npm install
# Generar data.json (requiere DATABASE_URL)
DATABASE_URL=postgres://... node scripts/generate-json.js
# Inicializar BD (crea tabla)
DATABASE_URL=postgres://... node scripts/init-db.js

Deploy

Hosteado en Coolify como aplicación GitHub (devschile/pegas).

Build: Dockerfile multi-stage — la etapa de build ejecuta init-db.js + generate-json.js y copia data.json a la imagen nginx final.

Variables de entorno requeridas en Coolify:

  • DATABASE_URL: connection string de PostgreSQL

Redeploy trigger: n8n llama a la API de Coolify para redeployar cuando hay pegas nuevas.

Licencia

MIT

Roadmap

  • Parser de LinkedIn Jobs (emails)
  • PostgreSQL + deduplicación por URL
  • Frontend con buscador y filtros
  • Detección de sueldo/rango salarial
  • GetOnBoard — API pública v0, sin auth, filtrada a Chile/Remoto (nodos getonbrd-* en n8n/workflow.json, validado con n8n/test-getonbrd.js)
  • WorkingNomads — API pública /api/exposed_jobs/, sin auth, filtrada a LatAm/Chile
  • Digest de Slack 2x/día (9:00 y 18:00) en vez de notificar en cada corrida — evita saturar el canal
  • Fuentes adicionales — evaluadas y descartadas por ahora: ver tabla "Fuentes de pegas" más arriba. Candidata más viable a futuro: Himalayas (API pública real, pero requiere filtro geográfico más fino por su volumen global)
  • Auto-expiración de pegas antiguas
  • Dashboard de métricas

About

Vitrina de pegas moderado para la comunidad devsChile

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages