diff --git a/README.md b/README.md index 99e21418..ec3bf7cb 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,373 @@ # Project E-Learning Unesa +A full-stack e-learning platform for health/medical courses (Teraplus), built with React on the frontend and Express + Sequelize on the backend. The system supports rich course content (videos, articles, PDFs), pre/post tests, automatic grading, progress tracking, and certificate generation. + +> Backend description from `backend/package.json`: +> "Backend E-Learning Kesehatan (Node.js, Express, Sequelize, Passport.js, PDFKit)" + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Architecture](#architecture) +3. [Tech Stack](#tech-stack) +4. [Features](#features) +5. [Project Structure](#project-structure) +6. [Getting Started](#getting-started) +7. [Environment Variables](#environment-variables) +8. [Database & Migrations](#database--migrations) +9. [Default Credentials](#default-credentials) +10. [API Surface](#api-surface) +11. [Frontend Routes](#frontend-routes) +12. [Certificate Generation](#certificate-generation) +13. [Testing](#testing) +14. [Deployment](#deployment) +15. [Code Review Notes](#code-review-notes) +16. [Contributing](#contributing) +17. [License](#license) + +--- + ## Overview -Project E-Learning Unesa is a comprehensive e-learning platform featuring a modern React frontend and an Express/Node.js backend. This project supports multiple authentication methods and rich course content management, while also planning for administrative functionality, user progress tracking, and assessment-based certification. -## Features (Current & Planned) +The project is a monorepo with two main applications: + +- **`backend/`** – Node.js + Express REST API that exposes course, module, question, enrollment, authentication and certificate endpoints. Persistence is handled by Sequelize (SQLite by default, with MySQL/Postgres drivers available). +- **`frontend/`** – React 18 + Create React App SPA styled with TailwindCSS. Routing is handled by `react-router-dom` v6 with role-based guards (`PrivateRoute`, `AdminRoute`) and a profile completion guard. + +Auxiliary directories: + +- **`amplify/`** – AWS Amplify configuration for hosting/CI. +- **`migrations/`** – Top-level Sequelize migrations (the canonical set lives in `backend/migrations/`). +- **`data/`**, **`dokumen/`** – Seed data and reference documents (including a sample certificate PDF). + +## Architecture + +``` + ┌───────────────────────────────────────┐ + │ Browser (SPA) │ + │ React 18 + Tailwind + React-Router │ + │ AuthContext / CourseProgressContext │ + └──────────────────┬────────────────────┘ + │ HTTPS (axios, JWT) + ▼ + ┌───────────────────────────────────────┐ + │ Express API (backend/app.js) │ + │ CORS · JSON · Static · Error MW │ + │ Routes: │ + │ /api/auth (local + Google OAuth)│ + │ /api/users │ + │ /api/courses │ + │ /api/admin │ + └──────────────────┬────────────────────┘ + │ Sequelize ORM + ▼ + ┌───────────────────────────────────────┐ + │ SQLite (default) · MySQL · Postgres │ + │ Users · Courses · Modules · Questions│ + │ Enrollments · UserProgress · Reviews │ + └───────────────────────────────────────┘ +``` + +Authentication is JWT-based (with sessions wired in for the Google OAuth flow via Passport). Tokens land in the URL after OAuth, are read in `frontend/src/App.jsx` `useEffect`, stored in `localStorage`, and then stripped from the URL. + +## Tech Stack + +### Backend (`backend/package.json`) + +| Concern | Library | +| --------------- | ---------------------------------------------------------- | +| Runtime | Node.js | +| HTTP | `express` 4 | +| ORM | `sequelize` 6 with `sqlite3`, `mysql2`, `pg` | +| Auth | `passport`, `passport-google-oauth20`, `jsonwebtoken`, `bcryptjs`, `express-session` | +| Files / Storage | `multer`, `@aws-sdk/client-s3` | +| PDF | `pdfkit` | +| Email | `nodemailer` | +| Excel I/O | `xlsx` | +| Config | `dotenv` | +| Dev | `nodemon`, `sequelize-cli` | + +### Frontend (`frontend/package.json`) + +| Concern | Library | +| --------------- | ---------------------------------------------------------- | +| UI | `react` 18, `react-dom` | +| Routing | `react-router-dom` 6 | +| Styling | `tailwindcss`, `@heroicons/react`, `@tailwindcss/aspect-ratio` | +| HTTP | `axios` | +| Auth | `jwt-decode` | +| PDF viewing | `@react-pdf-viewer/core`, `react-pdf`, `pdfjs-dist` | +| Rich text | `react-quill` | +| Video | `react-youtube` | +| Notifications | `react-toastify` | +| Build | `react-scripts` 5 | + +## Features ### Authentication & User Management -- [x] User registration (local) -- [x] User login (local) -- [x] Google OAuth login -- [x] Display user email on homepage after login -- [x] Admin role and dashboard -- [x] User profile page -- [x] Password reset functionality -- [x] User profile picture upload -- [x] User profile fields for affiliation and phone number -- [x] Multi Create User +- Local registration and login (email + password, hashed with bcrypt). +- Google OAuth login via Passport (`/api/auth/google`). +- JWT-based session handling on the frontend. +- Password reset flow (request reset + reset via signed token). +- User profile with avatar upload (Multer), affiliation, and phone number. +- "Complete Profile" guard that forces users to fill `affiliasi` and `noHp` before accessing the rest of the app. +- Admin role with a dedicated dashboard. +- Bulk user creation (Multi-Create) for admins. ### Course & Content Management (Admin) -- [x] CRUD operations for courses -- [x] CRUD operations for modules within courses -- [x] CRUD operations for content (videos, articles, PDFs) within modules -- [x] Ability to set course prerequisites -- [x] Support for PDF as a module content type -- [x] SEO-friendly slugs for courses -- [x] "Initial content" field for modules -- [x] Configuration for course post-tests (e.g., linking questions) -- [x] "Explanation" field for quiz/assessment questions -- [x] Tracking of correct option for quiz/assessment questions -- [x] Ability to manage course categories/tags - -### Course & Content Consumption (User) -- [x] Browse and filter courses -- [x] Enroll in courses -- [x] View course content (videos, articles, PDFs) -- [x] Track course progress -- [x] Mark modules/content as complete -- [x] Search functionality for courses and content +- Full CRUD for courses, modules, content blocks, and questions. +- Module content types: video, article (HTML/Markdown), and PDF. +- SEO-friendly slugs for courses. +- Course prerequisites. +- Post-test configuration (linked questions, passing score). +- Question metadata: explanation text and `correctOptionId`. +- Category / tag management. +- Settings page for app-wide configuration. + +### Course Consumption (User) +- Browse and filter courses. +- Enroll in courses (tracked in `enrollments`). +- Linear navigation through modules with `userProgress` persistence. +- Pre-test → modules → post-test flow. +- Mark modules complete and resume where you left off. +- Search across courses and content. ### Assessment & Certification -- [x] Quizzes/assessments within modules -- [x] Automatic grading for quizzes -- [x] Certificate download upon course completion (currently serves a dummy PDF: `dokumen/sertif.pdf`) -- [x] View and download certificate page - -## How to Use - -### Installation & Setup -1. **Clone the Repository:** - ```bash - git clone git@github.com:zXmill/project-web-elearning.git - ``` -2. **Navigate to the Project Root:** - ```bash - cd project-web-elearning - ``` - (Or the name you cloned the repository as) -3. **Install Dependencies:** - - From the `backend` folder: - ```bash - cd backend - npm install - ``` - - From the `frontend` folder: - ```bash - cd ../frontend - npm install - ``` - -### Running the Application -To run both the backend and frontend concurrently, navigate to the project root directory and execute: +- Multiple-choice quizzes with automatic grading. +- Result pages for pre-test and post-test. +- Certificate page (`/course/:identifier/certificate`). +- PDF certificate download (currently serves the sample at `dokumen/sertif.pdf`; dynamic name-on-template generation is planned). +- Practical-test fields and certificate-approval flow on enrollments. + +## Project Structure + +``` +project-web-elearning/ +├── backend/ +│ ├── app.js # Express bootstrap, CORS, route mounting +│ ├── server.js # Entry point (npm start / nodemon) +│ ├── controllers/ # Route handlers (auth, admin, courses, content, enrollment, certificate, questions) +│ ├── models/ # Sequelize models (User, Course, Module, Question, Enrollment, UserProgress, Review, Setting) +│ ├── migrations/ # Sequelize CLI migrations +│ ├── routes/ # Route definitions (auth, admin, courses, certificate) +│ ├── middleware/ # Auth + role guards, error handling +│ ├── scripts/ # Seed scripts (e.g. seedCourses.js) +│ ├── utils/ # Helpers (mailer, PDF, S3) +│ ├── public/ # Static assets served by Express +│ ├── uploads/ # Runtime upload destination +│ └── tests/ # Backend tests +├── frontend/ +│ ├── src/ +│ │ ├── App.jsx # Top-level routing + guards +│ │ ├── contexts/ # AuthContext, CourseProgressContext +│ │ ├── components/ # Admin, Auth, Common, Course, Layout +│ │ ├── pages/ # Home, Login, Register, AdminDashboard, Course*, *Test*, Certificate, etc. +│ │ ├── services/ # Axios clients / API wrappers +│ │ ├── hooks/ # Custom hooks +│ │ └── styles/ # Tailwind + global CSS +│ └── tailwind.config.js +├── amplify/ # AWS Amplify hosting config +├── amplify.yml # Amplify build spec +├── data/ # Seed/reference data +├── dokumen/ # Reference docs + sample certificate (sertif.pdf) +├── migrations/ # Top-level migrations (mirrored in backend) +└── package.json # Workspace runner (`npm run dev` -> both apps) +``` + +## Getting Started + +### Prerequisites + +- Node.js 18+ and npm +- SQLite 3 (default) — or MySQL / Postgres if you point `DATABASE_URL` at one +- Optional: a Google OAuth client if you want the social login flow + +### Installation + ```bash -npm run dev +# 1. Clone +git clone https://github.com/zXmill/project-web-elearning.git +cd project-web-elearning + +# 2. Install backend deps +cd backend +npm install + +# 3. Install frontend deps +cd ../frontend +npm install +``` + +### Running locally + +From the **project root**, the top-level `package.json` exposes a convenience script that boots both services in parallel: + +```bash +npm install # installs npm-run-all +npm run dev # runs `npm run dev` in backend AND `npm start` in frontend +``` + +You can also run them independently: + +```bash +# Terminal 1 +cd backend && npm run dev # nodemon server.js + +# Terminal 2 +cd frontend && npm start # CRA dev server on http://localhost:3000 ``` -This command will start the Express server for the backend and the React development server for the frontend. -The frontend will typically be available at [http://localhost:3000](http://localhost:3000) in your browser. + +The frontend is proxied to `http://localhost:3001` (see `frontend/package.json` `"proxy"`). Make sure the backend listens on the same port (it defaults to `5000` — adjust `PORT` if needed). ## Environment Variables -Create a `.env` file in the `backend` folder based on `.env.example`: +Create `backend/.env` based on `backend/.env.example`. Typical variables: ``` -SESSION_SECRET=your_session_secret_here +PORT=5000 +SESSION_SECRET=your_session_secret +JWT_SECRET=your_jwt_secret DATABASE_URL=sqlite://./data/elearning.sqlite FRONTEND_URL=http://localhost:3000 + +# Google OAuth (optional) +GOOGLE_CLIENT_ID=... +GOOGLE_CLIENT_SECRET=... +GOOGLE_CALLBACK_URL=http://localhost:5000/api/auth/google/callback + +# AWS S3 (optional, for file uploads) +AWS_REGION=... +AWS_ACCESS_KEY_ID=... +AWS_SECRET_ACCESS_KEY=... +S3_BUCKET=... + +# Mailer (password reset) +SMTP_HOST=... +SMTP_PORT=587 +SMTP_USER=... +SMTP_PASS=... ``` -## Database Setup - -1. **Run Migrations:** - ```powershell - cd backend - npx sequelize-cli db:migrate - ``` -2. **Seed the Database:** - ```powershell - node scripts/seedCourses.js - # You might also need to run: node scripts/seedRemainingModulesAndQuestions.js - ``` - The seeding process creates a default user. Credentials can be found in `dokumen/credentials.txt`: - - **Email:** `1@teraplus.com` - - **Password:** `AdminPass456` - This user may have admin privileges. - -## Default User Credentials -After seeding the database, a default user account is available for testing: +CORS is configured in `backend/app.js` to accept `http://localhost:3000`, the Amplify URL, and whatever you set in `FRONTEND_URL`. + +> **Never commit `.env`** — it's already in `.gitignore`. + +## Database & Migrations + +```bash +cd backend +npx sequelize-cli db:migrate +node scripts/seedCourses.js +# Optionally: +node scripts/seedRemainingModulesAndQuestions.js +``` + +The migration history (in `backend/migrations/`) tells the story of the data model evolution: profile picture, affiliation/phone, password reset, enrollments, user progress, course extras, module ordering, prerequisites, question explanations, PDF module type, settings, last-login timestamps, practical tests, certificate approval, WA group links, and reviews. + +## Default Credentials + +After seeding (see `dokumen/credentials.txt`): + - **Email:** `1@teraplus.com` - **Password:** `AdminPass456` -This account may have administrative privileges depending on the seed data. - -## For Testing Purpose - -- **Backend:** - - Add your Jest or Mocha tests in `backend/tests/`. - - Example run: - ```powershell - cd backend - npx jest - ``` -- **Frontend:** - - Add your React Testing Library or Jest tests in `frontend/src/__tests__/`. - - Example run: - ```powershell - cd frontend - npm test - ``` - -## Certificate Download - -- Upon completing a course and its post-test, users can download a certificate from the post-test results page. -- Currently, the system serves a **dummy certificate** located at `dokumen/sertif.pdf` (downloaded as `sertifikat_kompetensi.pdf`). -- **Future Enhancements:** - - Dynamic certificate generation using a template (e.g., `dokumen/sertif.png`). - - A "Confirm Name" modal will allow users to verify their name before the certificate is generated with their actual name. - - A dedicated page to view certificates online (planned for frontend route `/course/:courseId/certificate`). - -## Additional Notes -- **.env Files:** - Use the provided `.env.example` in the backend as a guide. Ensure you do NOT commit your actual `.env` file by listing it in `.gitignore`. -- **Authentication:** - Configure your Git identity and use SSH or a personal access token for GitHub. -- **Contribution:** - Contributions are welcome! Please fork the repository, create your feature branch, and open a pull request. + +This account is created with admin privileges by the seed scripts. + +## API Surface + +All routes are mounted under `/api`: + +| Prefix | Source | Purpose | +| --------------- | ------------------------------- | ---------------------------------------------------- | +| `/api/auth` | `routes/auth.js` | Register, login, Google OAuth, password reset | +| `/api/users` | `routes/userRoutes.js` | Profile, avatar upload, complete-profile | +| `/api/courses` | `routes/courseRoutes.js` | Browse, enroll, progress, pre/post test submissions | +| `/api/admin` | `routes/admin.js` | Admin CRUD for courses, modules, questions, users, enrollments, settings | +| `/api/health` | `app.js` | Liveness probe | + +Each domain has a dedicated controller: `authController.js`, `adminController.js`, `certificateController.js`, `contentController.js`, `courseController.js`, `courseAdminController.js`, `enrollmentController.js`, `moduleAdminController.js`, `questionAdminController.js`. + +## Frontend Routes + +Defined in `frontend/src/App.jsx`. Highlights: + +| Path | Guard | Component | +| ------------------------------------------------- | ------------------------------ | ---------------------------------- | +| `/login`, `/register` | public | `Login`, `RegisterPage` | +| `/request-password-reset`, `/reset-password/:token` | public | password reset pages | +| `/auth-redirect` | public | OAuth landing page | +| `/complete-profile` | `PrivateRoute` | `CompleteProfilePage` | +| `/` | `PrivateRoute + ProfileGuard` | `Home` | +| `/profile` | `PrivateRoute + ProfileGuard` | `UserProfilePage` | +| `/course/:identifier` | `PrivateRoute + ProfileGuard` | `CourseDetailPage` | +| `/course/:identifier/moduleslist` | … | `ModuleListPage` | +| `/course/:identifier/pretest` | … | `PreTestPage` | +| `/course/:identifier/pretest-result` | … | `PreTestResultPage` | +| `/course/:identifier/content/:moduleOrder` | … | `CourseContentPage` | +| `/course/:identifier/posttest` | … | `PostTestPage` | +| `/course/:identifier/posttest-result` | … | `PostTestResultPage` | +| `/course/:identifier/certificate` | … | `CertificatePage` | +| `/admin/*` | `AdminRoute` + `AdminLayout` | Dashboard, users, courses, content, settings, enrollment management | +| `*` | — | 404 page | + +`ProfileCompletionGuard` redirects to `/complete-profile` if `user.affiliasi` or `user.noHp` is missing. + +## Certificate Generation + +- After completing the post-test, users can download a certificate from the result page. +- The backend currently serves a **static dummy PDF** at `dokumen/sertif.pdf`, downloaded as `sertifikat_kompetensi.pdf`. +- Planned: dynamic generation via `pdfkit` using `dokumen/sertif.png` as a template, with a "Confirm Name" modal so the printed name matches the user's input. + +## Testing + +```bash +# Backend +cd backend +npx jest # tests live in backend/tests/ + +# Frontend +cd frontend +npm test # React Testing Library / Jest +``` + +Both projects ship without locked-down CI test commands — add your own before deploying to production. + +## Deployment + +The repo ships with AWS Amplify configuration: + +- `amplify.yml` — build spec for the frontend. +- `amplify/` — Amplify project metadata. +- `amplify/iamPolicyS3Access.json` and the `s3BucketPolicyUpdate*.json` files — reference IAM/S3 policies for uploads. + +The current production frontend is configured for `https://main.d350srbff0febm.amplifyapp.com` (allowed CORS origin in `backend/app.js`). + +The backend includes a `Dockerfile` for containerized deployment of the API. + +## Code Review Notes + +A few observations from a quick walkthrough of the codebase that future contributors may want to address: + +1. **Verbose logging in `backend/app.js`** — there are `console.log` statements on every middleware step. Useful for debugging the boot sequence but very noisy in production. Consider switching to a structured logger (e.g., `pino` or `winston`) and gating verbose logs behind `NODE_ENV !== 'production'`. +2. **CORS configuration** — `allowedOrigins` is a hard-coded array plus `process.env.FRONTEND_URL`. Multi-environment deployments would be easier if this accepted a comma-separated env var (e.g., `CORS_ORIGINS`). +3. **Models directory** — the controllers reference several models (`User`, `Module`, `Question`, `Enrollment`, `UserProgress`, `Review`, `Setting`); make sure they're all checked in alongside their migrations. The `models/index.js` should wire them up with Sequelize associations. +4. **Self-referential dependencies** — `backend/package.json` and `frontend/package.json` both list themselves (`"elearning-backend": "file:"`, `"frontend": "file:"`). These look like accidental leftovers from local linking and can be removed. +5. **Migration naming** — two files in `backend/migrations/` start with `MIGRATION_TIMESTAMP_` instead of a real ISO timestamp. Rename them to keep `sequelize-cli` ordering deterministic. +6. **Static certificate** — `certificateController.js` serves a fixed PDF. Once dynamic generation is added, gate it behind the user's enrollment + post-test pass state. +7. **Auth token storage** — the OAuth token-in-URL pattern in `App.jsx` works, but `localStorage` is vulnerable to XSS. If you stay with JWTs, consider httpOnly cookies for the access token and limit `localStorage` to non-sensitive metadata. +8. **`.env.example` parity** — the README references `.env.example`; keep it in sync with the variables the backend actually consumes (`SESSION_SECRET`, `JWT_SECRET`, OAuth keys, SMTP, S3, `DATABASE_URL`). +9. **Frontend bundle size** — `react-pdf`, `pdfjs-dist`, `react-quill`, and `react-youtube` are heavy. Lazy-load them per-route with `React.lazy` so the dashboard doesn't pay for PDF/Quill chunks on first paint. +10. **Test coverage** — both `tests/` and `__tests__/` directories exist but coverage is light. Auth, enrollment, and grading paths are the highest-value places to add tests first. + +## Contributing + +1. Fork the repo and create a feature branch: `git checkout -b feature/your-feature`. +2. Run lint/tests locally before pushing. +3. Open a pull request against `main` with a clear description and screenshots for any UI changes. +4. Do **not** commit `.env`, build artifacts, or files under `backend/uploads/`. ## License + +This project is currently distributed without an explicit `LICENSE` file. Treat it as **All Rights Reserved** until a license is added. For internal / academic use only.