A web app that generates Khmer-language Grade 12 math exercises and step-by-step solutions using AI, with progress tracking and an admin dashboard.
- Exercise generation by topic (Curve, Limit, Integrals, Differential Equations) and difficulty, powered by Google Gemini
- Step-by-step answers with LaTeX (MathJax) and graph rendering
- AI chatbot — select any text in an exercise to get an explanation
- Accounts — email/password and Google sign-in via Supabase
- Progress tracking — history, attempt counts, day streaks, charts
- User corrections — students submit fixes, admins review them
- Admin dashboard — student analytics, topic stats, sessions map, moderation queue
| Part | Technology |
|---|---|
| Backend | Node.js + Express |
| Database & Auth | Supabase (PostgreSQL) |
| AI | Google Gemini (@google/generative-ai) |
| Frontend | Vanilla JavaScript (ES modules), MathJax, Chart.js, function-plot, Leaflet |
┌─────────────────┐ ┌──────────────────────┐
│ Frontend │ │ Backend (Express) │
│ (static files) │ │ localhost:4000 │
│ │ │ │
│ index.html │ HTTP /api/ai ┌─────────────┐ │ ┌──────────────┐
│ admin.html │──────────────▶│ aiRoutes │──┼─────▶│ Google Gemini│
│ js/ modules │ │ │ gemini.js │ │ └──────────────┘
│ │ HTTP /api/admin └─────────────┘ │
│ │──────────────▶│ adminRoutes │ │ ┌──────────────┐
└────────┬────────┘ │ │ database.js │──┼─────▶│ Supabase │
│ │ └─────────────┘ │ │ (PostgreSQL │
│ Supabase JS SDK (auth + anon key) │ │ + Auth) │
└────────────────────────────────────────┼─────▶│ │
│ └──────────────┘
└──────────────────────┘
How the pieces talk:
- Auth — the frontend talks to Supabase Auth directly (Supabase JS SDK with the public anon key) for sign-up, login, and Google OAuth. Supabase returns a JWT that identifies the user.
- Exercises & AI — the frontend calls the backend at
/api/ai/*. The backend builds prompts, calls Google Gemini, and saves results to Supabase using the service role key (full database access, server-side only). Rate-limited to 20 requests per 15 minutes. - Admin —
admin.htmlcalls/api/admin/*with the user's Supabase JWT. The backend verifies the token and checks the email againstADMIN_EMAILbefore returning analytics or moderation data. - Data — all persistent data (users, exercise history, progression, pending edits, analytics) lives in Supabase/PostgreSQL.
backend/
controllers/ database.js (Supabase), gemini.js (AI)
routes/ aiRoutes.js, adminRoutes.js
server.js Express app entry point
frontend/
index.html student app
admin.html admin dashboard
js/ app modules (auth, exercise, chatbot, progress, ...)
cd backend
npm installCreate a backend/.env file with:
PORT=4000
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
GEMINI_API_KEY=your-gemini-api-key
ADMIN_EMAIL=you@example.com
Start the server:
node server.jsThe API runs at http://localhost:4000.
The frontend is static and expects the backend at the URL in
frontend/js/config.js (API_BASE, default http://localhost:4000). The
Supabase project URL and anon (public) key also live there.
Serve the frontend/ folder with any static server, for example:
cd frontend
python3 -m http.server 5500Then open http://localhost:5500.
| Route | Purpose |
|---|---|
POST /api/ai/... |
Exercise/answer/chatbot generation (rate-limited: 20 requests / 15 min) |
/api/admin/... |
Admin dashboard data — requires a Supabase JWT whose email matches ADMIN_EMAIL |
.envfiles are git-ignored — keep your service role key and Gemini key secret.- The Supabase service role key (backend) bypasses row-level security; never expose it in the frontend. The frontend uses the public anon key.
By Lyhorng Heng