Letโs be honest: searching for a job is a full-time job in itself. ๐ฉ
Most of us rely on messy spreadsheets, generic Trello boards, or just "hoping for the best" to track applications. I wanted something better. I wanted a tool that didn't just list jobs, but actually automated the data entry, visualized my pipeline, and calculated my conversion rates.
So, I built JobTrackerPro.
I treated this as a System Design Challenge. Itโs not just a CRUD app; itโs an event-driven ecosystem featuring Generative AI, L2 Caching, and Cloud-Native Storage.
๐ The Goods
- ๐ Live Demo: https://thughari.github.io/JobTrackerPro
- ๐ Monorepo (Source Code): github.com/thughari/JobTrackerPro
- ๐บ Video Walkthrough: https://youtu.be/w6iAq3LpIhw
๐ ๏ธ The Tech Stack
I chose a stack that balances raw performance with a modern developer experience:
- Backend: Java 21 + Spring Boot 3.4
- AI Orchestration: Google Gemini 2.0 Flash (Multilingual Extraction)
- Security: Spring Security 6 (OAuth2 + JWT RS256)
- Database: PostgreSQL 15 (Supabase) + Caffeine L2 Cache
- Storage: Cloudflare R2 (S3 Compatible)
- Frontend: Angular 17 (Signals) + TailwindCSS + D3.js
- DevOps: Docker + Google Cloud Run + GitHub Actions
๐ง Core Engineering Challenges
1. ๐ค Zero-Entry Ingestion (Gemini 2.0 AI)
Manual data entry is where productivity goes to die. I engineered a pipeline that allows users to simply forward an application confirmation email to a unique system address.
- The Flow: Gmail --> Cloudmailin Webhook --> Spring Boot --> Gemini 2.0 Flash.
- Smart Upsert: The system uses fuzzy matching to detect if an email is a new job or an update (e.g., status change from "Applied" to "Interview") for an existing record.
- Multilingual: If you apply for a job in the Netherlands (Dutch) or Germany (German), the AI automatically translates the metadata into English for your dashboard.
// Logic snippet for AI extraction using Spring Boot 3.4 RestClient
public JobDTO parseEmail(String rawBody) {
String prompt = "Extract company, role, and status from this email. Return JSON...";
// We send unstructured text; AI returns structured JobDTO
var response = restClient.post()
.uri(apiUrl)
.body(requestBody)
.retrieve()
.body(String.class);
return objectMapper.readValue(response, JobDTO.class);
}
2. ๐ Hybrid Security Architecture
Most tutorials make you choose: Social Login or Passwords. I wanted both. I implemented a custom OAuth2SuccessHandler that merges identities.
Users can sign in with Google/GitHub but also set a local password later. The system remains stateless using JWTs with high-precision expiration handling.
3. โก High-Performance State & Caching
As the data grows, fetching 100+ job applications can lag. I solved this with two patterns:
- Server-Side Everything: Pagination, Filtering, and Sorting are all done at the Database level using Spring Data Pageable.
- Caffeine Cache: User profiles and dashboard statistics are stored in an L2 in-memory cache, reducing Supabase query latency to < 15ms.
4. โ๏ธ Atomic Cloud Storage (Cloudflare R2)
I integrated Cloudflare R2 for profile management. To ensure a clean bucket, I implemented Atomic Cleanup: whenever a user updates their photo, the backend automatically issues a deleteObject command to remove the old file, preventing "orphaned assets" and saving costs.
๐จ Responsive Hybrid UI (Angular Signals)
On the frontend, I used Angular 17 Signals for fine-grained reactivity. The UI is a hybrid:
- Desktop: A high-density data grid for power users.
- Mobile: A custom Expandable Card System. Users can tap a card to reveal full details (notes, salary, links) and access large, touch-friendly action buttons.
| Mobile View | Desktop View |
|---|---|
![]() |
![]() |
๐๏ธ Open Source "Sandbox" Mode
I wanted to make this project easy for anyone to contribute to. I built a Developer Sandbox:
- Mock AI: If you don't have a Gemini API key, the app automatically switches to a Mock Service that returns realistic data.
- Local Storage: Files are saved to a local folder instead of R2.
- MailHog: All outgoing emails are trapped in a local dev UI.
You can run the entire system with one command:
docker-compose up -d && ./mvnw spring-boot:run
Conclusion
Building JobTrackerPro taught me that "Full Stack" isn't just about connecting a UI with Backend. Itโs about handling Timezones (forced UTC on JVM), XSS protection on extracted URLs, and ensuring Transactional Integrity between the Cloud and the DB.
Iโm keeping this project Open Source. Feel free to fork it, star it, or use it to land your next job! ๐
- Monorepo: thughari/JobTrackerPro
๐ฌ Discussion
How are you handling unstructured data ingestion in your apps? Have you switched from S3 to Cloudflare R2 yet? Let's talk in the comments!
Thanks for reading! Happy coding! ๐



Top comments (0)