<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Hari babu Thatikonda</title>
    <description>The latest articles on Forem by Hari babu Thatikonda (@thughari).</description>
    <link>https://forem.com/thughari</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3040758%2F696e4358-4236-40c1-b251-50561cb4400f.gif</url>
      <title>Forem: Hari babu Thatikonda</title>
      <link>https://forem.com/thughari</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thughari"/>
    <language>en</language>
    <item>
      <title>How I Built an AI-Powered Email Ingestion System with Spring Boot, Angular and Gemini</title>
      <dc:creator>Hari babu Thatikonda</dc:creator>
      <pubDate>Mon, 02 Feb 2026 07:56:42 +0000</pubDate>
      <link>https://forem.com/thughari/how-i-built-an-ai-powered-email-ingestion-system-with-spring-boot-angular-and-gemini-2a4n</link>
      <guid>https://forem.com/thughari/how-i-built-an-ai-powered-email-ingestion-system-with-spring-boot-angular-and-gemini-2a4n</guid>
      <description>&lt;p&gt;Let’s be honest: searching for a job is a full-time job in itself. 😩&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;automated the data entry&lt;/strong&gt;, visualized my pipeline, and calculated my conversion rates.&lt;/p&gt;

&lt;p&gt;So, I built &lt;strong&gt;&lt;a href="https://thughari.github.io/JobTrackerPro" rel="noopener noreferrer"&gt;JobTrackerPro&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I treated this as a &lt;strong&gt;System Design Challenge&lt;/strong&gt;. It’s not just a CRUD app; it’s an event-driven ecosystem featuring &lt;strong&gt;Generative AI&lt;/strong&gt;, &lt;strong&gt;L2 Caching&lt;/strong&gt;, and &lt;strong&gt;Cloud-Native Storage&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 The Goods
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;🚀 Live Demo:&lt;/strong&gt; &lt;a href="https://thughari.github.io/JobTrackerPro" rel="noopener noreferrer"&gt;https://thughari.github.io/JobTrackerPro&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;📂 Monorepo (Source Code):&lt;/strong&gt; &lt;a href="https://github.com/thughari/JobTrackerPro" rel="noopener noreferrer"&gt;github.com/thughari/JobTrackerPro&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;📺 Video Walkthrough:&lt;/strong&gt; &lt;a href="https://youtu.be/w6iAq3LpIhw" rel="noopener noreferrer"&gt;https://youtu.be/w6iAq3LpIhw&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ The Tech Stack
&lt;/h2&gt;

&lt;p&gt;I chose a stack that balances raw performance with a modern developer experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Backend:&lt;/strong&gt; Java 21 + Spring Boot 3.4&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AI Orchestration:&lt;/strong&gt; Google Gemini 2.0 Flash (Multilingual Extraction)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Security:&lt;/strong&gt; Spring Security 6 (OAuth2 + JWT RS256)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL 15 (Supabase) + Caffeine L2 Cache&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Storage:&lt;/strong&gt; Cloudflare R2 (S3 Compatible)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Frontend:&lt;/strong&gt; Angular 17 (Signals) + TailwindCSS + D3.js&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;DevOps:&lt;/strong&gt; Docker + Google Cloud Run + GitHub Actions&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jv7qxkoc42u6cc3tt3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jv7qxkoc42u6cc3tt3t.png" alt="Dashboard" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Core Engineering Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 🤖 Zero-Entry Ingestion (Gemini 2.0 AI)
&lt;/h3&gt;

&lt;p&gt;Manual data entry is where productivity goes to die. I engineered a pipeline that allows users to simply &lt;strong&gt;forward&lt;/strong&gt; an application confirmation email to a unique system address.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The Flow:&lt;/strong&gt; Gmail --&amp;gt; Cloudmailin Webhook --&amp;gt; Spring Boot --&amp;gt; &lt;strong&gt;Gemini 2.0 Flash&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Smart Upsert:&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Multilingual:&lt;/strong&gt; If you apply for a job in the Netherlands (Dutch) or Germany (German), the AI automatically translates the metadata into English for your dashboard.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Logic snippet for AI extraction using Spring Boot 3.4 RestClient&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;JobDTO&lt;/span&gt; &lt;span class="nf"&gt;parseEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rawBody&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Extract company, role, and status from this email. Return JSON..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// We send unstructured text; AI returns structured JobDTO&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiUrl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieve&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;JobDTO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. 🔐 Hybrid Security Architecture
&lt;/h3&gt;

&lt;p&gt;Most tutorials make you choose: Social Login or Passwords. I wanted both. I implemented a custom &lt;code&gt;OAuth2SuccessHandler&lt;/code&gt; that merges identities.&lt;/p&gt;

&lt;p&gt;Users can sign in with &lt;strong&gt;Google/GitHub&lt;/strong&gt; but also set a local password later. The system remains stateless using &lt;strong&gt;JWTs&lt;/strong&gt; with high-precision expiration handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. ⚡ High-Performance State &amp;amp; Caching
&lt;/h3&gt;

&lt;p&gt;As the data grows, fetching 100+ job applications can lag. I solved this with two patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Server-Side Everything:&lt;/strong&gt; Pagination, Filtering, and Sorting are all done at the Database level using Spring Data Pageable.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Caffeine Cache:&lt;/strong&gt; User profiles and dashboard statistics are stored in an L2 in-memory cache, reducing Supabase query latency to &lt;strong&gt;&amp;lt; 15ms&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. ☁️ Atomic Cloud Storage (Cloudflare R2)
&lt;/h3&gt;

