Skip to content

mayankSystems/mayankgupta-portfolio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

40 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿš€ Mayank Gupta โ€” Portfolio Website

Deployed on Vercel Deployed on Render

Live site: Mayank's Portfolio
Backend API: Backend's Health Check

A professional full-stack portfolio: a static site (HTML/CSS/JS) front-end with a Spring Boot REST API backend. Features include a contact form (emails via SendGrid), resume download (latest PDF), and a responsive, animated UI. Deployed on Vercel (frontend) and Railway (backend).

๐Ÿ› ๏ธ Tech Stack

Layer Technology
Frontend HTML5 ยท CSS3 ยท Vanilla JS
Backend Java 17 ยท Spring Boot 3.2 ยท Spring Web
Email SendGrid API (Java SDK)
Validation Jakarta Bean Validation
Rate Limiting Bucket4j (3 req/IP/hour)
Deployment Vercel (FE) ยท Render (BE)
Container Docker (multi-stage, non-root)

โœจ Features

  • Contact Form: Visitor submits name/email/message. Backend uses SendGrid to email the owner and send a confirmation. Input validated (Jakarta Validation) and rate-limited (3 requests/IP/hour).
  • Resume Download: Latest resume (resume-v3.pdf) served via /api/resume/download. The old resume.pdf is kept as a backup.
  • Responsive UI: Mobile-friendly portfolio with scroll fade-in animations and a typing text effect.
  • Secure: CORS restricted to the frontend domain; no secrets in code (all configs via environment variables); uses HTTPS deployment.
  • Offline Fallback: If the email API fails, the page displays a mailto: link for manual contact as a fallback.
  • Observability: Example logging of resume downloads and contact submissions (in-memory counters, with suggestion to persist in a DB for production).

๐Ÿ—๏ธ Architecture

Mermaid sequence diagram for request flow:

sequenceDiagram
    participant U as User
    participant F as Frontend
    participant B as Backend (Spring Boot API)
    participant S as SendGrid
    participant E as Email Inbox

    U->>F: Fill contact form
    F->>B: POST /api/contact (name, email, subject, message)
    B->>S: SendGrid.sendEmail()
    S->>E: Deliver email (owner & user)
    E-->>U: User receives confirmation email
    
    U->>F: Click resume download
    F->>B: GET /api/resume/download
    B-->>F: Return Resume PDF
    F->>U: Prompt file download
Loading

๐Ÿ“ Project Structure

mayankgupta-portfolio/
โ”œโ”€โ”€ portfolio/                  โ† Frontend (static site)
โ”‚   โ”œโ”€โ”€ index.html              โ† Main HTML, CSS, JS
โ”‚   โ”œโ”€โ”€ photo.jpg               โ† Profile picture
โ”‚   โ””โ”€โ”€ og-image.png            โ† Open Graph preview image
โ”‚
โ””โ”€โ”€ portfolio-backend/          โ† Spring Boot REST API
    โ”œโ”€โ”€ src/
    โ”‚   โ””โ”€โ”€ main/java/dev/mayank/portfolio/
    โ”‚       โ”œโ”€โ”€ controller/
    โ”‚       โ”‚   โ”œโ”€โ”€ ContactController.java   โ† POST /api/contact
    โ”‚       โ”‚   โ”œโ”€โ”€ ResumeController.java    โ† GET /api/resume/download
    โ”‚       โ”‚   โ””โ”€โ”€ PortfolioController.java โ† GET /api/health, /api/info
    โ”‚       โ”œโ”€โ”€ service/
    โ”‚       โ”‚   โ””โ”€โ”€ ContactService.java       โ† Email sending (SendGrid)
    โ”‚       โ”œโ”€โ”€ model/    โ† DTOs (ContactRequest, ApiResponse, etc.)
    โ”‚       โ””โ”€โ”€ config/   โ† CORS config, global exceptions
    โ”œโ”€โ”€ src/main/resources/static/
    โ”‚   โ”œโ”€โ”€ resume-v3.pdf       โ† Latest resume PDF
    โ”‚   โ””โ”€โ”€ resume.pdf          โ† Old resume (backup)
    โ”œโ”€โ”€ Dockerfile
    โ””โ”€โ”€ pom.xml

โš™๏ธ API Endpoints

Method Endpoint Description
GET /api/health Health check (returns status)
GET /api/info Portfolio info (owner name/tagline)
POST /api/contact Handle contact form submission (JSON)
GET /api/resume/download Download latest resume PDF

๐Ÿƒโ€โ™‚๏ธ Run Locally

Frontend

cd portfolio
# Serve statically (no build needed)
python3 -m http.server 3000

Open http://localhost:3000 in your browser.

Backend

cd portfolio-backend

# Set environment variables (example for Unix/Mac)
export SENDGRID_API_KEY=SG.xxxxxxxx
export CONTACT_EMAIL=ce.mayank8@gmail.com
export FROM_EMAIL=ce.mayank8@gmail.com
export FRONTEND_URL=http://localhost:3000
export PORT=8080

mvn spring-boot:run

The API will be available at http://localhost:8080.

