Rohini's JAM Academy LMS
Developer Guide v1.0

Contents
  1. Repository Structure
  2. Running Locally
  3. Environment Variables
  4. Backend Conventions
  5. Frontend Conventions
  6. Activating Integrations
  7. Testing
  8. Common Tasks (How-To)

1. Repository Structure

/app
├── backend/
│   ├── server.py            # FastAPI app, CORS, router registration, startup (indexes + seed)
│   ├── database.py          # motor client — db from MONGO_URL + DB_NAME
│   ├── auth_utils.py        # bcrypt hash/verify, JWT create/decode, get_current_user, require_role
│   ├── seed.py              # idempotent demo data (admin/teacher/student, courses, tests, batches…)
│   ├── notify.py            # send_email (Resend), email_template, notify() in-app + email
│   ├── zoom_service.py      # Server-to-Server OAuth token cache + create_zoom_meeting
│   ├── uploads/             # uploaded files (lesson notes, submissions)
│   ├── requirements.txt     # update ONLY via: pip install X && pip freeze > requirements.txt
│   ├── .env                 # secrets/config (never commit; not pushed to GitHub)
│   ├── tests/               # pytest suites per iteration
│   └── routers/             # one file per domain:
│       auth.py courses.py tests.py live_classes.py assignments.py
│       announcements.py dashboard.py payments.py batches.py files.py
│       certificates.py notifications.py admin.py
└── frontend/
    ├── package.json         # update ONLY via: yarn add <pkg>
    ├── .env                 # REACT_APP_BACKEND_URL, REACT_APP_ACADEMY_NAME
    ├── public/docs/         # these downloadable documents
    └── src/
        ├── App.js           # all routes; Protected wrapper redirects to /auth
        ├── index.css        # fonts (Cabinet Grotesk + IBM Plex Sans), .font-heading, tokens
        ├── lib/api.js       # axios instance (+Bearer interceptor), formatApiError, uploadFile, fileUrl
        ├── lib/config.js    # ACADEMY_NAME branding constant
        ├── context/AuthContext.jsx
        ├── components/      # PortalLayout (role-aware nav), EnrollModal, NotificationsBell,
        │                    # TeacherAnalytics, AdminDashboard, ui/ (shadcn)
        └── pages/           # one default-export component per route

2. Running Locally

# services are supervisor-managed
sudo supervisorctl restart backend    # after .env changes or pip installs
sudo supervisorctl restart frontend   # after yarn add
tail -n 100 /var/log/supervisor/backend.err.log   # backend logs

Backend must stay on 0.0.0.0:8001, frontend on 3000. Every backend route must be prefixed /api (ingress routes /api → 8001, everything else → 3000). The frontend must always call process.env.REACT_APP_BACKEND_URL.

3. Environment Variables

backend/.env

KeyPurpose
MONGO_URL, DB_NAMEMongoDB connection (do not rename)
CORS_ORIGINSComma-separated origins ("*" default)
JWT_SECRETHS256 signing key for tokens
ADMIN_EMAIL / ADMIN_PASSWORDAdmin account seeded at startup
STRIPE_API_KEY, RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRETPayment gateways — empty = demo mode (simulated success)
RESEND_API_KEY, SENDER_EMAILEmail sending — empty key = demo (logs only)
FRONTEND_URLBase URL used in password-reset links
ACADEMY_NAMEBrand name used in emails / API title
ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRETZoom S2S OAuth — empty = auto-create disabled

frontend/.env

REACT_APP_BACKEND_URLAPI base (do not hardcode URLs anywhere)
REACT_APP_ACADEMY_NAMEBrand name shown across the UI

4. Backend Conventions

5. Frontend Conventions

6. Activating Integrations

Stripe / Razorpay (currently demo)

  1. Put real keys in backend/.env → gateway_config() flips demo_mode to false.
  2. Implement gateway verification in routers/payments.py → confirm_payment (currently returns 501 when keys exist): create a gateway order in checkout, verify signature/webhook in confirm, then reuse the existing enrollment + notification block.
  3. Frontend EnrollModal already selects method and shows/hides the demo notice automatically.

Zoom (wired, awaiting credentials)

  1. marketplace.zoom.us → Develop → Build App → Server-to-Server OAuth; add scope meeting:write:admin; activate.
  2. Fill ZOOM_ACCOUNT_ID / ZOOM_CLIENT_ID / ZOOM_CLIENT_SECRET in backend/.env, restart backend.
  3. GET /api/zoom/config now returns configured:true → the "Auto-create Zoom meeting" checkbox enables itself; created classes get join_url as meeting_link.

Resend full delivery

Verify a domain at resend.com/domains, then set SENDER_EMAIL to an address on that domain. Until then delivery is limited to the Resend account owner.

7. Testing

cd /app/backend && python -m pytest tests/ -v      # 59+ API tests across 3 suites

Seeded accounts: admin@jamacademy.com/Admin@123 · teacher@jamacademy.com/Teacher@123 · student@jamacademy.com/Student@123. Historical reports live in /app/test_reports/. Password-reset tests extract the token from backend logs (grep "Password reset link" /var/log/supervisor/backend.err.log).

8. Common Tasks (How-To)

TaskWhere
Rename the academyfrontend/.env REACT_APP_ACADEMY_NAME + backend/.env ACADEMY_NAME, restart both
Change seed/demo databackend/seed.py (idempotent — only inserts when collections are empty)
Add a lesson typeLessonBody in routers/courses.py + icon/handling in pages/CourseDetail.jsx
Change upload limits/typesrouters/files.py → ALLOWED_EXT, MAX_SIZE
Add a notification triggercall notify() from the relevant router (see courses.py enroll for the pattern)
Change JWT lifetimeauth_utils.py create_access_token timedelta
Add admin capabilityrouters/admin.py + pages/Admin*.jsx + ADMIN_NAV in PortalLayout
Email template stylingnotify.py email_template()
Never hardcode URLs/ports/secrets, edit requirements.txt or package.json by hand, or remove the /api prefix — these break the managed deploy pipeline.

© 2026 Rohini's JAM Academy · Developer Guide v1.0