Búsqueda de empleo automatizada y self-hosted: scraping + scoring con IA multi-proveedor, todo gobernado desde una única pantalla de configuración.
Job-radar es un sistema self-hosted de búsqueda de empleo que automatiza lo tedioso y deja al candidato solo lo que importa: decidir a qué apuntar. Cada pocas horas rastrea portales (Indeed, LinkedIn) y feeds RSS, puntúa cada oferta con IA (0–100 según una rúbrica de encaje), genera CV y carta a medida por oferta y lo organiza todo en un CRM tipo Kanban — corriendo 24/7 en infraestructura propia (NAS + Docker), sin depender de servicios de pago.
Lo que lo hace distinto: todo se controla desde una única pantalla de Configuración. Zona, modalidades, portales, términos de búsqueda, umbrales, perfil y claves de IA viven en la base de datos; la web los escribe y el scraper, n8n y el worker los leen de ahí. Cambias un campo en la web y todo el sistema obedece — sin tocar código ni redesplegar. Eso lo hace adaptable a cualquier persona o sector (de "desarrollador" a "enfermero") con solo rellenar un formulario.
Un diseño, dos motores. El scoring y la generación pueden correr en n8n (7 workflows, robusto, 24/7) o en un worker Node autónomo (stack mínimo de 3 piezas: Postgres + web + worker). Ambos leen la misma configuración de la BD.
Ficha completa en el portfolio.
git clone https://github.com/IvanjonasFC/Job-radar.git && cd Job-radar
cp .env.example .env # rellena las variables (contraseña de BD, etc.)
docker compose up -d --build # Postgres + web + workerAbre http://127.0.0.1:3010, entra en Config y define tu zona, portales, tu perfil y una clave de IA (Groq y Gemini tienen plan gratuito). A partir de ahí, todo el sistema obedece a esa configuración. Para lanzar una recogida de ofertas: docker compose --profile scraper run --rm scraper.
- Radar de ofertas — scraping (Python + JobSpy: Indeed/LinkedIn) + feeds RSS, con prefiltro por título configurable.
- Scoring con IA — rúbrica 0–100 (skills 35 · seniority 20 · encaje 15 · modalidad/ubicación 15 · proyección 15) que devuelve JSON; descarta lo no‑IT automáticamente.
- IA multi-proveedor con fallback — Groq → Gemini → xAI (Grok) → Ollama local. Si uno se satura, el siguiente responde; una oferta sin puntuar vuelve a la cola y se reintenta.
- CV y carta a medida — generación por oferta desde tu perfil real, sin inventar experiencia ni títulos.
- CRM completo — cola de hoy, buscador de ofertas, pipeline Kanban (drag & drop) y recordatorios de seguimiento.
- Analíticas en vivo — embudo de conversión, tecnologías más pedidas y tiempo de reacción.
- Todo configurable desde la web — una única fuente de verdad en Postgres; adaptable a cualquier perfil sin tocar código.
- Self-hosted y privado — corre en tu NAS/servidor; tus datos y tus claves nunca salen de tu infraestructura.
Analíticas![]() |
Pipeline (Kanban)![]() |
Ofertas![]() |
Configuración![]() |
graph TD
subgraph NAS ["Infraestructura self-hosted - NAS + Docker"]
CFG["Config - empleo.settings<br/>UNICA FUENTE DE VERDAD"]
PG["PostgreSQL 16<br/>schema empleo"]
SCR["Scraper - Python + JobSpy + RSS<br/>contenedor one-shot, programado"]
N8N["n8n - 7 workflows<br/>radar / scoring / cartas"]
WK["Worker Node<br/>motor alternativo sin n8n"]
WEB["Web CRM - Astro SSR"]
OL["Ollama - IA local"]
CADDY["Caddy - HTTPS + pasarela LLM"]
end
CLOUD["LLMs cloud<br/>Groq / Gemini / xAI"]
WEB -->|"escribe config"| CFG
CFG -->|"lee"| SCR
CFG -->|"lee"| N8N
CFG -->|"lee"| WK
SCR -->|"INSERT ofertas nuevas"| PG
N8N -->|"scoring + cartas"| PG
WK -->|"scoring + cartas"| PG
PG <-->|"lee / escribe"| WEB
N8N -->|"fallback"| CLOUD
N8N -->|"fallback local"| OL
CADDY --> WEB
El corazón del diseño es la tabla empleo.settings (una fila JSONB). La web la edita; el scraper, n8n y el worker la leen. No hay configuración duplicada ni valores a fuego repartidos por el código:
| Ajuste | Lo escribe | Lo consume | Cuándo aplica |
|---|---|---|---|
| Zona, modalidades, umbral, marca | Web | Web + n8n (estado de oferta) | Al instante |
| Portales, términos, países, antigüedad, RSS | Web | Scraper | En el siguiente scrapeo programado |
| Perfil profesional | Web | Scoring + cartas (n8n / worker) | En la siguiente ejecución |
| Claves de IA (Groq / Gemini / xAI / Ollama) | Web | Scoring + cartas (n8n / worker), con fallback a .env |
En la siguiente ejecución |
Consecuencia práctica: para adaptar el sistema a otra persona no se toca código — se rellena el formulario de Config (perfil, zona, sector, claves) y funciona. Los valores por defecto del repositorio son genéricos y no contienen datos personales.
El scoring y la generación hablan con cualquier proveedor compatible con la API de OpenAI (/chat/completions) y prueban una cadena ordenada hasta obtener una respuesta válida:
Groq (rápido) → Gemini → xAI (Grok) → Ollama (local, ilimitado)
Cada clave se toma primero de Config (BD) y, si está vacía, del entorno (.env). Si un proveedor se satura o cae, entra el siguiente; y si una oferta no logra puntuarse, vuelve a estado nueva y se reintenta en la siguiente pasada — el sistema se auto‑cura y no pierde ofertas.
Ver detalle del stack
| Componente | Detalle |
|---|---|
| Astro 5 (SSR) | output: server, adaptador @astrojs/node (standalone). Datos en vivo desde Postgres. |
| TypeScript | Tipado en la capa de datos, config y endpoints. |
| React (islas) + Recharts + dnd-kit | Kanban con drag & drop y gráficos de las analíticas. |
| postgres-js / Drizzle | Acceso a Postgres con rol de mínimos privilegios (empleo_web). |
| Zod | Validación de entrada. |
| Componente | Detalle |
|---|---|
| n8n | 7 workflows: radar (scrape RSS + scoring), digest, acciones (carta/CV, aplicar, descartar), healthcheck, recordatorios, IA on-demand, guardar carta. |
| Worker Node | Motor alternativo (solo dependencia pg) que replica el bucle: scoring, generación y acciones. Reduce el sistema a 3 piezas. |
| Multi-LLM | Groq, Google Gemini, xAI (Grok), Ollama local — en cadena, con reintento. |
| Componente | Detalle |
|---|---|
| Python + JobSpy | Indeed y LinkedIn; contenedor de un solo uso lanzado por el programador del NAS. |
| feedparser (RSS) | Feeds de remoto global (WeWorkRemotely, Tecnoempleo…). |
| Docker + Compose | Stack de 3 piezas (db + web + worker) con perfiles opcionales scraper y backup. |
| Caddy | HTTPS automático y pasarela hacia los LLMs. |
- Self-hosted: todo corre en tu NAS/servidor; las ofertas, tu perfil y tus claves nunca salen de tu red.
- Secretos fuera del repo:
.envy credenciales están en.gitignore; solo se versiona.env.examplecon placeholders. Los workflows de n8n del repo van neutralizados (sin tokens, dominios ni datos personales). - Rol de BD de mínimos privilegios para la web (
empleo_web): lee y solo actualiza lo imprescindible. - Login en la web (sesión con cookie httpOnly) y proxy server-side hacia n8n (la URL interna nunca llega al navegador).
Estructura del proyecto
Job-radar/
├── src/ # Web CRM (Astro SSR + TypeScript)
│ ├── pages/ # hoy, ofertas, pipeline, analiticas, configuracion, login + api/
│ ├── lib/ # config.ts (settings), db.ts, queries.ts, auth.ts
│ └── components/ # KanbanBoard.tsx
├── db/
│ └── schema.sql # esquema empleo (job_offers, applications, generated, settings, events)
├── automation/
│ ├── n8n/ # 7 workflows exportados (neutralizados) + profile.example.md
│ ├── worker/ # motor Node autónomo (alternativa a n8n)
│ └── scraper/ # scraper Python (JobSpy + RSS), lee Config de la BD
├── assets/ # capturas del CRM
├── docker-compose.yml # db + web + worker (+ perfiles scraper / backup)
├── Dockerfile # imagen de la web
└── .env.example # plantilla de variables (sin secretos)
Cómo ejecutar el proyecto
Requisitos: Docker + Docker Compose (y, para desarrollo, Node 20+). Una clave de IA (Groq/Gemini/xAI) o un Ollama local.
1) Configura el entorno:
cp .env.example .env # rellena DATABASE_URL, N8N_WEBHOOK_BASE y (si usas compose) DB_PASSWORD2) Levanta el stack (Postgres + web + worker):
docker compose up -d --build
# Web en http://127.0.0.1:3010 · el worker puntúa en bucle
docker compose --profile scraper run --rm scraper # una pasada de scraping (opcional)3) Abre la web → pestaña Config y rellena zona, portales, tu perfil y una clave de IA. A partir de ahí, el scraper y el motor obedecen a esa configuración.
Desarrollo local (sin Docker):
npm install
npm run dev # http://localhost:4321Vía n8n (opcional, en vez del worker): importa automation/n8n/*.json en tu instancia, apunta N8N_WEBHOOK_BASE a sus webhooks y pon tus claves en Config (o en el .env de n8n como respaldo).
- Normalizar los verdicts de la IA a un conjunto fijo (ES/EN unificados).
- Panel de estado de proveedores de IA (latencia y errores por proveedor).
- Más portales vía proxy (InfoJobs/Google Jobs) y más países.
- Exportar CV/carta a PDF/DOCX desde la propia web.
- Modo multiusuario (varios perfiles sobre la misma instancia).
Iván Jonás Fernández Correa — Técnico Superior en DAM + ASIR. Perfil híbrido desarrollo + sistemas (full-stack, self-hosted, IA aplicada).
Distribuido bajo licencia MIT.



