|
1 | | -# Mini LMS (Learning Management System) Backend |
2 | | - |
3 | | -[](https://github.com/PiyushInt/learning-management-system-fullstack/actions/workflows/ci.yml) |
4 | | - |
5 | | -An advanced, resilient, and secure RESTful API for a Learning Management System designed to handle core educational workflows: course creation, student enrollment, assignment distribution, and grade-less submission tracking. |
6 | | - |
7 | | -## Overview |
8 | | -This backend powers a multi-tenant learning environment where Teachers manage courses and assignments, and Students enroll in those courses and submit their work. It heavily emphasizes security boundaries, rate-limiting resilience, strict data consistency logic, and robust automated testing utilizing modern Node.js backend practices. |
9 | | - |
10 | | -## Tech Stack |
11 | | -* **Language & Runtime:** Node.js (v20+), ES Modules |
12 | | -* **Framework:** Express.js (v5) |
13 | | -* **Database & ORM:** PostgreSQL (v15), Prisma ORM |
14 | | -* **Security & Hardening:** Helmet, express-rate-limit, CORS, bcrypt, Joi |
15 | | -* **Observability:** Winston (Structured logging, Recursive redaction) |
16 | | -* **Testing:** Jest, Supertest |
17 | | -* **CI/CD:** GitHub Actions (Clean-clone DB migration tests, Linting, Audit) |
18 | | - |
19 | | -## Architecture |
20 | | - |
21 | | -```mermaid |
22 | | -graph TD |
23 | | - Client[Client App / Postman] --> |HTTP / JSON| API[Express API Gateway] |
24 | | - |
25 | | - subgraph Express Backend |
26 | | - API --> RateLimit[Rate Limiter] |
27 | | - RateLimit --> Auth[Auth Middleware] |
28 | | - Auth --> Validation[Joi Boundary Validation] |
29 | | - Validation --> Controllers[Controllers] |
30 | | - Controllers --> CoreAuth[Core Authorization] |
31 | | - CoreAuth --> Services[Domain Services] |
32 | | - end |
33 | | - |
34 | | - Services --> |Prisma Client| DB[(PostgreSQL)] |
35 | | - |
36 | | - subgraph Observability |
37 | | - Controllers -.-> Logger[Winston Logger] |
38 | | - Services -.-> Logger |
39 | | - Logger -.-> |Redacts PII/Tokens| LogOutput[Stdout/JSON] |
40 | | - end |
41 | | -``` |
| 1 | +# Learning Management System |
42 | 2 |
|
43 | | -## Project Structure |
44 | | -```text |
45 | | -backend/ |
46 | | -├── src/ |
47 | | -│ ├── app.js # Express app configuration & hardening |
48 | | -│ ├── server.js # Entry point & Graceful shutdown |
49 | | -│ ├── config/ # Centralized environment variable parsing |
50 | | -│ ├── controllers/ # HTTP request/response handlers |
51 | | -│ ├── core/ # Core domain logic (authorization.js) |
52 | | -│ ├── middlewares/ # Joi validation, error handling, logging, JWT auth |
53 | | -│ ├── routes/ # Route definitions |
54 | | -│ ├── services/ # Business logic and Prisma DB interactions |
55 | | -│ ├── utils/ # Helpers (e.g., JWT signing/verification) |
56 | | -│ └── validations/ # Joi schema definitions |
57 | | -├── tests/ # Jest integration test suite & fixtures |
58 | | -├── prisma/ # Prisma schema and migrations |
59 | | -├── .env.example # Template for environment variables |
60 | | -├── package.json # NPM dependencies & scripts |
61 | | -└── eslint.config.js # Linter configuration |
62 | | -.github/workflows/ # CI/CD pipelines |
63 | | -``` |
| 3 | +A React application backed by a C# ASP.NET Core API and PostgreSQL. Teachers create courses and assignments; students enroll and submit work. |
64 | 4 |
|
65 | | -## Getting Started |
| 5 | +## Stack |
66 | 6 |
|
67 | | -### Prerequisites |
68 | | -* Node.js (v20+) |
69 | | -* Docker & Docker Compose (for the local test database) |
70 | | -* PostgreSQL (if running locally without Docker) |
| 7 | +- Frontend: React, Vite, Redux Toolkit, Axios |
| 8 | +- Backend: .NET 10, ASP.NET Core controllers, Entity Framework Core, Npgsql |
| 9 | +- Database: PostgreSQL 15+ (the existing Prisma table structure is preserved) |
| 10 | +- Authentication: bcrypt passwords and HS256 JWTs with teacher/student authorization |
| 11 | +- Tests: xUnit integration tests against isolated PostgreSQL databases |
71 | 12 |
|
72 | | -### Environment Setup |
73 | | -Create a `.env` file in the `backend/` directory: |
| 13 | +## Structure |
74 | 14 |
|
75 | | -```env |
76 | | -PORT=3000 |
77 | | -DATABASE_URL="postgresql://user:password@localhost:5432/dbname" |
78 | | -JWT_SECRET="replace_with_a_secure_random_string" |
79 | | -JWT_EXPIRES_IN="1d" |
| 15 | +```text |
| 16 | +frontend/ Existing React application |
| 17 | +backend-dotnet/Lms.Api/ C# API, entities, services, EF migrations |
| 18 | +backend-dotnet/Lms.Api.Tests/ API and database adoption tests |
| 19 | +backend/ Legacy Express API and original Prisma migrations |
| 20 | +start_app.sh Starts React and .NET; supports Express rollback |
80 | 21 | ``` |
81 | | -*(Note: A test database configuration is automatically managed via the `run-tests.sh` script when running `npm test`.)* |
82 | 22 |
|
83 | | -### Installation & Execution |
| 23 | +## Run with a fresh local database |
| 24 | + |
| 25 | +Install .NET 10 SDK, Node.js 22.12+ and Docker Compose (or use an existing PostgreSQL server). |
| 26 | + |
84 | 27 | ```bash |
85 | | -cd backend/ |
| 28 | +cp backend-dotnet/.env.example backend-dotnet/.env |
| 29 | +# Edit backend-dotnet/.env and replace the JWT secret. |
| 30 | +docker compose -f backend-dotnet/compose.yaml up -d --wait db |
| 31 | +bash backend-dotnet/run.sh --migrate |
| 32 | +bash start_app.sh |
| 33 | +``` |
86 | 34 |
|
87 | | -# Install dependencies using ci for exact lockfile match |
88 | | -npm ci |
| 35 | +React uses port 5173 and proxies `/api` to the .NET API at port 5050. Schema migrations are explicit commands; starting the API never recreates tables or applies migrations. |
89 | 36 |
|
90 | | -# Generate the Prisma Client |
91 | | -npm run prisma:generate |
| 37 | +**Existing LMS database:** follow the backup, inspection, and adoption procedure in [the migration guide](backend-dotnet/README.md#existing-prisma-database). Do not initialize or reset it as a fresh database. |
92 | 38 |
|
93 | | -# Apply database migrations |
94 | | -npm run prisma:migrate |
| 39 | +## Test |
95 | 40 |
|
96 | | -# Start the server (Development) |
97 | | -npm run dev |
| 41 | +```bash |
| 42 | +bash backend-dotnet/run-tests.sh |
| 43 | +cd frontend |
| 44 | +npm ci |
| 45 | +npm run build |
98 | 46 | ``` |
99 | 47 |
|
100 | | -## API Reference |
101 | | - |
102 | | -### Authentication |
103 | | -| Method | Endpoint | Description | Auth Required | |
104 | | -| :--- | :--- | :--- | :--- | |
105 | | -| `POST` | `/auth/register` | Register a new user (Teacher/Student) | No | |
106 | | -| `POST` | `/auth/login` | Authenticate and receive a JWT | No | |
107 | | - |
108 | | -### Courses & Enrollments |
109 | | -| Method | Endpoint | Description | Auth Required (Role) | |
110 | | -| :--- | :--- | :--- | :--- | |
111 | | -| `GET` | `/courses` | List all available courses | No | |
112 | | -| `POST` | `/courses` | Create a new course | Yes (TEACHER) | |
113 | | -| `GET` | `/courses/enrolled` | List courses the student is enrolled in | Yes (STUDENT) | |
114 | | -| `POST` | `/courses/:id/enroll` | Enroll the current student into a course | Yes (STUDENT) | |
115 | | -| `GET` | `/courses/:id/assignments` | List assignments for a course | Yes (Enrolled STUDENT / Owning TEACHER) | |
116 | | -| `POST` | `/courses/:id/assignments` | Create a new assignment for a course | Yes (Owning TEACHER) | |
117 | | - |
118 | | -### Assignments & Submissions |
119 | | -| Method | Endpoint | Description | Auth Required (Role) | |
120 | | -| :--- | :--- | :--- | :--- | |
121 | | -| `POST` | `/assignments/:id/submit` | Submit work for an assignment | Yes (Enrolled STUDENT) | |
122 | | -| `GET` | `/assignments/:id/submissions` | List all submissions for an assignment | Yes (Owning TEACHER) | |
123 | | - |
124 | | -## Security |
125 | | - |
126 | | -This system implements a strict defense-in-depth security architecture with a clear separation of concerns: |
127 | | - |
128 | | -1. **Role Checks vs. Ownership Checks**: |
129 | | - - **Role Checks** are performed early at the HTTP boundary via the JWT middleware (e.g., verifying a user is a `TEACHER` before allowing access to course creation). |
130 | | - - **Ownership & Domain Guards** live deeper in the stack within `src/core/authorization.js`. This module strictly handles data-level authorization, ensuring that a standard Prisma query always verifies a Teacher actually owns the course they are modifying, or a Student is genuinely enrolled in the course they are interacting with. |
131 | | - |
132 | | -2. **Route Coverage Matrix**: |
133 | | - |
134 | | -| Route | Role Check (Middleware) | Ownership / Domain Check (`authorization.js`) | |
135 | | -| :--- | :--- | :--- | |
136 | | -| `/auth/register` | None | None | |
137 | | -| `/auth/login` | None | None | |
138 | | -| `GET /courses` | None | None | |
139 | | -| `POST /courses` | `TEACHER` | None | |
140 | | -| `GET /courses/enrolled` | `STUDENT` | None | |
141 | | -| `POST /courses/:id/enroll` | `STUDENT` | Implicit (creates enrollment) | |
142 | | -| `GET /courses/:id/assignments` | `STUDENT` or `TEACHER` | `assertEnrolled` (Student) OR `assertOwnsCourse` (Teacher) | |
143 | | -| `POST /courses/:id/assignments`| `TEACHER` | `assertOwnsCourse` | |
144 | | -| `POST /assignments/:id/submit` | `STUDENT` | `assertEnrolled` (via Course) | |
145 | | -| `GET /assignments/:id/submissions`| `TEACHER`| `assertOwnsCourse` (via Course) | |
146 | | - |
147 | | -3. **Strict Boundary Validation**: All incoming requests pass through a global `validateBody` middleware using strict Joi schemas, dropping malformed payloads before they reach business logic. |
148 | | -4. **Recursive Log Redaction**: The Winston logger utilizes a recursive object traversal function to guarantee PII (`password`, `token`, etc.) is redacted (`[REDACTED]`), regardless of nesting depth. |
149 | | - |
150 | | -## Testing |
151 | | -The repository maintains strict integration test coverage spanning the full API boundary, database constraint validation, rate limiting, and log redaction. |
| 48 | +Tests use disposable, uniquely named PostgreSQL databases. An external local test server can be selected using `LMS_TEST_ADMIN_CONNECTION`. |
| 49 | + |
| 50 | +## Roll back to Express |
152 | 51 |
|
153 | 52 | ```bash |
154 | | -# Run the full test suite (automatically spins up a clean Postgres container on port 5434) |
155 | | -npm test |
| 53 | +LMS_BACKEND=node bash start_app.sh |
156 | 54 | ``` |
157 | 55 |
|
158 | | -## Design Decisions |
159 | | -- **Hard Delete cascades**: `onDelete: Cascade` rules were implemented strictly across the schema. A course serves as a container; deleting a course destroys its assignments and submissions. |
160 | | -- **UUIDs and API Coercion**: A dedicated integer coercion middleware shields the database from Prisma's `NaN` panics. |
161 | | -- **Graceful Shutdown & Resilience**: The application leverages `SIGTERM`/`SIGINT` traps and explicitly delays startup until the database emits a successful connection ping. |
162 | | -- **Audit Exception (`deepmerge-ts`, `mysql2`)**: The `npm audit` step currently returns advisory warnings for high-severity findings related to `deepmerge-ts` and `mysql2`. These are transitive dependencies of the Prisma CLI. Since this project strictly uses PostgreSQL, the `mysql2` vulnerability is unreachable at runtime. Resolving these warnings would require a breaking downgrade of Prisma. The audit step remains advisory (non-blocking) as no runtime risk is present. |
| 56 | +This selects the old API on port 3000 and changes the development proxy. Retain the same database and JWT secret during the compatibility window; do not run Prisma migrations after transferring migration ownership to EF Core. |
163 | 57 |
|
164 | | -## Limitations |
165 | | -- **No Frontend**: This repository is strictly a backend API. The GitHub Actions CI pipeline verifies backend linting, database migrations from scratch, and integration tests, but intentionally does not build or assert UI artifacts. |
166 | | -- **In-Memory Rate Limiting**: The `express-rate-limit` configuration uses the default memory store. In a multi-node production deployment, this should be migrated to a Redis store. |
| 58 | +See [backend setup, API contract, and deployment](backend-dotnet/README.md) for details. |
0 commit comments