Back to Projects

CodeCrafters

Next.jsTypeScriptReactPrismaMySQLNextAuth.jsMuxRazorpayUploadThingTailwind CSSZustandMongoDBRadix UIRecharts
IMAGE — Home & Course Catalog
IMAGE — Sign-in page with Google OAuth + credentials form
IMAGE — Course browse/search page with category filter and search bar
IMAGE — Course detail page (before purchase) showing chapters, price, enroll button
IMAGE — Razorpay payment modal during checkout
IMAGE — Student dashboard showing in-progress and completed courses with progress bars
IMAGE — Chapter video player (Mux) with sidebar showing chapter list and completion checkmarks
IMAGE — Teacher course list (data table with published/draft status)
IMAGE — Teacher course editor (title, description, image upload, category, price, chapters)
IMAGE — Chapter editor (title, rich text description, video upload, free/paid toggle)
IMAGE — Drag-and-drop chapter reordering in course editor
IMAGE — Teacher analytics dashboard with revenue chart (Recharts)
IMAGE — Password reset OTP email verification flow
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
VIDEO
Home & Course Catalog1 / 23

Overview

A full-featured Learning Management System where instructors create and publish video-based courses, and students browse, purchase (via Razorpay), and track their learning progress. Built with Next.js 14 App Router, Prisma ORM, MySQL, Mux for video streaming, and NextAuth.js for authentication.

Key Features

Auth & Security
  • Authentication using NextAuth.js with credentials and Google OAuth (JWT sessions)
  • Email OTP-based password reset with rate limiting and 45-second auto-expiry (MongoDB TTL)
Core Features
  • Browse and filter courses by category with debounced title search across the published catalog
  • Purchase courses using Razorpay with HMAC-SHA256 signature verification and duplicate purchase prevention
  • Mark chapters as completed or uncompleted with per-chapter progress tracking
  • Progress calculation of each course displayed as percentage on student dashboard
  • Student dashboard showing in-progress and completed courses with progress bars
  • Teacher mode with role-based access to create and manage courses
  • Create new courses with title, description, image upload, category, and pricing
  • Create new chapters with rich text descriptions, video uploads, and free/paid access control
  • Easily reorder chapter position with drag-and-drop
  • Upload thumbnails, attachments, and videos using UploadThing
  • HLS video player using Mux with playback controls and auto-play next chapter
  • Rich text editor for chapter description using React Quill
  • ORM using Prisma with MySQL database and full-text search on course titles
  • Instructor analytics dashboard with per-course revenue and sales charts (Recharts)
  • Course attachments (PDFs, resources) that students can download
  • Free course enrollment for courses priced at zero — separate enrollment endpoint bypassing payment flow
Video & Content
  • Video processing and adaptive streaming using Mux with CDN delivery

Architecture Overview

How the system is structured from frontend to external services.

01

Frontend

Next.js 14 (App Router)ReactTailwind CSSRadix UIZustand

Server Components for static pages, Client Components for interactive features like drag-and-drop chapter reordering, Mux video playback, and the Razorpay payment modal. Zustand for global state management, SWR for data fetching and caching.

02

Backend & API

Next.js API RoutesPrisma ORMNextAuth.js (JWT)

RESTful API routes handling course CRUD, purchase verification, and progress tracking. JWT-based sessions with credentials and Google OAuth providers. Server-side authorization checks on every protected route.

03

Databases

MySQL (primary)MongoDB (OTP store)

MySQL via Prisma for relational data — users, courses, chapters, purchases, and progress. Full-text search index on course titles. MongoDB with TTL indexes for time-sensitive OTP documents that auto-expire after 45 seconds.

04

External Services

MuxRazorpayUploadThingGmail SMTP

Mux handles video encoding, adaptive streaming, and CDN delivery. Razorpay processes payments with HMAC-SHA256 signature verification. UploadThing manages file uploads for images, videos, and attachments. Nodemailer sends OTP emails via Gmail SMTP.

Data Model

Core entities and how they relate to each other.

User

Has many Sessions, Accounts (OAuth). Has one PasswordResetRequest, one OTP. Referenced by Course (userId), Purchase (userId), UserProgress (userId)

Central user account — supports both credentials (email/password) and Google OAuth sign-in

id (uuid)nameemail (unique)password (hashed, optional for OAuth)phoneNumber (unique)emailVerifiedimage

Account

Belongs to User (cascade delete on user removal)

OAuth provider links — stores Google OAuth tokens and provider metadata for each connected account

providerproviderAccountIdaccess_tokenrefresh_tokenexpires_at@@unique([provider, providerAccountId])

Session

Belongs to User (cascade delete on user removal)

Stores active user sessions with unique tokens and expiry timestamps

sessionToken (unique)expiresuserId

Course

Belongs to Category (optional). Has many Chapters (cascade delete), Attachments (cascade delete), Purchases (cascade delete)

Course metadata with publish/unpublish workflow — requires title, description, image, category, and at least one published chapter to go live

userIdtitle (full-text indexed)descriptionimageUrlpriceisPublishedcategoryId

Category

Has many Courses

Course categories for catalog filtering — seeded with values like Web Development, Data Science, etc.

id (uuid)name (unique)

Chapter

