ScentStealer
Table of Contents
About ScentStealer
ScentStealer is a full-stack web application that helps fragrance enthusiasts discover high-quality, affordable alternatives to expensive designer perfumes. Built with modern TypeScript and a scalable MERN architecture, the platform leverages community reviews, AI-powered search, and comprehensive fragrance data to help users save 70-90% on luxury scents.
The application features a sophisticated search engine with fuzzy matching, real-time updates, user authentication via OAuth 2.0, and a gamified community system with leaderboards and achievements. With over 1,000+ fragrances indexed and growing daily, ScentStealer democratizes access to luxury fragrances.
Live Site: https://scentstealer.com
Features
- Intelligent Search Engine: Fuzzy search with debouncing and autocomplete for finding fragrances and their clones instantly
- Community Reviews & Ratings: User-generated content with upvoting, commenting, and quality verification
- OAuth Authentication: Secure Google and GitHub authentication with JWT token management
- Gamification System: User levels, achievements, and leaderboards to encourage community engagement
- Q&A Forum: Stack Overflow-style question system for fragrance advice and recommendations
- Real-time Updates: Live data synchronization using Redis caching and optimistic UI updates
- SEO Optimized: Server-side rendering, structured data, and dynamic meta tags for search visibility
- Responsive Design: Mobile-first UI built with Tailwind CSS and Shadcn/ui components
- Admin Dashboard: Comprehensive moderation tools for managing content and users
- Automated Data Ingestion: Scheduled scraping and ETL pipelines for expanding fragrance database
Tech Stack
Frontend
- React 18 with TypeScript for type-safe component development
- Vite for lightning-fast builds and hot module replacement
- React Router for client-side routing with protected routes
- TanStack Query (React Query) for server state management and caching
- Zustand for lightweight global state management
- Tailwind CSS + Shadcn/ui for modern, accessible UI components
- React Helmet Async for SEO meta tag management
- Axios for HTTP client with interceptors
Backend
- Node.js + Express with TypeScript for RESTful API
- MongoDB + Mongoose for document database with schema validation
- Redis (ioredis) for caching and session management
- BullMQ for job queue processing (data ingestion, notifications)
- Passport.js for OAuth 2.0 authentication (Google, GitHub)
- JWT for stateless authentication
- Helmet + express-rate-limit for security hardening
- Winston for structured logging
- Cheerio for web scraping
DevOps & Infrastructure
- Docker + Docker Compose for containerization
- GitHub Actions for CI/CD pipelines
- Cloudflare Tunnel for secure self-hosting
- pnpm workspaces for monorepo management
- ESLint + Prettier for code quality
- Husky + lint-staged for pre-commit hooks
Architecture
Monorepo Structure
ScentStealer/
├── api/ # Backend API (Node.js + Express + TypeScript)
│ ├── src/
│ │ ├── config/ # Database, Redis, OAuth configurations
│ │ ├── middleware/ # Authentication, rate limiting, error handling
│ │ ├── models/ # Mongoose schemas (User, Fragrance, Review, etc.)
│ │ ├── routes/ # RESTful API endpoints
│ │ ├── jobs/ # Background job processors
│ │ └── utils/ # Helper functions and services
│ └── Dockerfile
├── web/ # Frontend (React + Vite + TypeScript)
│ ├── src/
│ │ ├── components/ # Reusable React components
│ │ ├── pages/ # Route-level page components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # API client and utilities
│ │ └── stores/ # Zustand state management
│ └── Dockerfile
└── docker-compose.yml # Multi-container orchestration
Key Design Patterns
- Repository Pattern: Abstracted data access layer with Mongoose models
- Service Layer: Business logic separated from route handlers
- Middleware Pipeline: Request processing with authentication, validation, rate limiting
- API Client: Centralized Axios instance with interceptors for token refresh
- Optimistic Updates: Instant UI feedback with rollback on error
- Caching Strategy: Multi-level caching (Redis, React Query) with TTL management
User Guide
Prerequisites
You must have Docker Desktop installed and running, or Node.js 18+ with MongoDB and Redis.
Quick Start (Docker)
STEP 1: Clone the repository
git clone https://github.com/alstonnguyen/ScentStealer.git cd ScentStealerSTEP 2: Configure environment variables
cp .env.prod.example .env.prod # Edit .env.prod with your MongoDB URI, Redis URL, and OAuth credentialsSTEP 3: Build and run with Docker Compose
docker-compose up -dSTEP 4: Access the application
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:3000 - API Documentation:
http://localhost:3000/api/health
- Frontend:
Self-Hosting (Production)
For a production-ready self-hosted setup with Cloudflare Tunnel and automatic SSL:
./self-host-setup.sh # Mac/Linux
# or
self-host-setup.bat # Windows
This automated script:
- Installs dependencies and configures environment
- Sets up Cloudflare Tunnel for secure public access
- Configures automatic startup on reboot
- Initializes the database with seed data
- Starts monitoring services
Full documentation: SELF_HOSTING_GUIDE.md
Development Mode
STEP 1: Install dependencies
pnpm installSTEP 2: Start development servers
# Terminal 1 - Backend API cd api && pnpm run dev # Terminal 2 - Frontend cd web && pnpm run devSTEP 3: Run data ingestion (optional)
cd api && pnpm run ingest
Below is a demo:
Search fragrances → Compare clones → Read reviews → Save money
For an in-depth look at ScentStealer features, visit scentstealer.com or check the About Page
Development Highlights
Technical Achievements
- Scalable Architecture: Monorepo structure with shared TypeScript configurations and consistent tooling across frontend/backend
- Type Safety: End-to-end TypeScript with strict mode enabled, shared types between API and client
- Performance Optimization:
- Redis caching reduced API response time by 60%
- React Query minimized unnecessary network requests
- Code splitting and lazy loading decreased initial bundle size by 40%
- Security Implementation:
- JWT refresh token rotation with httpOnly cookies
- Rate limiting (100 requests/15min per IP)
- MongoDB injection protection with express-mongo-sanitize
- Helmet.js for security headers
- SEO Strategy:
- Dynamic meta tags and Open Graph protocol
- Structured data (JSON-LD) for rich search results
- Sitemap generation and Google Search Console integration
- Testing: Unit tests with Jest, integration tests for API endpoints
- CI/CD Pipeline: Automated testing, linting, and deployment with GitHub Actions
Problem Solving
- Challenge: Handling 10,000+ fragrance entries with complex search requirements
- Solution: Implemented MongoDB text indexes with custom scoring algorithm, integrated fuzzy search with Levenshtein distance for typo tolerance
- Challenge: Preventing spam and maintaining content quality
- Solution: Built gamification system with reputation points, rate limiting per user level, and automated content moderation
- Challenge: Managing real-time updates without WebSockets
- Solution: Leveraged React Query’s background refetching with smart polling intervals, optimistic updates for instant feedback
- Challenge: Self-hosting on residential internet without public IP
- Solution: Integrated Cloudflare Tunnel for secure public access, implemented automatic reconnection and health monitoring
Future Enhancements
- Mobile app (React Native)
- AI-powered fragrance recommendations using machine learning
- Advanced filtering (notes, concentration, longevity)
- User collections and wishlists
- Email notifications for followed fragrances
License
Distributed under MIT License. See LICENSE for more information.
Repository: github.com/alstonnguyen/ScentStealer
Author: Alston Nguyen
Contact: GitHub | LinkedIn
