Skip to content

Repository files navigation

JumpIn Job Board

JumpIn is a full-stack job aggregation platform focused on student and early-career jobs in Hungary. It collects listings from multiple sources, normalizes them into a unified PostgreSQL schema, enriches selected fields, and exposes the results through a searchable API and React frontend.

The project is built as a modular monorepo:

  • Frontend: React, TypeScript, Vite, TanStack Router, TanStack Query, Tailwind CSS.
  • Backend: Go API and worker processes with OpenAPI-first HTTP handlers.
  • Scrapers: Python source adapters using httpx, BeautifulSoup, and optional Playwright.
  • Infrastructure: PostgreSQL, Kafka in KRaft mode, Temporal, Docker Compose, Nginx for the frontend container.

Features

  • Aggregates listings from multiple job sources.
  • Stores raw scraper payloads for provenance and replay.
  • Normalizes heterogeneous source data into canonical job records.
  • Supports search, filters, cursor pagination, tags, sources, and job details.
  • Runs ingestion and enrichment through Kafka-backed worker flows.
  • Uses Temporal for durable scraper orchestration.
  • Generates AI summaries for job detail pages when enrichment is configured.
  • Provides a responsive React UI with list, filter, and detail views.
  • Includes backend, frontend, scraper, integration, and E2E test coverage.

Repository Layout

.
|-- backend/                 # Go API, worker, migrations, OpenAPI contract
|-- frontend/                # React + Vite web application
|-- scrapers/                # Python scraper framework and source adapters
|-- docs/                    # Product, architecture, data model, deployment docs
|-- scripts/                 # Monorepo Docker/deployment helpers
|-- docker-compose.yml       # Shared infra services
|-- docker-compose.backend.yml
|-- docker-compose.frontend.yml
`-- Makefile                 # Root local runtime commands

Quick Start

The canonical local runtime is Docker Compose from the repository root.

Prerequisites

  • Docker with the Compose plugin
  • Make

Optional for component-level development:

  • Go 1.25 for backend work
  • Node.js 22 LTS and npm for frontend work
  • uv for scraper work
  • Playwright browsers for scraper/frontend E2E flows that need browser automation

Run the Full Stack

make up

This starts infrastructure, backend services, and the frontend when the frontend compose file is present.

Default local URLs:

Useful runtime commands:

make status
make logs
make update-no-db
make down

Use make reset only when you intentionally want to stop the stack and remove local infra volumes.

Configuration

Backend and compose configuration are loaded from:

backend/.env

If it is missing, the monorepo Docker script creates it from:

backend/.env.example

Important settings:

  • DATABASE_URL
  • KAFKA_BOOTSTRAP_SERVERS
  • TEMPORAL_ADDRESS
  • ORCHESTRATION_ENABLED
  • ORCHESTRATION_SCRAPERS_WORKDIR
  • OPENAI_API_KEY
  • OPENAI_BASE_URL
  • OPENAI_SUMMARY_MODEL

AI summary enrichment requires a compatible API key and model configuration. The rest of the stack can still run locally without real enrichment credentials.

API

The API contract lives in:

backend/api/openapi-v1.yaml

Main endpoints:

  • GET /healthz
  • GET /readyz
  • GET /v1/jobs
  • GET /v1/jobs/{jobId}
  • GET /v1/jobs/metrics
  • GET /v1/tags
  • GET /v1/sources

The frontend uses /api as its browser-facing API prefix in the containerized Nginx runtime, which proxies requests to the backend API service.

Development

Backend

cd backend
go test ./...
go vet ./...
make run-api
make run-worker

Database migrations:

cd backend
make migrate-up
make migrate-version

Frontend

cd frontend
npm install
npm run dev

Frontend quality checks:

npm run lint
npm run test
npm run test:e2e
npm run build

Mock API mode:

cd frontend
npm run dev:mock

Scrapers

cd scrapers
uv sync
uv run playwright install chromium
uv run python run_all.py

Scraper checks:

uv run ruff check .
uv run pytest tests

Run one source:

uv run python run_source.py --source minddiak

Data and Ingestion Flow

At a high level:

  1. Python scrapers fetch source listings and emit raw envelope records.
  2. Worker processes publish and consume raw events through Kafka.
  3. Ingestion validates, normalizes, and persists canonical job data in PostgreSQL.
  4. Enrichment requests are published for derived fields such as AI summaries.
  5. The API serves read models to the React frontend.

Raw payloads are retained so normalization bugs can be fixed and replayed without re-scraping the original sources.

Testing

Common checks by component:

# Backend
cd backend && go test ./...

# Frontend
cd frontend && npm run lint && npm run test

# Scrapers
cd scrapers && uv run ruff check . && uv run pytest tests

Some backend E2E tests require Docker/Testcontainers and libpostal support:

cd backend
make test-e2e
make test-readapi-e2e

Deployment

The VPS deployment workflow is in:

.github/workflows/deploy-vps.yml
scripts/deploy-vps.sh

Required GitHub secrets:

  • VPS_HOST
  • VPS_PORT optional, defaults to 22
  • VPS_USER
  • VPS_SSH_KEY
  • VPS_APP_DIR

The deploy script checks out the exact commit on the VPS, runs make update-no-db when the database already exists, and falls back to make up for first bootstrap. Database volumes are kept intact.

Documentation

Start here:

  • docs/project-overview.md - product scope and goals
  • docs/architecture.md - system and module architecture
  • docs/data-model.md - data quality and schema contract
  • docs/database-schema.md - implemented PostgreSQL schema
  • docs/backend.md - backend engineering reference
  • docs/frontend.md - frontend engineering reference
  • docs/kafka-ingestion-pipeline.md - ingestion event flow
  • docs/ingestion-enrichment-workflow.md - worker and enrichment flow
  • docs/vps-hardening.md - VPS hardening notes
  • docs/git-governance.md - branch and release policy

Project Principles

  • Preserve source provenance for canonical listing updates.
  • Prefer strict normalization over inference.
  • Keep backend module boundaries explicit.
  • Keep ingestion inside the modular monolith for now.
  • Store raw values and confidence/nulls when data is uncertain.
  • Avoid weakening validation just to make tests pass.

About

A job board for students and emerging professionals

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages