A full-stack Learning Management System where instructors create and sell courses, and students browse, purchase, and learn through a video-based platform with progress tracking.
Live demo: add your deployed link here after Phase 8 Video walkthrough: optional, add a Loom link if you record one
- Browse and search published courses
- View detailed course pages (curriculum, instructor, pricing)
- Purchase courses via Stripe Checkout
- Watch video lectures with a resumable player
- Track completion progress per course
- "My Learning" dashboard of enrolled courses
- Create, edit, and publish courses
- Upload thumbnails and lecture videos (Cloudinary)
- Organize content into sections and lectures
- Dashboard with per-course revenue and enrollment stats (MongoDB aggregation)
- JWT-based authentication with role-based access control (student / instructor)
- Stripe Checkout + webhook-driven enrollment (source of truth is the webhook, not the client redirect)
- Cloudinary media pipeline via direct buffer streaming (no temp files on disk)
- MongoDB text search for course discovery
Frontend: React, Vite, Tailwind CSS v4, React Router, Axios, React Hook Form, React Player, React Hot Toast
Backend: Node.js, Express, MongoDB (Mongoose), JWT, bcrypt, Multer, Cloudinary SDK, Stripe SDK
Infrastructure: MongoDB Atlas · Cloudinary · Stripe · Vercel (frontend) · Render (backend)
Request → Route → Middleware (auth / validation) → Controller → Service → Model → MongoDB
- Service layer separates business logic (e.g.
enrollmentService.enrollStudent()) from HTTP concerns, so the same enrollment logic is reusable whether triggered by a manual action or a Stripe webhook. - Ownership-enforced queries — course mutations use
Course.findOne({ _id, instructor: instructorId })rather thanfindById, so an instructor can never modify another instructor's course even by guessing an ID. - Enrollment gating is enforced server-side (not just hidden in the UI) — the progress and lecture endpoints verify enrollment before returning data.
- Node.js 18+
- MongoDB Atlas account (free tier)
- Cloudinary account (free tier)
- Stripe account (test mode)
# Clone
git clone <your-repo-url>
cd coursly
# Backend
cd server
npm install
# create .env — see server/.env.example
npm run dev
# Frontend (in a new terminal)
cd client
npm install
# create .env — see client/.env.example
npm run devserver/.env
PORT=5000
MONGO_URI=your_mongodb_atlas_connection_string
JWT_SECRET=your_random_secret
CLIENT_URL=http://localhost:5173
NODE_ENV=development
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
client/.env
VITE_API_URL=http://localhost:5000/api
VITE_STRIPE_PUBLISHABLE_KEY=
Use the Stripe CLI to forward webhooks to your local server:
stripe listen --forward-to localhost:5000/api/payments/webhookTest card: 4242 4242 4242 4242, any future expiry, any CVC/ZIP.
coursly/
├── client/ # React + Vite frontend
│ └── src/
│ ├── api/ # Axios request functions
│ ├── components/ # Shared UI components
│ ├── context/ # AuthContext
│ ├── pages/ # Route-level pages (auth, student, instructor)
│ └── routes/ # AppRoutes, ProtectedRoute
│
└── server/ # Express backend
└── src/
├── config/ # DB, Cloudinary, Stripe setup
├── controllers/ # Request/response handling
├── services/ # Business logic
├── models/ # Mongoose schemas
├── middleware/ # Auth, error handling, uploads
└── routes/ # Express routers
- Course reviews and ratings
- Coupon codes
- Completion certificates
- AI-generated course summaries and quizzes
- Discussion forum per course
MIT