{"id":50170245,"url":"https://github.com/samuelhm/42jobs","last_synced_at":"2026-05-30T03:02:01.095Z","repository":{"id":359080844,"uuid":"1243567693","full_name":"samuelhm/42Jobs","owner":"samuelhm","description":"AI-powered job search platform for junior software engineers: job fetching, smart filtering, keyword extraction, and ATS-optimized CV generation. Built with .NET 10, ASP.NET Core, React 19, and PostgreSQL. Production-deployed with SSL.","archived":false,"fork":false,"pushed_at":"2026-05-28T00:27:57.000Z","size":6072,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-28T01:24:24.984Z","etag":null,"topics":["ai","aspnet-core","ci-cd","csharp","cv-generator","docker","dotnet","job-search","lets-encrypt","machine-learning","postgresql","react","ssl"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samuelhm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-19T13:10:55.000Z","updated_at":"2026-05-28T00:27:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/samuelhm/42Jobs","commit_stats":null,"previous_names":["samuelhm/bimjobsnet","samuelhm/42jobs"],"tags_count":64,"template":false,"template_full_name":null,"purl":"pkg:github/samuelhm/42Jobs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelhm%2F42Jobs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelhm%2F42Jobs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelhm%2F42Jobs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelhm%2F42Jobs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samuelhm","download_url":"https://codeload.github.com/samuelhm/42Jobs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelhm%2F42Jobs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33678271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","aspnet-core","ci-cd","csharp","cv-generator","docker","dotnet","job-search","lets-encrypt","machine-learning","postgresql","react","ssl"],"created_at":"2026-05-24T23:03:49.648Z","updated_at":"2026-05-30T03:02:01.090Z","avatar_url":"https://github.com/samuelhm.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 42jobs — junior job search, done right\n\n[![License](https://img.shields.io/badge/license-AGPL_v3-blue.svg?style=for-the-badge)](LICENSE)\n[![Stars](https://img.shields.io/github/stars/samuelhm/42jobs?style=for-the-badge)](https://github.com/samuelhm/42jobs/stargazers)\n[![Issues](https://img.shields.io/github/issues/samuelhm/42jobs?style=for-the-badge)](https://github.com/samuelhm/42jobs/issues)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge)](CONTRIBUTING.md)\n\n42jobs is a job search platform tailored for **junior software engineers**. It fetches job offers from LinkedIn, filters them with AI for relevance and junior-friendliness, extracts keywords, and generates ATS-optimized CVs — so you spend less time searching and more time landing interviews.\n\n**Live at [42jobs.xyz](https://42jobs.xyz)**\n\n## Table of Contents\n\n- [Features](#features)\n- [Screenshots](#screenshots)\n- [Tech Stack](#tech-stack)\n- [Prerequisites](#prerequisites)\n- [Quick Start](#quick-start)\n- [Architecture](#architecture)\n- [Environment Variables](#environment-variables)\n- [Roadmap](#roadmap)\n- [Contributing](#contributing)\n- [License](#license)\n- [Contact](#contact)\n\n## Features\n\n- **Job fetching** — pulls offers from LinkedIn via RapidAPI by category\n- **AI filtering** — keeps only relevant offers suitable for junior profiles\n- **Keyword extraction** — identifies technologies, skills, and soft skills per offer\n- **CV generation** — generates ATS-optimized CVs via LLM, customized per job offer\n- **GitHub import** — analyzes your repositories and creates project entries automatically\n- **Profile management** — education, experience, certifications, languages, skills\n- **Job tracking** — status pipeline: saved → CV sent → interview → hired / rejected\n- **Pluggable AI** — swap between Gemini and OpenAI, or add your own provider\n- **Pluggable job sources** — LinkedIn today, more sources planned\n\n## Screenshots\n\n\u003c!-- TODO: add screenshots of Dashboard, Offers, Profile, CV generation --\u003e\n| Dashboard | Job Search | CV Generation |\n|-----------|------------|---------------|\n| _coming soon_ | _coming soon_ | _coming soon_ |\n\n## Tech Stack\n\n| Layer       | Technology                                      |\n|-------------|-------------------------------------------------|\n| Backend     | .NET 10 (ASP.NET Core Web API), EF Core, JWT    |\n| Database    | PostgreSQL 16                                   |\n| Frontend    | React 19 + React Router 7 + TypeScript (Vite)   |\n| AI          | OpenAI / Google Gemini (pluggable providers)    |\n| Auth        | JWT in HttpOnly cookies + BCrypt                |\n| Infra       | Docker + Docker Compose (dev \u0026 prod profiles)   |\n| Package mgr | pnpm                                            |\n\n## Prerequisites\n\nBefore you start, make sure you have:\n\n- [Docker](https://docs.docker.com/get-docker/) \u0026 Docker Compose\n- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) _(optional, only for local dev without Docker)_\n- [Node.js 20+](https://nodejs.org/) + [pnpm](https://pnpm.io/installation) _(optional, only for local dev without Docker)_\n- A [RapidAPI](https://rapidapi.com/) account with LinkedIn Jobs API subscription\n- An API key for [Google Gemini](https://aistudio.google.com/) or [OpenAI](https://platform.openai.com/)\n\n## Quick Start\n\n```bash\n# 1. Clone\ngit clone https://github.com/samuelhm/42jobs.git\ncd 42jobs\n\n# 2. Configure environment\ncp .env.example .env\n# Edit .env with your database credentials and a random JWT secret\n\n# 3. Start\nmake dev-up\n```\n\nThe app will be available at:\n\n- **Frontend**: http://localhost:3000\n- **API**: http://localhost:8080\n\n### First-time setup\n\n1. Open the frontend, register an account\n2. Go to **Admin** panel (you'll need to promote your user to Admin in the DB first — see [CONTRIBUTING.md](CONTRIBUTING.md))\n3. Configure your AI provider (Gemini or OpenAI) with your API key\n4. Configure a job provider (LinkedIn RapidAPI) with your API key\n5. Create a search category (e.g. \"React Developer\") and hit **Fetch Jobs**\n\n### Useful commands\n\n```bash\nmake dev-up          # start development services\nmake dev-down        # stop development services\nmake dev-restart     # rebuild + restart\nmake dev-logs        # follow all logs\nmake prod-up         # start production services\nmake clean           # stop everything + delete volumes\nmake release         # create new version tag + trigger deploy\n```\n\n## Architecture\n\n```\n42jobs/\n├── backend/src/\n│   ├── Controllers/     # 13 REST controllers (partial classes, one endpoint per file)\n│   ├── Models/          # 22 C# entity models + DTOs\n│   ├── Data/            # EF Core DbContext (Fluent API config)\n│   ├── Services/\n│   │   ├── Ai/          # AI abstraction layer\n│   │   │   ├── AiService.cs               # reads prompts from DB, resolves providers\n│   │   │   └── Providers/{Gemini,OpenAI}   # low-level API clients\n│   │   ├── Jobs/        # Job fetching (background queue via Channel\u003cT\u003e)\n│   │   ├── EncryptionService.cs            # API key encryption at rest\n│   │   └── JwtService.cs\n│   └── Utils/\n├── frontend/src/        # React 19 + React Router 7 (Vite + TypeScript)\n│   ├── router.tsx        # createBrowserRouter with loaders/actions\n│   ├── types/            # Shared TypeScript interfaces\n│   ├── utils/            # api, format, match (barrel)\n│   ├── hooks/            # useDebounce, usePolling (barrel)\n│   ├── context/          # AuthContext, ToastContext (barrel)\n│   ├── styles/           # 13 CSS modules by responsibility\n│   ├── components/       # Reusable components (7 domain folders + barrel)\n│   └── pages/            # Route pages with loaders (4 domain folders + barrel)\n├── database/migrations/  # 22 SQL migration + seed files\n└── docs/                # Provider guides, encryption docs\n```\n\n### AI provider abstraction\n\nControllers never call AI providers directly. They inject `IAiService`. The actual provider (Gemini, OpenAI, or future ones) is selected at runtime from the database. Prompts and response schemas are stored in the DB, not hardcoded.\n\n→ [Add a new AI provider](docs/IAProvider.md)\n\n### Job provider abstraction\n\nJob sources are pluggable. `JobFetchService` calls all enabled `IJobProvider` implementations, one per portal. Provider config (keys, URLs) lives in the database and is encrypted at rest.\n\n→ [Add a new job provider](docs/JobProvider.md)\n→ [How API key encryption works](docs/Encryption.md)\n\n## Environment Variables\n\n| Variable           | Description                  | Required |\n|--------------------|------------------------------|----------|\n| `POSTGRES_USER`    | Database user                | Yes      |\n| `POSTGRES_PASSWORD`| Database password            | Yes      |\n| `POSTGRES_DB`      | Database name                | Yes      |\n| `DATABASE_URL`     | Full connection string (`postgres://user:pass@host:5432/db`) | Yes |\n| `JWT_SECRET_KEY`   | Random string for JWT signing (≥ 256 bits) | Yes |\n\nAI and job provider API keys are configured via the **Admin panel** (not env vars) and are **encrypted at rest** in the database.\n\n## Roadmap\n\n- [x] Job fetching from LinkedIn (RapidAPI)\n- [x] AI filtering + keyword extraction (Gemini / OpenAI)\n- [x] CV generation per job offer\n- [x] GitHub project import\n- [x] Job tracking pipeline\n- [x] API key encryption at rest\n- [ ] Email notifications for new matching jobs\n- [ ] More job providers (InfoJobs, Indeed)\n- [ ] UI tests with Playwright\n- [ ] Light mode\n- [ ] Public demo instance\n\nGot an idea? [Open an issue](https://github.com/samuelhm/42jobs/issues) or pick one from the roadmap and send a PR!\n\n## Contributing\n\nContributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on how to get started, project conventions, and how to add new providers.\n\n## License\n\nDual-licensed under [AGPL v3](LICENSE) for open source / non-commercial use. For commercial use (if you wish to keep your modifications private), contact the author for a commercial license.\n\n## Contact\n\nSamuel Hurtado — [@hurtadom.dev](https://hurtadom.dev) — samuel@hurtadom.dev\n\nProject Link: [https://github.com/samuelhm/42jobs](https://github.com/samuelhm/42jobs)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuelhm%2F42jobs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamuelhm%2F42jobs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuelhm%2F42jobs/lists"}