Testing the API

# Health check
curl http://localhost:8080/api/health

# Submit contact form (replace with your own data)
curl -X POST http://localhost:8080/api/contact \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com","subject":"Hello","message":"This is a test."}'

# Download resume PDF
curl -L http://localhost:8080/api/resume/download --output Mayank_Gupta_Resume.pdf

A successful contact form submission returns {"success":true} and sends emails to both you and the visitor. The resume download command saves Mayank_Gupta_Resume.pdf locally.

๐ŸŒ Deployment

  • Backend (Render):
    1. Go to render.com and sign in with GitHub
    2. Click "New +" โ†’ "Web Service"
    3. Connect your mayankgupta-portfolio GitHub repository
    4. Set the following:
      • Name: portfolio-backend
      • Environment: Docker
      • Root Directory: portfolio-backend
      • Auto-Deploy: Yes (deploy on push)
    5. Add environment variables in the Render dashboard:
    SENDGRID_API_KEY=YOUR_SENDGRID_KEY
    CONTACT_EMAIL=ce.mayank8@gmail.com
    FROM_EMAIL=ce.mayank8@gmail.com
    FRONTEND_URL=https://your-portfolio.vercel.app
    PORT=8080
    
  • Frontend (Vercel): Connect the portfolio folder on Vercel (Framework: Other / Static). After deploying, update the API_BASE URL in index.html to your Render backend URL (e.g. https://portfolio-backend.onrender.com).

๐Ÿ”ง Environment Variables

Variable Purpose
SENDGRID_API_KEY API key for sending emails
CONTACT_EMAIL Email to receive contact form
FROM_EMAIL Verified sender email address
FRONTEND_URL Frontend domain (for CORS)
PORT Server port (default 8080)

โš ๏ธ Troubleshooting

  • CORS Errors: Ensure your backend allows your frontend origin. For example, use @CrossOrigin(origins="https://your-portfolio.vercel.app") on controllers or configure global CORS to include your Vercel domain or http://localhost:3000 for local dev.
  • SendGrid 401 Unauthorized: Make sure SENDGRID_API_KEY is correctly set in your environment (Render/local).
  • SendGrid 403 Forbidden: The FROM_EMAIL must be a verified sender identity in SendGrid (via Single Sender Verification or Domain Authentication). Use a verified email (e.g. your Gmail) or set up domain authentication to fix this.
  • Email Not Received: Check SendGrid Email Activity for delivery, bounces, or drops. A "Dropped" status often indicates unverified sender or spam filtering.
  • Broken Email Link (404 /cdn-cgi): Remove any Cloudflare /cdn-cgi/email-protection script from the HTML. Use a direct mailto: link instead (the JS fallback sets this up).
  • API Fallback: If the contact form submission fails (network issue), the page will display a mailto: link so users can email you directly.

๐Ÿ“ˆ Observability (Stats & Logging)

  • Resume Downloads: You can log or count downloads in ResumeController. Example:
    @GetMapping("/api/resume/download")
    public ResponseEntity<Resource> downloadResume() {
        int count = downloadCount.incrementAndGet();
        System.out.println("Resume downloads: " + count);
        Resource res = new ClassPathResource("static/resume-v3.pdf");
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=Mayank_Gupta_Resume.pdf")
            .contentType(MediaType.APPLICATION_PDF)
            .body(res);
    }
  • Contact Submissions: In ContactController or ContactService, increment a counter on each form submission:
    int leads = contactCount.incrementAndGet();
    System.out.println("Contacts received: " + leads);
  • Stats Endpoint (Optional): For advanced tracking, add /api/stats:
    @RestController
    @RequestMapping("/api/stats")
    public class StatsController {
        private AtomicInteger resumeDownloads = new AtomicInteger();
        private AtomicInteger contacts = new AtomicInteger();
        @GetMapping
        public Map<String,Integer> getStats() {
            return Map.of("resumeDownloads", resumeDownloads.get(),
                          "contacts", contacts.get());
        }
    }
    In production, consider persisting these stats to a database (e.g. PostgreSQL) with timestamps.

๐Ÿ”ฎ Future Improvements

  • Email Authentication: Configure SendGrid domain authentication and SPF/DKIM records for a custom sender domain, improving deliverability.
  • Persistent Leads: Store contact messages and stats in a database for audit/analytics (e.g. PostgreSQL, MongoDB).
  • Analytics Dashboard: Build a simple admin dashboard or integrate third-party analytics to visualize contacts and resume download stats.
  • CI/CD & Testing: Set up GitHub Actions for automated builds/tests and deploy pipelines.
  • SEO & Social: Add a sitemap, robots.txt, and dynamic Open Graph images for better SEO and link previews.
  • UI Enhancements: Dark mode toggle, a blog or project filter section, and improved accessibility features.

๐Ÿ‘จโ€๐Ÿ’ป Author

Mayank Gupta โ€” Senior Backend Engineer
๐Ÿ“ง ce.mayank8@gmail.com
๐Ÿ”— LinkedIn ยท GitHub

About

Mayank's Portfolio Website

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors