An AI-assisted recruiting platform: recruiters post jobs and manage a hiring pipeline, candidates apply with a resume, and an ML service scores each application against the job description for automatic ranking and shortlisting.
The system is three independently-run services:
| Service | Stack | Port | Role |
|---|---|---|---|
backend |
Java 17, Spring Boot, MySQL, JWT | 8080 |
REST API, auth, business logic |
frontend |
React, TypeScript, Vite, Tailwind | 5173 |
Recruiter & candidate web app |
ml-engine |
Python, Flask, spaCy, scikit-learn | 5000 |
Resume/job-description scoring |
Candidates
- Register/login, browse and search job postings, apply with a resume upload
- Track applications and interview status, with a live match score
- Join scheduled interviews via a real video link once the meeting time arrives
- Resume vs. job-description ATS score checker, profile strength meter
- In-app notifications and activity log
Recruiters
- Post, edit, publish/draft, close/reopen, and delete job listings
- Per-job AI screening toggle (auto-score applicants) and auto-shortlist threshold
- Applicant pipeline: search, filter, change status, manually shortlist, view submitted skills/experience/cover letter
- AI candidate ranking against a pasted job description
- Schedule interviews — generates a shareable video meeting link, emails and notifies the candidate, and the Join button only activates near the scheduled time
- Dashboard stats and a Reports page (applicants by job, pipeline status breakdown)
- Data is scoped per recruiter — one recruiter cannot see or modify another's jobs or applicants
Platform
- JWT-based auth with bcrypt-hashed passwords (legacy plaintext accounts are silently upgraded on next login)
- Password reset via emailed link
- ML-driven ATS scoring: spaCy keyword extraction + TF-IDF/cosine similarity between resume and job text
SmartRecruitmentSystem/
├── backend/ Spring Boot REST API
│ └── src/main/java/com/smartrecruit/backend/
│ ├── controller/ REST endpoints
│ ├── service/ business logic
│ ├── model/ JPA entities
│ ├── repository/ Spring Data repositories
│ └── dto/ request/response shapes
├── frontend/ React + Vite SPA
│ └── src/
│ ├── pages/ route-level views (dashboard, jobs, candidates, ...)
│ ├── components/ shared UI (navbar, sidebar, job actions menu, ...)
│ └── context/ auth context
└── ml-engine/ Flask scoring service
├── app.py HTTP entrypoint (`POST /score`)
├── resume_parser.py spaCy keyword extraction (resume)
├── jd_parser.py spaCy keyword extraction (job description)
└── ats_engine.py TF-IDF + cosine similarity scoring
- Java 17+ and Maven (or use the bundled
./mvnw) - Node.js 18+ and npm
- Python 3.10+
- MySQL 8.x, running locally
Create the database the backend expects:
CREATE DATABASE smart_recruit;Update the credentials in backend/src/main/resources/application.properties (spring.datasource.username / spring.datasource.password) to match your local MySQL setup. The backend auto-creates/updates tables on startup (spring.jpa.hibernate.ddl-auto=update).
For password-reset and application-notification emails to actually send, also set spring.mail.username / spring.mail.password to a real mailbox (a Gmail app password works well). Without valid credentials, email sends fail silently and the app keeps working otherwise.
cd backend
./mvnw spring-boot:runRuns on http://localhost:8080.
cd ml-engine
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS/Linux
pip install flask flask-cors spacy scikit-learn
python -m spacy download en_core_web_sm
python app.pyRuns on http://localhost:5000.
cd frontend
npm install
npm run devRuns on http://localhost:5173.
All three services must be running for the app to work end-to-end — the frontend talks to the backend, and the backend talks to the ML engine for AI screening/ranking. If the ML engine is down, screening/ranking scores come back as null/0 instead of failing the request.
- Meeting links for scheduled interviews point to Jitsi Meet rooms generated per-application — no video infrastructure or API keys required, but also no recording/host controls.
- Authorization is scoped by a
recruiterIdthe frontend passes with each request (matching what the logged-in user's token resolved to), not by re-verifying the JWT on every recruiter-scoped call. It stops accidental cross-recruiter access from the UI, but isn't a hardened security boundary against a client that lies about its own id. - All URLs are currently hardcoded to
localhostin the frontend — there's no environment-based API base URL yet, so deploying anywhere other than local dev needs that to be introduced first.