Belongs to Course (cascade delete). Has one MuxData (video asset, cascade delete). Has many UserProgress records (cascade delete)

Individual lessons within a course — supports ordering, free preview access, and Mux video hosting

titledescriptionvideoUrlposition (int, drag-and-drop order)isPublishedisFree

MuxData

One-to-one with Chapter (cascade delete)

Links a chapter to its Mux video asset — deleted and recreated when the instructor replaces a video

assetIdplaybackIdchapterId (unique)

Attachment

Belongs to Course (cascade delete)

Downloadable course resources (PDFs, documents) that students can access after purchase

nameurlcourseId

Purchase

Belongs to Course (cascade delete). References User by userId

Records successful Razorpay transactions — stores order and payment IDs for audit trail

userIdcourseIdorderId (unique)paymentId (unique)@@unique([userId, courseId])

UserProgress

Belongs to Chapter (cascade delete). References User by userId

Per-chapter completion tracking — aggregated to calculate course progress percentage

userIdchapterIdisCompleted@@unique([userId, chapterId])

VerificationToken

Standalone — managed internally by NextAuth.js

Email verification tokens with identifier and expiry — managed by NextAuth.js Prisma Adapter for email verification flows

id (uuid)identifiertoken (unique)expires@@unique([identifier, token])

PasswordResetRequest

Belongs to User via email (cascade delete)

Rate-limits password reset attempts — tracks how many OTPs a user has requested to prevent abuse

email (unique)countlastReset (BigInt timestamp)

OTP (Prisma + MongoDB)

Belongs to User via email (Prisma: cascade delete, MongoDB: TTL auto-cleanup)

Stores one-time passwords for password reset — Prisma model for relational linking, Mongoose model with 45-second TTL auto-expiry for the actual verification

email (unique)otp (6-digit code)otpTypeTTL index: expireAfterSeconds: 45

Key Flows

Step-by-step walkthrough of the main user and system flows.

Course Purchase (Razorpay)

  1. 1Student clicks "Enroll" on a course page
  2. 2API creates a Razorpay order with course price and returns the order ID
  3. 3Frontend opens the Razorpay payment modal with the order details
  4. 4After payment, Razorpay sends back order_id, payment_id, and signature
  5. 5API concatenates order_id|payment_id, runs HMAC-SHA256 with the secret key, and compares against the received signature
  6. 6If valid, a Purchase record is created (unique constraint prevents duplicates)
  7. 7Student is redirected to the course with a confetti celebration

Course Publishing Workflow

  1. 1Instructor creates a new course (initially unpublished draft)
  2. 2Fills in required fields: title, description, course image, and category
  3. 3Creates chapters with drag-and-drop reordering, uploads videos to Mux
  4. 4Publishes at least one chapter (each chapter requires title and video)
  5. 5API validation gate checks all required fields before allowing course publish
  6. 6If instructor unpublishes or deletes their last published chapter, course auto-unpublishes to prevent broken listings

Password Reset (OTP)

  1. 1User clicks "Forgot Password" and enters their email
  2. 2API checks rate limit via PasswordResetRequest model (returns 429 if exceeded)
  3. 3Generates a 6-digit OTP, stores it in MongoDB with a 45-second TTL index
  4. 4Sends OTP to user's email via Nodemailer / Gmail SMTP
  5. 5User enters OTP within the 45-second window
  6. 6API verifies OTP against MongoDB (document auto-deleted after TTL expires)
  7. 7If valid, user sets a new password (hashed with bcrypt) and is logged in

Challenges I Solved

01

Securing Razorpay Payments

The Problem

The purchase API could be called directly with fake payment responses, allowing unauthorized course access without actual payment.

How I Solved It

Server-side HMAC-SHA256 verification of Razorpay's signature by concatenating order_id|payment_id and comparing against the hash. Added a Prisma @@unique constraint on [userId, courseId] to prevent duplicate purchases at the database level.

02

Preventing OTP Spam & Brute Force

The Problem

The /send-otp endpoint could be abused for email flooding, and attackers could brute-force the 6-digit code with repeated guesses.

How I Solved It

Rate limiting via MySQL PasswordResetRequest model tracks request count and timestamps. MongoDB TTL index auto-expires OTP documents after 45 seconds, making brute force impractical within the time window.

03

Mux Video Asset Cleanup

The Problem

When instructors replace or delete chapter videos, the old Mux assets pile up on the account and keep incurring costs.

How I Solved It

Before every PATCH/DELETE on a chapter, check for existing MuxData and delete the old Mux asset first. Cascade deletion on course removal ensures no orphaned video assets remain.

04

Publish / Unpublish Cascade Logic

The Problem

Publishing a course with missing chapters, images, or descriptions leads to broken product listings visible to students.

How I Solved It

Validation gate requires title, description, image, category, and at least one published chapter before allowing publish. Auto-unpublish triggers when the last published chapter is deleted or unpublished.

Summary

CodeCrafters demonstrates full-stack development with Next.js 14, secure payment processing via Razorpay, video streaming infrastructure with Mux, and complex database relationships using Prisma. The project showcases handling real-world edge cases like OTP rate limiting, video asset lifecycle management, and cascading publish/unpublish logic.