LearnDesk is a full-stack Learning Management System (LMS) that lets educators create and sell courses while students discover, enroll in, and learn from them. It combines a MERN stack backend with an AI-powered natural-language course search, secure payments, and OTP-based account recovery — all in one platform.
- 🤖 AI-Powered Course Search: Users can type natural-language queries (e.g. "I want to learn to build mobile apps") and Gemini AI maps the intent to the right course category/level, falling back to keyword search when no direct match exists.
- 🔐 Secure Authentication: Email/password signup with bcrypt hashing, Google Sign-In, JWT-based sessions via httpOnly cookies, and OTP-based forgot-password flow (emailed via Nodemailer).
- 📚 Course & Lecture Management: Educators can create courses, add/edit/remove lectures, upload thumbnails and lecture videos (via Cloudinary), and publish/unpublish courses.
- 💳 Payments & Enrollment: Razorpay integration for course checkout, with server-side payment verification before enrolling a student.
- ⭐ Ratings & Reviews: Students can rate and review enrolled courses; one review per user per course.
- 📊 Educator Dashboard: Creator-facing dashboard to manage owned courses, lectures, and view enrolled students.
- 🎨 Responsive UI: Built with React 19, Redux Toolkit, and Tailwind CSS v4 for a fast, modern student/educator experience.
Frontend
- Framework: React 19 (via Vite)
- State Management: Redux Toolkit + React Redux
- Styling: Tailwind CSS v4
- Routing: React Router v7
- HTTP Client: Axios
- Auth (Client): Firebase (Google Sign-In)
- UI Extras: React Icons, React Toastify, React Spinners, React Simple Star Rating, Recharts
Backend
- Runtime/Framework: Node.js + Express 5
- Database: MongoDB (via Mongoose)
- Authentication: JWT (
jsonwebtoken) +bcryptjspassword hashing - AI: Google Gemini via
@google/genai(gemini-2.5-flash) - File Uploads: Multer + Cloudinary
- Payments: Razorpay
- Email: Nodemailer (Gmail transport for OTP emails)
- Validation: validator
┌───────────────────────────────────────────────────────────────┐
│ Frontend │
│ (React 19 + Vite + Redux Toolkit) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
│ │ Pages / UI │ │ Redux Slices │ │ Tailwind CSS + Icons │ │
│ │ (student, │ │ (course, │ │ │ │
│ │ admin, ai) │ │ user, etc.) │ │ │ │
│ └─────────────┘ └──────────────┘ └───────────────────────┘ │
│ │ │
│ [Axios + Cookies] │
└─────────────────────────────┼──────────────────────────────────┘
│
┌─────────▼─────────┐
│ Express Server │
│ (index.js entry) │
└─────────┬─────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───────▼───────┐ ┌─────────▼────────┐ ┌────────▼────────┐
│ Auth Layer │ │ API Routes │ │ Middlewares │
│ (JWT + cookie │ │ /api/auth │ │ isAuth, multer │
│ + Firebase) │ │ /api/user │ │ │
│ │ │ /api/course │ │ │
│ │ │ /api/payment │ │ │
│ │ │ /api/ai │ │ │
│ │ │ /api/review │ │ │
└───────┬───────┘ └─────────┬────────┘ └────────┬────────┘
│ │ │
└─────────────────────┼─────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───────▼───────┐ ┌─────────▼────────┐ ┌────────▼────────┐
│ MongoDB │ │ External APIs │ │ Cloudinary │
│ (via Mongoose)│ │ - Google Gemini │ │ (media storage)│
│ Users, Courses│ │ - Razorpay │ │ │
│ Lectures, │ │ - Gmail (OTP) │ │ │
│ Reviews,Orders│ │ │ │ │
└───────────────┘ └──────────────────┘ └─────────────────┘
LearnDesk's AI search and backend have been benchmarked to ensure a fast, reliable experience.
- 2.06s Average AI Search Latency — end-to-end time for a natural-language query to return matched courses.
- 92% Top-1 Hit Rate — how often the AI-inferred keyword surfaces the correct course category/level on the first result.
- 475ms Average API Response Time across core endpoints.
- 64% Protected API Coverage — share of API routes secured behind authentication middleware (
isAuth).
| Category | Score |
|---|---|
| Performance | 84% |
| Accessibility | 92% |
| Best Practices | 96% |
| SEO | 91% |
- User Query: A student types a query in natural language on the "Search with AI" page.
- AI Intent Matching: The query is sent to
/api/ai/search, where Gemini maps it to the closest course category/level keyword. - Course Lookup: MongoDB is first queried directly against the raw input (title, subtitle, description, category, level); if no match is found, it falls back to a regex search using the AI-inferred keyword.
- Enrollment: On checkout, Razorpay creates an order; after payment, the backend verifies the signature and updates both the
User.enrolledCoursesandCourse.enrolledStudentsarrays. - Learning: Enrolled students access lectures (video streamed from Cloudinary) and can leave a rating/review once done.
learndesk/
├── backend/
│ ├── index.js # Express app entry point
│ ├── configs/
│ │ ├── cloudinary.js # Cloudinary upload helper
│ │ ├── db.js # MongoDB connection
│ │ ├── Mail.js # Nodemailer OTP email sender
│ │ └── token.js # JWT token generator
│ ├── controllers/
│ │ ├── aiController.js # Gemini-powered course search
│ │ ├── authController.js # Signup, login, Google auth, OTP flow
│ │ ├── courseController.js # Course & lecture CRUD
│ │ ├── orderController.js # Razorpay order + payment verification
│ │ ├── reviewController.js # Course ratings & reviews
│ │ └── userController.js # Profile fetch/update
│ ├── middlewares/
│ │ ├── isAuth.js # JWT cookie verification
│ │ └── multer.js # Multipart file upload handling
│ ├── models/
│ │ ├── courseModel.js
│ │ ├── lectureModel.js
│ │ ├── orderModel.js
│ │ ├── reviewModel.js
│ │ └── userModel.js
│ └── routes/
│ ├── aiRoute.js
│ ├── authRoute.js
│ ├── courseRoute.js
│ ├── paymentRoute.js
│ ├── reviewRoute.js
│ └── userRoute.js
└── frontend/
├── src/
│ ├── components/ # Nav, Footer, Card, ReviewCard, VideoPlayer, etc.
│ ├── customHooks/ # getAllReviews, getCourseData, getCurrentUser
│ ├── pages/
│ │ ├── Home.jsx, Login.jsx, SignUp.jsx, ForgotPassword.jsx
│ │ ├── AllCourses.jsx, ViewCourse.jsx, ViewLecture.jsx
│ │ ├── EnrolledCourse.jsx, Profile.jsx, EditProfile.jsx
│ │ ├── SearchWithAi.jsx # AI-powered course search page
│ │ └── admin/ # Dashboard, Courses, CreateCourse, CreateLecture, EditLecture
│ ├── redux/ # courseSlice, lectureSlice, reviewSlice, userSlice, store
│ └── utils/
│ └── Firebase.js # Firebase (Google Sign-In) config
└── package.json
| Variable | Description | Required |
|---|---|---|
PORT |
Port the Express server listens on | ✅ Yes |
MONGODB_URL |
MongoDB connection string | ✅ Yes |
JWT_SECRET |
Secret key for signing JWTs | ✅ Yes |
EMAIL |
Gmail address used to send OTP emails | ✅ Yes |
EMAIL_PASS |
Gmail app password for Nodemailer | ✅ Yes |
CLOUDINARY_CLOUD_NAME |
Cloudinary cloud name | ✅ Yes |
CLOUDINARY_API_KEY |
Cloudinary API key | ✅ Yes |
CLOUDINARY_API_SECRET |
Cloudinary API secret | ✅ Yes |
RAZORPAY_KEY_ID |
Razorpay key ID | ✅ Yes |
RAZORPAY_SECRET |
Razorpay key secret | ✅ Yes |
GEMINI_API_KEY / Google GenAI credentials |
Used by @google/genai for AI search |
✅ Yes |
| Variable | Description | Required |
|---|---|---|
VITE_API_URL |
Base URL of the backend API | ✅ Yes |
VITE_FIREBASE_* |
Firebase project config keys (API key, auth domain, project ID, etc.) for Google Sign-In | ✅ Yes |
- Unauthenticated Access:
isAuthmiddleware checks for a valid JWT cookie on protected routes and rejects requests with a 400/401 if missing or invalid. - Duplicate Reviews: A user cannot submit more than one review for the same course — enforced at the controller level.
- Payment Verification: Enrollment only happens after Razorpay order status is confirmed as
paid; failed verification returns a clear error without enrolling the student. - OTP Expiry: Password reset OTPs expire after 5 minutes and are validated against both value and expiry before allowing a reset.
- File Upload Cleanup: Temporary files are removed from local storage (
fs.unlinkSync) after a successful or failed Cloudinary upload to avoid disk bloat. - Graceful Failures: All controllers wrap logic in try/catch and return descriptive JSON error messages instead of leaking stack traces.
- Single AI Provider: Only Google Gemini is used for search intent matching; no fallback LLM provider.
- Regex-Based Fallback Search: When direct keyword matches fail, search falls back to MongoDB regex queries rather than true semantic/vector search.
- Single Payment Gateway: Only Razorpay is supported (India-focused); no Stripe/PayPal support yet.
- No Real-Time Progress Tracking: Lecture-level completion/progress tracking isn't implemented yet.
- Protected Route Coverage: ~64% of API routes currently sit behind auth middleware; the rest (e.g. public course listing, review fetch) are intentionally public.
- JWT in httpOnly Cookies: Chosen over localStorage tokens to reduce XSS-based token theft risk.
- Gemini for Query Understanding: A lightweight keyword-classification prompt (rather than full RAG) keeps AI search fast (~2s) while still handling free-form queries.
- Cloudinary for Media: Offloads video/image storage and delivery instead of self-hosting media files.
- Mongoose Population: Used extensively (
lectures,reviews,enrolledCourses) to keep API responses rich without extra round trips.
- Lecture progress tracking (mark as complete / resume where left off)
- Pagination for course listings and reviews
- Improve AI search with true vector/semantic search instead of regex fallback
- Expand protected API coverage and add rate limiting
- Multi-provider payments (Stripe/PayPal) for international students
- Course certificates on completion
- Wishlist / saved courses for students
- Admin analytics dashboard (enrollments, revenue, ratings over time)
- Live/cohort-based classes alongside self-paced courses
- Discussion forums / Q&A per lecture
- Mobile app (React Native)
- Multi-language support for course content and UI
- Node.js (v18+)
- npm or yarn
- MongoDB database (local or Atlas)
- Cloudinary account
- Razorpay account
- Google Gemini API key
- Firebase project (for Google Sign-In)
-
Clone the repository:
git clone https://github.com/Vanshgargji/learndesk.git cd learndesk -
Install backend dependencies:
cd backend npm install -
Install frontend dependencies:
cd ../frontend npm install -
Set up environment variables: Create a
.envfile inside/backendwith the variables listed above, and a.envfile inside/frontendfor your Firebase config and API URL. -
Run the backend server:
cd backend npm run dev -
Run the frontend dev server:
cd frontend npm run devOpen http://localhost:5173 (Vite's default port) to see the result.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request