&lt;p&gt;I integrated &lt;strong&gt;Cloudflare R2&lt;/strong&gt; for profile management. To ensure a clean bucket, I implemented &lt;strong&gt;Atomic Cleanup&lt;/strong&gt;: whenever a user updates their photo, the backend automatically issues a &lt;code&gt;deleteObject&lt;/code&gt; command to remove the old file, preventing "orphaned assets" and saving costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Responsive Hybrid UI (Angular Signals)
&lt;/h2&gt;

&lt;p&gt;On the frontend, I used &lt;strong&gt;Angular 17 Signals&lt;/strong&gt; for fine-grained reactivity. The UI is a hybrid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Desktop:&lt;/strong&gt; A high-density data grid for power users.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mobile:&lt;/strong&gt; A custom &lt;strong&gt;Expandable Card System&lt;/strong&gt;. Users can tap a card to reveal full details (notes, salary, links) and access large, touch-friendly action buttons.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mobile View&lt;/th&gt;
&lt;th&gt;Desktop View&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcyutt9uhcgwxvgscpo7.png" alt="Mobile View" width="368" height="802"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtbzeb8obco7kjuoftqt.png" alt="Desktop View" width="800" height="384"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🏗️ Open Source "Sandbox" Mode
&lt;/h2&gt;

&lt;p&gt;I wanted to make this project easy for anyone to contribute to. I built a &lt;strong&gt;Developer Sandbox&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Mock AI:&lt;/strong&gt; If you don't have a Gemini API key, the app automatically switches to a Mock Service that returns realistic data.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Local Storage:&lt;/strong&gt; Files are saved to a local folder instead of R2.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;MailHog:&lt;/strong&gt; All outgoing emails are trapped in a local dev UI.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can run the entire system with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./mvnw spring-boot:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building &lt;strong&gt;JobTrackerPro&lt;/strong&gt; taught me that "Full Stack" isn't just about connecting a UI with Backend. It’s about handling &lt;strong&gt;Timezones&lt;/strong&gt; (forced UTC on JVM), &lt;strong&gt;XSS protection&lt;/strong&gt; on extracted URLs, and ensuring &lt;strong&gt;Transactional Integrity&lt;/strong&gt; between the Cloud and the DB.&lt;/p&gt;

&lt;p&gt;I’m keeping this project &lt;strong&gt;Open Source&lt;/strong&gt;. Feel free to fork it, star it, or use it to land your next job! 👇&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Monorepo:&lt;/strong&gt; &lt;a href="https://github.com/thughari/JobTrackerPro" rel="noopener noreferrer"&gt;thughari/JobTrackerPro&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💬 Discussion
&lt;/h3&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;Thanks for reading! Happy coding! 🚀&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>ai</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>I built a simple anonymous 1-on-1 video chat app - just talk, no logins.</title>
      <dc:creator>Hari babu Thatikonda</dc:creator>
      <pubDate>Sun, 15 Jun 2025 19:34:51 +0000</pubDate>
      <link>https://forem.com/thughari/i-built-a-simple-anonymous-1-on-1-video-chat-app-just-talk-no-logins-4j2d</link>
      <guid>https://forem.com/thughari/i-built-a-simple-anonymous-1-on-1-video-chat-app-just-talk-no-logins-4j2d</guid>
      <description>&lt;p&gt;Hi folks 👋&lt;/p&gt;

&lt;p&gt;I’ve always found small talk and real-time conversations a bit challenging - so I decided to turn that discomfort into something creative.&lt;/p&gt;

&lt;p&gt;I built an anonymous 1-on-1 video chat app - no signups, no usernames, just a direct connection between two strangers.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://randomchat-hfta.onrender.com" rel="noopener noreferrer"&gt;Try it here&lt;/a&gt;&lt;br&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/thughari/RandomChat" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ Tech Stack:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 21 + Virtual Threads&lt;/strong&gt; (for lightweight concurrency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spring Boot&lt;/strong&gt; (backend server)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSockets&lt;/strong&gt; (for signaling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC&lt;/strong&gt; (for peer-to-peer video streaming)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; (for deployment)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  💡 Why I Built It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To understand the actual handshake between browsers during a WebRTC connection.&lt;/li&gt;
&lt;li&gt;To explore how virtual threads in Java 21 could scale real-time communication.&lt;/li&gt;
&lt;li&gt;And honestly, to build something weirdly personal - something that felt a little like Omegle, but from scratch.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧠 What I Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC is deceptively complex.&lt;/strong&gt; STUN/TURN servers are essential once you're outside local networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java virtual threads are amazing&lt;/strong&gt; for handling lots of concurrent lightweight tasks - perfect for chat apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signaling is half the battle&lt;/strong&gt; - getting peers to connect securely and efficiently is a big chunk of the work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Being introverted doesn’t mean you can’t build communication tools&lt;/strong&gt; - it just means your version might be simpler, quieter, and intentional.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🚧 Things I Want to Improve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Better UI/UX (right now it’s very minimal).&lt;/li&gt;
&lt;li&gt;Optional audio-only mode.&lt;/li&gt;
&lt;li&gt;Mobile responsiveness and fallback logic.&lt;/li&gt;
&lt;li&gt;Add basic moderation or CAPTCHA to prevent abuse.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If this sounds interesting to you, feel free to try it and let me know what you think - feedback, bugs, weird edge cases… all welcome!&lt;/p&gt;

&lt;p&gt;Thanks for reading 🙏&lt;/p&gt;




&lt;p&gt;⭐ Check it out → &lt;a href="https://randomchat-hfta.onrender.com" rel="noopener noreferrer"&gt;Live URL&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🐙 GitHub Repo → &lt;a href="https://github.com/thughari/RandomChat" rel="noopener noreferrer"&gt;RandomChat&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>webdev</category>
      <category>java</category>
      <category>webrtc</category>
    </item>
  </channel>
</rss>
