- Login for Admins & Employees (
/auth/login→ JWT) - Create/Update/Delete Employees (ADMIN/SUPER_ADMIN)
- Create/Update/Delete Admins (SUPER_ADMIN, with “self” exceptions)
- Attendance: punch-in & punch-out (employees self; admins can act for others)
- Reports: download attendance CSV with optional filters
- Redis caching for quick list views
docker run -d --name mongo -p 27017:27017 -v mongo_data:/data/db mongo:6
docker run -d --name redis -p 6379:6379 redis:7Create .env in the project root:
PORT=3010
MONGODB_URI=mongodb://localhost:27017/ems
REDIS_URL=redis://localhost:6379
JWT_SECRET=supersecret_change_me
JWT_EXPIRES=1d
npm install
npm run start:devSwagger Documentation - http://localhost:3010/docs for Swagger UI.
Auth
POST /auth/login→ returns{ access_token }
Employees (ADMIN/SUPER_ADMIN)
POST /employeesGET /employeesGET /employees/:id(admin or self)PATCH /employees/:id(admin or self)DELETE /employees/:id
Admins (SUPER_ADMIN; self rules applied on GET/PATCH by id)
POST /adminsGET /adminsGET /admins/:idPATCH /admins/:idDELETE /admins/:id(SUPER_ADMIN)
Attendance (JWT required)
POST /attendance/punch-inPOST /attendance/punch-outGET /attendance?employee=&from=&to=&date=→ filter by employee and date/range
Reports
GET /reports/attendance?employee=&from=&to=→ CSV download (headers when empty)
# 1) Login (get token)
curl -X POST http://localhost:3010/auth/login -H "Content-Type: application/json" --data '{"email":"[email protected]","password":"Password@123"}'
# 2) Punch in (as employee)
curl -X POST http://localhost:3010/attendance/punch-in -H "Authorization: Bearer <TOKEN>"
# 3) Download CSV (admin)
curl -G http://localhost:3010/reports/attendance -H "Authorization: Bearer <TOKEN>" --data-urlencode "employee=<EMPLOYEE_ID>" --data-urlencode "from=2025-08-01" --data-urlencode "to=2025-08-31" -o attendance.csv- Duplicate email → API returns
409 Conflict: email already existson create/update. - No active punch-in on punch-out → make sure there’s an open attendance record (
punchOutmissing or null) for that employee. - Connected to wrong DB (e.g.,
test) → check.envMONGODB_URIand restart. - Unauthorized → click Authorize in Swagger and paste
Bearer <token>.
Approach:
- Modular NestJS design (Auth, Admin, Employee, Attendance, Reports), MongoDB via Mongoose, Redis cache for “list” endpoints, JWT + role guards, DTO validation, Swagger at /docs.
- Pragmatic error handling: pre-check + try/catch around writes (409 on duplicate email), clean CSV responses (fields defined; header-only on empty).
Design decisions:
- Separate Admin and Employee entities: different lifecycles/permissions; Admin carries a Role enum (SUPER_ADMIN/ADMIN), Employee is simpler. Reduces over-fetching and keeps RBAC explicit.
- RBAC model: token embeds { sub, kind: 'ADMIN'|'EMP', role? };
@Roles()+RolesGuardenforce route access. Employees act on self; Admin/Super Admin can act “on behalf” (e.g., attendance). - Attendance storage: one document per session with
punchInand optionalpunchOut. “Open session” =punchOutmissing or null; punch-out uses atomicfindOneAndUpdateto avoid races. - Reports: server-side CSV with
json2csv; fields defined explicitly; when no data, return header-only CSV (or 204). Report rows map employee to email for readability. - Caching: Redis-backed CacheModule TTL ~60s for list endpoints; invalidation on create/update/delete to keep UX snappy without stale data.
- Config: env-first (
process.env/ConfigModule), sensible defaults, easy Docker-friendly local dev.