An Edmingle-style Learning Management System for IIT-JAM coaching with three roles — Student, Teacher, Admin — built to scale into a multi-academy SaaS. Features: course catalog with video/PDF curriculum & progress tracking, batches/cohorts, payment checkout (Stripe/Razorpay placeholders in demo mode), live class scheduling (batch-scoped, Zoom-ready), timed auto-graded mock tests with review & leaderboards, assignments with file submissions and grading, announcements, in-app + email (Resend) notifications, completion certificates, teacher analytics and an admin panel.
Browser (React SPA, port 3000)
│ HTTPS — all API calls prefixed /api → routed by K8s ingress to :8001
▼
FastAPI backend (uvicorn, 0.0.0.0:8001)
├── routers/* (feature-modular API)
├── auth_utils (bcrypt + JWT Bearer/cookie, role guards)
├── notify.py (in-app notifications + Resend email, fire-and-forget)
├── zoom_service.py (Server-to-Server OAuth, token cache)
└── uploads/ (file storage on disk, served via /api/files/{id})
▼
MongoDB (motor async driver; DB name from env DB_NAME)
| Layer | Technology | Notes |
|---|---|---|
| Frontend | React 19 (CRA + craco), Tailwind CSS, shadcn/ui, lucide-react, recharts, axios, sonner, dayjs | SPA with react-router; token in localStorage jam_token |
| Backend | FastAPI + uvicorn (hot reload), Pydantic v2, PyJWT, bcrypt, httpx, resend | All routes under /api; supervisor-managed |
| Database | MongoDB via motor | UUID string _ids (JSON-safe, no ObjectId leakage) |
| Token | Value |
|---|---|
| Style | Swiss / International Typographic — high-contrast light theme, sharp corners (no border radius), 1px zinc borders, generous whitespace |
| Primary | Klein Blue #1D4ED8 (actions, links, active nav) |
| Accent | Signal Red #E63946 / red-600 (live badges, alerts, destructive) |
| Neutrals | zinc-50 … zinc-950 (backgrounds, borders, text) |
| Headings font | Cabinet Grotesk (fontshare) — class font-heading |
| Body font | IBM Plex Sans (Google Fonts) |
| Branding | Academy name configurable: REACT_APP_ACADEMY_NAME (frontend) / ACADEMY_NAME (backend) → src/lib/config.js & notify.py |
| Collection | Key fields |
|---|---|
| users | _id(uuid), name, email(unique idx), password_hash(bcrypt), role(student|teacher|admin), created_at |
| courses | _id, title, subject, description, thumbnail, price, duration, published, teacher_id, teacher_name, sections:[{id,title,lessons:[{id,title,type(video|pdf),url,duration}]}] |
| enrollments | _id, course_id, student_id, batch_id?, completed_lessons:[lessonId], payment_id?, enrolled_at — unique idx (course_id,student_id) |
| batches | _id, course_id, teacher_id, name, start_date, schedule, capacity |
| tests | _id, title, subject, duration_min, published, course_id?, course_name?, total_marks, teacher_id, questions:[{id,text,options[4],correct_index,marks}] |
| test_attempts | _id, test_id, student_id, answers{qid:optionIdx}, score, total, correct_count, question_count, submitted_at — unique idx (test_id,student_id) |
| assignments | _id, title, subject, description, due_date, max_marks, course_id?, course_name?, teacher_id |
| submissions | _id, assignment_id, student_id, student_name, content, link, file_url, file_name, grade, feedback, submitted_at |
| live_classes | _id, title, subject, description, start_time(ISO), duration_min, meeting_link, course_id?, batch_id?, course_name?, batch_name?, zoom_meeting_id?, teacher_id |
| announcements | _id, title, body, teacher_id, teacher_name, created_at |
| payments | _id, student_id, course_id, course_title, batch_id?, amount, currency, method(stripe|razorpay), gateway(demo|…), status(pending|paid|failed), created_at, paid_at |
| notifications | _id, user_id, title, body, link, read, created_at — idx (user_id, created_at desc) |
| certificates | _id, cert_no(JAM-XXXXXXXX), student_id, student_name, course_id, course_title, subject, teacher_name, issued_at |
| password_reset_tokens | _id(token), user_id, expires_at(TTL idx), used |
| files | _id(uuid), filename, ext, content_type, size, uploader_id |
| Domain | Endpoints |
|---|---|
| Auth | POST /auth/register · /auth/login · /auth/logout · GET /auth/me · POST /auth/forgot-password · /auth/reset-password |
| Courses | GET/POST /courses · GET/PUT/DELETE /courses/{id} · POST …/sections · …/sections/{sid}/lessons · …/enroll · …/lessons/{lid}/complete · GET /courses/{id}/students · /teacher/courses · /student/enrollments |
| Batches | GET/POST /courses/{id}/batches · DELETE /batches/{id} · GET /batches/{id}/students |
| Tests | GET/POST /tests · GET/PUT/DELETE /tests/{id} · POST /tests/{id}/attempt · GET …/attempts · …/review · …/leaderboard · /student/attempts |
| Assignments | GET/POST /assignments · DELETE /assignments/{id} · POST …/submit · GET …/submissions · PUT /submissions/{id}/grade |
| Live classes | GET/POST /live-classes · DELETE /live-classes/{id} · GET /zoom/config |
| Payments | GET /payments/config · POST /payments/checkout · /payments/{id}/confirm · GET /student/payments |
| Files | POST /files/upload (multipart, 25MB, ext whitelist) · GET /files/{id} |
| Notifications | GET /notifications · POST /notifications/read-all |
| Certificates | GET /courses/{id}/certificate · /student/certificates |
| Dashboards | GET /dashboard/student · /dashboard/teacher · /dashboard/teacher/analytics |
| Admin | GET /admin/stats · /admin/users?q=&role= · PUT /admin/users/{id}/role · DELETE /admin/users/{id} · GET /admin/payments |
Register/login → bcrypt verify → JWT (HS256, 7-day exp, claims sub/email/role) returned in body and httpOnly cookie. Frontend stores token in localStorage and sends Authorization: Bearer via axios interceptor. get_current_user checks header then cookie; require_role(*roles) guards endpoints. Password reset: single-use token (secrets.token_urlsafe) with 1h TTL, emailed as link.
EnrollModal → GET payments/config (demo when gateway keys empty) → POST /payments/checkout (creates pending payment, validates batch capacity) → POST /payments/{id}/confirm → marks paid + creates enrollment + notifies student (email) and teacher. Free courses enroll directly. When real keys are added, confirm returns 501 until gateway verification is implemented.
Marking a lesson complete does $addToSet completed_lessons; progress = completed/total lessons. At 100%, GET /courses/{id}/certificate issues (or returns) a certificate with unique cert_no.
Teacher publishes MCQs (optionally course-linked). Students see tests that are global or belong to enrolled courses; correct_index is stripped from student payloads. Attempt is auto-graded server-side, one per student (unique index). Review returns answers + correct indexes; leaderboard ranks by score desc, time asc, computes percentile.
student_class_query() builds $or: global classes, OR classes of enrolled courses where batch is null or matches the student's enrollment batch. Reused by live-class list and student dashboard.
notify(user_ids, title, body, link, email_subject?) inserts db.notifications docs and fire-and-forgets Resend emails (branded HTML template). Bell component polls every 30s.
| Service | Status | Config (backend/.env) |
|---|---|---|
| Resend (email) | ACTIVE — testing mode (delivers only to account owner until domain verified at resend.com/domains) | RESEND_API_KEY, SENDER_EMAIL |
| Stripe / Razorpay | PLACEHOLDER — demo checkout simulates success | STRIPE_API_KEY, RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET |
| Zoom (Server-to-Server OAuth) | WIRED — awaiting credentials; auto-create meeting checkbox disabled until set | ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET |
© 2026 Rohini's JAM Academy · Design & Architecture v1.0