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.
- 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.
.
|-- 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
The canonical local runtime is Docker Compose from the repository root.
- 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
uvfor scraper work- Playwright browsers for scraper/frontend E2E flows that need browser automation
make upThis starts infrastructure, backend services, and the frontend when the frontend compose file is present.
Default local URLs:
- Frontend: http://localhost:3000
- API: http://localhost:8080
- API health: http://localhost:8080/healthz
- pgAdmin: http://localhost:5050
- Kafka UI: http://localhost:8081
- Temporal UI: http://localhost:8233
Useful runtime commands:
make status
make logs
make update-no-db
make downUse make reset only when you intentionally want to stop the stack and remove local infra volumes.
Backend and compose configuration are loaded from:
backend/.envIf it is missing, the monorepo Docker script creates it from:
backend/.env.exampleImportant settings:
DATABASE_URLKAFKA_BOOTSTRAP_SERVERSTEMPORAL_ADDRESSORCHESTRATION_ENABLEDORCHESTRATION_SCRAPERS_WORKDIROPENAI_API_KEYOPENAI_BASE_URLOPENAI_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.
The API contract lives in:
backend/api/openapi-v1.yaml
Main endpoints:
GET /healthzGET /readyzGET /v1/jobsGET /v1/jobs/{jobId}GET /v1/jobs/metricsGET /v1/tagsGET /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.
cd backend
go test ./...
go vet ./...
make run-api
make run-workerDatabase migrations:
cd backend
make migrate-up
make migrate-versioncd frontend
npm install
npm run devFrontend quality checks:
npm run lint
npm run test
npm run test:e2e
npm run buildMock API mode:
cd frontend
npm run dev:mockcd scrapers
uv sync
uv run playwright install chromium
uv run python run_all.pyScraper checks:
uv run ruff check .
uv run pytest testsRun one source:
uv run python run_source.py --source minddiakAt a high level:
- Python scrapers fetch source listings and emit raw envelope records.
- Worker processes publish and consume raw events through Kafka.
- Ingestion validates, normalizes, and persists canonical job data in PostgreSQL.
- Enrichment requests are published for derived fields such as AI summaries.
- 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.
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 testsSome backend E2E tests require Docker/Testcontainers and libpostal support:
cd backend
make test-e2e
make test-readapi-e2eThe VPS deployment workflow is in:
.github/workflows/deploy-vps.yml
scripts/deploy-vps.sh
Required GitHub secrets:
VPS_HOSTVPS_PORToptional, defaults to22VPS_USERVPS_SSH_KEYVPS_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.
Start here:
docs/project-overview.md- product scope and goalsdocs/architecture.md- system and module architecturedocs/data-model.md- data quality and schema contractdocs/database-schema.md- implemented PostgreSQL schemadocs/backend.md- backend engineering referencedocs/frontend.md- frontend engineering referencedocs/kafka-ingestion-pipeline.md- ingestion event flowdocs/ingestion-enrichment-workflow.md- worker and enrichment flowdocs/vps-hardening.md- VPS hardening notesdocs/git-governance.md- branch and release policy
- 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.