GitHub - cvm101/findJobWithMe · GitHub
Skip to content

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Java Job Finder

A self-hosted, ToS-safe job-search assistant built with Spring Boot. It aggregates jobs from free APIs, scores them against your resume with AI, drafts application material, and can generate and send tailored recruiter outreach emails from a Notion database — without auto-submitting anything.

This is a Java/Spring Boot port of the original Next.js "Free Job Finder," and it reuses the same Supabase Postgres database, so both apps can share data.

Features

  • Daily job aggregation from Adzuna, JSearch, Remotive, and Arbeitnow.
  • AI fit scoring & enrichment (match reasons, per-job suggestions) with automatic multi-provider fallback: Groq → Gemini → OpenRouter.
  • Resume upload & parsing (PDF / DOCX / TXT) with AI skill extraction.
  • Application tracker board with statuses.
  • InMail: AI-drafted recruiter outreach emails from a pasted job description.
  • Notion Outreach (two-phase): generate drafts from unprocessed Notion JD rows, review them, then send per-row with your resume attached and auto-tick the Notion checkbox.
  • Suggestions report: consolidated resume/skill/positioning advice across your matches.
  • Excel export of jobs.
  • Single-user password gate with signed HMAC cookies.
  • Scheduled daily runs via @Scheduled (plus a secret-protected manual trigger).

Tech stack

  • Java 21, Spring Boot 3.3.x
  • Spring Web + Thymeleaf (server-rendered UI) + vanilla JS/CSS
  • Spring Data JPA + PostgreSQL (points at the existing Supabase DB)
  • Spring Mail (Gmail SMTP) for outreach sending
  • Apache POI (Excel + DOCX), Apache PDFBox (PDF)

High-level architecture

flowchart TB
  subgraph client [Browser]
    UI["Thymeleaf pages + app.js"]
  end

  subgraph web [Spring Boot web layer]
    Filter["AuthFilter (HMAC cookie gate)"]
    controllers["Controllers (/api/*): Auth, Profile, Resume, Jobs, Tracker, InMail, Notion, Suggestions, Settings, Export, RunDaily"]
  end

  subgraph services [Service layer]
    Pipeline[PipelineService]
    Sources[SourceService]
    Ai[AiService]
    NotionMail[NotionMailService]
    Mail[MailService]
    InMailSvc[InMailService]
    Others["JobService, TrackerService, ProfileService, SettingsService, RunService, ExcelService, ResumeParser"]
    Llm[LlmClient]
    NotionCli[NotionClient]
  end

  repos["Spring Data JPA repositories"]
  DB[("Supabase PostgreSQL")]

  subgraph external [External APIs]
    LLM["Groq / Gemini / OpenRouter"]
    Boards["Adzuna / JSearch / Remotive / Arbeitnow"]
    NotionAPI["Notion API"]
    SMTP["Gmail SMTP"]
  end

  Sched["DailyScheduler (cron)"]

  UI -->|"HTTP + fetch()"| Filter --> controllers --> services --> repos --> DB
  Ai --> Llm --> LLM
  Sources --> Boards
  NotionMail --> NotionCli --> NotionAPI
  NotionMail --> Mail --> SMTP
  Sched --> Pipeline
Loading

Notion outreach flow (draft now, send later)

flowchart TB
  gen["POST /api/notion/generate"] --> nms[NotionMailService.generateDrafts]
  nms --> nc["NotionClient: listPending + fetchPageText"] --> notion[Notion API]
  nms --> ai["AiService.generateOutreachEmail"] --> llm[LlmClient]
  nms --> save["InMailService.createFromNotion (persist)"] --> db[("inmail table")]

  send["POST /api/notion/send {id}"] --> sd[NotionMailService.sendDraft]
  sd --> mail["MailService.send (resume attached)"] --> smtp[Gmail SMTP]
  sd --> mark["NotionClient.markDone"] --> notion
  sd --> sent["InMailService.markSent"] --> db
Loading

Project structure

JavaJobFinder/
  pom.xml
  db/schema.sql                 # full Postgres schema (run once in Supabase)
  src/main/java/com/jobfinder/
    JavaJobFinderApplication.java
    auth/         AuthService, AuthFilter, AuthController
    config/       JobFinderProperties, WebConfig, HttpConfig
    controller/   Home, Auth, Profile, Resume, Jobs, Tracker, InMail,
                  Notion, Suggestions, Settings, Export, RunDaily, ApiExceptionHandler
    domain/       Profile, Settings, SourceToggles, Job, Run, Tracker, InMail, RawJob
    repository/   Spring Data JPA repositories
    scheduler/    DailyScheduler
    service/      PipelineService, AiService, JobService, TrackerService,
                  ProfileService, SettingsService, RunService, ExcelService,
                  ResumeParser, MailService, NotionMailService, InMailService,
                  FetchDescriptionService,
                  llm/LlmClient, notion/NotionClient, sources/*
  src/main/resources/
    application.yml
    application-local.yml.example
    templates/    index.html, login.html
    static/       css/app.css, js/app.js, js/login.js

Prerequisites

  • JDK 21 (Eclipse Temurin recommended):
    winget install EclipseAdoptium.Temurin.21.JDK
  • Maven:
    winget install Apache.Maven
  • Your Supabase database password (Supabase → Project Settings → Database). This is separate from the API keys — JPA connects directly to Postgres.

After installing, open a new terminal and verify:

java -version
mvn -version

Database setup

Run db/schema.sql once in the Supabase SQL editor. All statements are idempotent, so re-running is safe. It includes the resume-attachment and Notion-link columns used by the outreach feature:

alter table public.profile add column if not exists resume_file bytea;
alter table public.profile add column if not exists resume_content_type text;
alter table public.inmail  add column if not exists notion_page_id text;

Configure

Copy the example local config and fill in real values:

Copy-Item src\main\resources\application-local.yml.example src\main\resources\application-local.yml

application-local.yml is git-ignored. Every value can also be supplied as an environment variable (see application.yml for the variable names):

Purpose Env var(s)
Database SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD
Auth gate APP_PASSWORD, AUTH_SECRET, CRON_SECRET
LLM LLM_PROVIDER_ORDER, GROQ_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY (+ matching *_MODEL)
Job sources ADZUNA_APP_ID, ADZUNA_APP_KEY, RAPIDAPI_KEY
Notion NOTION_TOKEN, NOTION_DATABASE_ID, NOTION_STATUS_PROPERTY, NOTION_PROCESS_CHECKED
Email (Gmail) MAIL_USERNAME, MAIL_APP_PASSWORD, MAIL_FROM_NAME

Notes:

  • If the direct db.<ref>.supabase.co host can't be reached (some networks are IPv6-only there), switch to the Session pooler host + username shown in the Supabase dashboard.
  • Gmail requires a 16-character App Password (2-Step Verification must be enabled) — not your normal login password.
  • The Notion integration must be shared with your database (open the database → ••• → Connections → add your integration). NOTION_DATABASE_ID is the 32-character id before ?v= in the database URL.

Run

mvn spring-boot:run "-Dspring-boot.run.profiles=local"

Open http://localhost:8080. If app-password is set you'll hit the login page; otherwise the dashboard loads directly.

Using the Notion Outreach tab

  1. Ensure your Notion database has a title column (job title), a Status checkbox column, and the JD text (including the recruiter email) in each row's page body.
  2. Leave rows you want to process unchecked.
  3. Click Generate drafts — drafts are created and saved (nothing is sent). Safe to re-run: already-drafted rows are skipped, and rate-limited rows can be retried later.
  4. Review/edit each draft, then click Send — the email is sent with your resume attached, the Notion checkbox is ticked, and the draft is marked sent.

Security notes

  • Single-user gate: one shared APP_PASSWORD, validated into an HMAC (AUTH_SECRET) signed cookie. No user accounts or signup by design.
  • application-local.yml holds secrets and is git-ignored — keep tokens, the DB password, and the Gmail app password out of commits and screenshots.
  • /api/run-daily is protected by CRON_SECRET for external schedulers.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages