1637 lines
55 KiB
Markdown
1637 lines
55 KiB
Markdown
# Backend Development Tasks - Interview Quiz Application
|
|
|
|
## Project Setup Phase
|
|
|
|
### Task 1: Project Initialization
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1-2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create backend folder structure
|
|
- [x] Initialize Node.js project with `npm init`
|
|
- [x] Install core dependencies
|
|
```bash
|
|
npm install express sequelize mysql2 dotenv bcrypt jsonwebtoken
|
|
npm install express-validator cors helmet morgan
|
|
npm install --save-dev nodemon jest supertest
|
|
```
|
|
- [x] Install Sequelize CLI globally: `npm install -g sequelize-cli`
|
|
- [x] Create `.gitignore` file
|
|
- [x] Create `.env.example` file with all required variables
|
|
- [x] Setup basic Express server in `server.js`
|
|
- [x] Configure port and basic middleware (CORS, JSON parser, helmet)
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Server starts successfully on specified port
|
|
- ✅ Environment variables load correctly
|
|
- ✅ Basic middleware functions properly
|
|
|
|
---
|
|
|
|
### Task 2: Database Setup
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2-3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Install MySQL 8.0+ locally
|
|
- [x] Create database: `interview_quiz_db`
|
|
- [x] Initialize Sequelize: `npx sequelize-cli init`
|
|
- [x] Create `.sequelizerc` configuration file
|
|
- [x] Configure `config/database.js` with connection settings
|
|
- [x] Test database connection
|
|
- [x] Setup connection pooling configuration
|
|
- [x] Create `models/index.js` for model initialization
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Database connection successful
|
|
- ✅ Sequelize properly configured
|
|
- ✅ Connection pool working
|
|
- ✅ Can execute test query
|
|
|
|
#### Files Created:
|
|
- ✅ `.sequelizerc`
|
|
- ✅ `config/database.js`
|
|
- ✅ `config/db.js`
|
|
- ✅ `models/index.js`
|
|
- ✅ `test-db-connection.js`
|
|
|
|
---
|
|
|
|
### Task 3: Environment Configuration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1 hour
|
|
|
|
#### Subtasks:
|
|
- [x] Create `.env` file from `.env.example`
|
|
- [x] Configure database credentials
|
|
- [x] Generate JWT secret key
|
|
- [x] Setup NODE_ENV variables
|
|
- [x] Configure API prefix
|
|
- [x] Add rate limiting configuration
|
|
- [x] Add Redis configuration (optional for caching)
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ All required environment variables configured
|
|
- ✅ Secure JWT secret generated (128 characters)
|
|
- ✅ Environment validation passes
|
|
- ✅ Configuration centralized in config module
|
|
- ✅ Server validates environment on startup
|
|
|
|
#### Files Created:
|
|
- ✅ `.env` (configured with all variables)
|
|
- ✅ `generate-jwt-secret.js` (JWT secret generator)
|
|
- ✅ `validate-env.js` (environment validator)
|
|
- ✅ `config/config.js` (centralized configuration)
|
|
- ✅ `ENVIRONMENT_GUIDE.md` (complete documentation)
|
|
|
|
---
|
|
|
|
## Database Schema Phase
|
|
|
|
### Task 4: Create User Model & Migration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create migration: `npx sequelize-cli migration:generate --name create-users`
|
|
- [x] Define users table schema with UUID primary key
|
|
- [x] Add all user fields (username, email, password, role, stats)
|
|
- [x] Add indexes for email, username, role
|
|
- [x] Create `models/User.js` Sequelize model
|
|
- [x] Add model validations
|
|
- [x] Add password hashing hooks (beforeCreate, beforeUpdate)
|
|
- [x] Test migration: `npx sequelize-cli db:migrate`
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Migration 1
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Migration runs successfully
|
|
- ✅ Users table created with all fields
|
|
- ✅ Indexes applied (email, username, role, is_active, created_at)
|
|
- ✅ Model validations work (email, username, password)
|
|
- ✅ Password auto-hashing on create/update
|
|
- ✅ UUID primary key generation
|
|
- ✅ Helper methods (comparePassword, calculateAccuracy, etc.)
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251109214253-create-users.js`
|
|
- ✅ `models/User.js`
|
|
- ✅ `test-user-model.js`
|
|
|
|
---
|
|
|
|
### Task 5: Create Categories Model & Migration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1.5 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create migration: `npx sequelize-cli migration:generate --name create-categories`
|
|
- [x] Define categories table schema
|
|
- [x] Add guest_accessible and count fields
|
|
- [x] Add indexes for slug, is_active, guest_accessible
|
|
- [x] Create `models/Category.js` Sequelize model
|
|
- [x] Add slug generation hook
|
|
- [x] Test migration
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Migration 2
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Categories table created with 13 fields and 6 indexes
|
|
- ✅ Slug auto-generation works (beforeValidate, beforeCreate, beforeUpdate hooks)
|
|
- ✅ Model associations defined (Question, QuizSession, GuestSettings)
|
|
- ✅ Helper methods implemented (incrementQuestionCount, findBySlug, etc.)
|
|
- ✅ All 15 tests passing
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251109214935-create-categories.js`
|
|
- ✅ `models/Category.js` (255 lines)
|
|
- ✅ `test-category-model.js` (15 comprehensive tests)
|
|
|
|
---
|
|
|
|
### Task 6: Create Questions Model & Migration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create migration: `npx sequelize-cli migration:generate --name create-questions`
|
|
- [x] Define questions table with JSON columns
|
|
- [x] Add foreign key to categories
|
|
- [x] Add foreign key to users (created_by)
|
|
- [x] Add multiple indexes
|
|
- [x] Add full-text index for search
|
|
- [x] Create `models/Question.js` Sequelize model
|
|
- [x] Handle JSON serialization for options, keywords, tags
|
|
- [x] Add model associations (belongsTo Category, belongsTo User)
|
|
- [x] Test migration
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Migration 3
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Questions table created with 20 fields including JSON columns (options, keywords, tags)
|
|
- ✅ Full-text search index created on question_text and explanation
|
|
- ✅ Foreign keys enforced (category_id RESTRICT, created_by SET NULL)
|
|
- ✅ JSON fields serialize/deserialize correctly with custom getters/setters
|
|
- ✅ 10 indexes created including composite indexes for query optimization
|
|
- ✅ Model validations implemented (question type, options, correct answer format)
|
|
- ✅ Helper methods: incrementAttempted, incrementCorrect, getAccuracy, toSafeJSON
|
|
- ✅ Class methods: findActiveQuestions, searchQuestions, getRandomQuestions, getQuestionsByCategory
|
|
- ✅ Auto-set points based on difficulty (easy: 10, medium: 20, hard: 30)
|
|
- ✅ All 18 tests passing
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251109220030-create-questions.js`
|
|
- ✅ `models/Question.js` (455 lines)
|
|
- ✅ `test-question-model.js` (18 comprehensive tests)
|
|
|
|
---
|
|
|
|
### Task 7: Create Guest Sessions Model & Migration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1.5 hours
|
|
|
|
#### Subtasks:
|
|
- ✅ Create migration: `npx sequelize-cli migration:generate --name create-guest-sessions`
|
|
- ✅ Define guest_sessions table (14 fields, 7 indexes)
|
|
- ✅ Add indexes for guest_id (unique), session_token (unique), expires_at, is_converted, converted_user_id, device_id, created_at
|
|
- ✅ Create `models/GuestSession.js` model (340+ lines)
|
|
- ✅ Add JWT token generation and verification methods
|
|
- ✅ Add expiry validation and session extension
|
|
- ✅ Add guest-to-user conversion tracking
|
|
- ✅ Test migration (20 comprehensive tests - all passing)
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Migration 4
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Guest sessions table created with 14 fields
|
|
- ✅ JWT token generation works (format: guest_{timestamp}_{random})
|
|
- ✅ Expiry check functional with configurable duration
|
|
- ✅ Quiz limit tracking (default 3 quizzes per guest)
|
|
- ✅ Session extension capability (default 24 hours)
|
|
- ✅ Guest-to-user conversion tracking with foreign key
|
|
- ✅ Analytics methods: active count, conversion rate
|
|
- ✅ Test suite: 20 tests covering all functionality
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251109221034-create-guest-sessions.js`
|
|
- ✅ `models/GuestSession.js` (340+ lines with JWT management)
|
|
- ✅ `test-guest-session-model.js` (20 comprehensive tests)
|
|
- ✅ Added test script: `npm run test:guest`
|
|
|
|
---
|
|
|
|
### Task 8: Create Quiz Sessions Model & Migration
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- ✅ Create migration: `npx sequelize-cli migration:generate --name create-quiz-sessions`
|
|
- ✅ Define quiz_sessions table (21 fields, 11 indexes)
|
|
- ✅ Add foreign keys (user_id, guest_session_id, category_id) with proper CASCADE/SET NULL/RESTRICT
|
|
- ✅ Add indexes for user_id, guest_session_id, category_id, status, quiz_type, started_at, completed_at, created_at, is_passed
|
|
- ✅ Add composite indexes for user_status and guest_status
|
|
- ✅ Create `models/QuizSession.js` model (650+ lines)
|
|
- ✅ Add associations (belongsTo User, GuestSession, Category; hasMany QuizAnswer, QuizSessionQuestion)
|
|
- ✅ Implement quiz lifecycle methods (start, complete, abandon, timeout)
|
|
- ✅ Implement scoring and progress tracking
|
|
- ✅ Add validation (require either userId or guestSessionId, not both)
|
|
- ✅ Test migration (26 comprehensive tests - all passing)
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Migration 5
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Quiz sessions table created with 21 fields
|
|
- ✅ Support for both user and guest quizzes
|
|
- ✅ Multiple quiz types: practice, timed, exam
|
|
- ✅ Difficulty levels: easy, medium, hard, mixed
|
|
- ✅ Quiz lifecycle management (start, in_progress, complete, abandon, timeout)
|
|
- ✅ Automatic score calculation and pass/fail determination
|
|
- ✅ Time tracking with timeout support for timed quizzes
|
|
- ✅ Progress tracking (questions answered, remaining, accuracy)
|
|
- ✅ Statistics methods: user stats, category stats, history
|
|
- ✅ Cleanup method for abandoned sessions
|
|
- ✅ Test suite: 26 tests covering all functionality
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251110190953-create-quiz-sessions.js`
|
|
- ✅ `models/QuizSession.js` (650+ lines with comprehensive quiz management)
|
|
- ✅ `test-quiz-session-model.js` (26 comprehensive tests)
|
|
- ✅ Added test script: `npm run test:quiz`
|
|
|
|
---
|
|
|
|
### Task 9: Create Quiz Answers & Junction Tables
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- ✅ Create quiz_answers table migration (9 fields, 5 indexes)
|
|
- ✅ Create quiz_session_questions junction table migration (5 fields, 4 indexes)
|
|
- ✅ Create user_bookmarks junction table migration (5 fields, 4 indexes)
|
|
- ✅ Create achievements table migration (13 fields, 5 indexes)
|
|
- ✅ Create user_achievements junction table migration (6 fields, 5 indexes)
|
|
- ✅ Run all migrations successfully
|
|
- ✅ Verify foreign keys and cascade rules
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Quiz answers table created - stores individual answers during quizzes
|
|
- ✅ Quiz session questions junction table - links quizzes with questions in order
|
|
- ✅ User bookmarks junction table - allows users to save questions with notes
|
|
- ✅ Achievements table - defines available achievements with requirements
|
|
- ✅ User achievements junction table - tracks earned achievements
|
|
- ✅ All junction tables have unique composite indexes
|
|
- ✅ Foreign key constraints properly enforced (CASCADE on delete/update)
|
|
- ✅ All tables use UTF8MB4 charset for full Unicode support
|
|
|
|
#### Files Created:
|
|
- ✅ `migrations/20251110191735-create-quiz-answers.js` - Quiz answers table
|
|
- ✅ `migrations/20251110191906-create-quiz-session-questions.js` - Quiz-question junction
|
|
- ✅ `migrations/20251110192000-create-user-bookmarks.js` - User bookmarks junction
|
|
- ✅ `migrations/20251110192043-create-achievements.js` - Achievements definitions
|
|
- ✅ `migrations/20251110192130-create-user-achievements.js` - User-achievement junction
|
|
|
|
#### Database Schema Summary:
|
|
**quiz_answers**: Stores each answer given during a quiz
|
|
- Links to quiz_sessions and questions
|
|
- Tracks correct/incorrect, points earned, time taken
|
|
- Unique constraint: one answer per question per session
|
|
|
|
**quiz_session_questions**: Links quiz sessions with questions
|
|
- Maintains question order in quiz
|
|
- Enables loading quiz questions in sequence
|
|
|
|
**user_bookmarks**: User-saved questions for review
|
|
- Optional notes field for user annotations
|
|
- Prevents duplicate bookmarks
|
|
|
|
**achievements**: Gamification system
|
|
- 6 categories: quiz, streak, score, speed, milestone, special
|
|
- 8 requirement types: quizzes_completed, quizzes_passed, perfect_score, streak_days, etc.
|
|
- Configurable points and display order
|
|
|
|
**user_achievements**: Tracks earned achievements
|
|
- Notification tracking
|
|
- Prevents duplicate awards
|
|
|
|
---
|
|
|
|
### Task 10: Database Seeding
|
|
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create seeder for demo categories (7 categories)
|
|
- [x] Create seeder for admin user (admin@quiz.com)
|
|
- [x] Create seeder for sample questions (35 questions - 5 per category)
|
|
- [x] Create seeder for achievements (19 achievements across 6 categories)
|
|
- [x] Run all seeders: `npx sequelize-cli db:seed:all`
|
|
- [x] Test data integrity
|
|
|
|
#### Reference:
|
|
See `SAMPLE_MIGRATIONS.md` - Seeders section
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ All seed data inserted (7 categories, 1 admin user, 35 questions, 19 achievements)
|
|
- ✅ Relationships maintained (questions linked to categories, created_by admin user)
|
|
- ✅ Admin credentials: admin@quiz.com / Admin@123
|
|
- ✅ Guest-accessible categories: JavaScript, Angular, React (3/7)
|
|
- ✅ Auth-only categories: Node.js, TypeScript, SQL & Databases, System Design (4/7)
|
|
|
|
---
|
|
|
|
## Authentication & Authorization Phase
|
|
|
|
### Task 11: User Registration Endpoint
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create `routes/auth.routes.js`
|
|
- [x] Create `controllers/auth.controller.js`
|
|
- [x] Implement `register` controller function
|
|
- [x] Add input validation (email, password strength, username)
|
|
- [x] Check for duplicate email/username
|
|
- [x] Hash password with bcrypt (via User model hook)
|
|
- [x] Generate JWT token
|
|
- [x] Handle guest migration (optional guestSessionId)
|
|
- [x] Add error handling with transactions
|
|
- [x] Write middleware: `validation.middleware.js` and `auth.middleware.js`
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/auth/register
|
|
Body: {
|
|
username, email, password, guestSessionId (optional)
|
|
}
|
|
```
|
|
|
|
#### Reference:
|
|
See `interview_quiz_user_story.md` - User Story 1.1
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ User registered successfully with JWT token
|
|
- ✅ Password hashed automatically by User model beforeCreate hook
|
|
- ✅ JWT token returned with user data (password excluded)
|
|
- ✅ Duplicate emails/usernames rejected with 400 status
|
|
- ✅ Input validation works (username 3-50 chars, email format, password min 8 chars with uppercase/lowercase/number)
|
|
- ✅ Guest migration supported with transaction rollback on errors
|
|
- ✅ Login endpoint implemented
|
|
- ✅ Token verification endpoint implemented
|
|
- ✅ Logout endpoint implemented
|
|
- ✅ Auth middleware created (verifyToken, isAdmin, isOwnerOrAdmin)
|
|
|
|
---
|
|
|
|
### Task 12: User Login Endpoint
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `login` controller function
|
|
- [x] Validate email and password
|
|
- [x] Compare password hash
|
|
- [x] Generate JWT token
|
|
- [x] Update last_login timestamp
|
|
- [x] Return user data (exclude password)
|
|
- [ ] Add rate limiting
|
|
- [x] Write unit tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/auth/login
|
|
Body: { email, password }
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - User Operations
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Login successful with valid credentials
|
|
- ✅ JWT token generated with 24h expiration
|
|
- ✅ Invalid credentials rejected with 401 status
|
|
- ✅ Password comparison using User.comparePassword() method
|
|
- ✅ User data returned (password excluded via toSafeJSON)
|
|
- ✅ Only active users can login (isActive check)
|
|
- ✅ Email normalized to lowercase for case-insensitive login
|
|
- ✅ Tests included in auth.test.js and logout-verify.test.js
|
|
- ⏳ Rate limiting (pending - to be added in Task 44)
|
|
|
|
---
|
|
|
|
### Task 13: JWT Authentication Middleware
|
|
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create `middleware/auth.middleware.js`
|
|
- [x] Implement `verifyToken` middleware
|
|
- [x] Extract token from Authorization header
|
|
- [x] Verify JWT signature
|
|
- [x] Attach user to request object
|
|
- [x] Handle expired tokens
|
|
- [x] Handle invalid tokens
|
|
- [x] Create `isAdmin` middleware
|
|
- [x] Write tests
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Protected routes require valid token (Bearer format)
|
|
- ✅ User data available in req.user (userId, email, username, role)
|
|
- ✅ Expired tokens rejected with 401 status
|
|
- ✅ Admin-only routes protected with isAdmin middleware
|
|
- ✅ isOwnerOrAdmin middleware created for resource ownership checks
|
|
- ✅ Proper error handling (TokenExpiredError, JsonWebTokenError)
|
|
- ✅ Used in auth routes (GET /api/auth/verify)
|
|
- ✅ Tests included in auth.test.js and logout-verify.test.js
|
|
|
|
---
|
|
|
|
### Task 14: User Logout & Token Verification
|
|
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 1 hour
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `logout` endpoint (client-side token removal)
|
|
- [x] Implement `verifyToken` endpoint
|
|
- [x] Return user info if token valid
|
|
- [x] Write tests
|
|
|
|
#### API Endpoints:
|
|
```
|
|
POST /api/auth/logout
|
|
GET /api/auth/verify
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Logout endpoint returns success (stateless JWT approach)
|
|
- ✅ Token verification validates JWT and returns user data
|
|
- ✅ Password excluded from response (toSafeJSON method)
|
|
- ✅ Invalid tokens rejected with 401 status
|
|
- ✅ Missing tokens rejected with 401 status
|
|
- ✅ Expired tokens rejected with 401 status
|
|
- ✅ Inactive users rejected with 404 status
|
|
- ✅ Comprehensive test suite created (15+ tests)
|
|
- ✅ Manual test script created for verification
|
|
- ✅ Token still valid after logout (client-side token removal pattern)
|
|
|
|
---
|
|
|
|
## Guest User Management Phase
|
|
|
|
### Task 15: Guest Session Creation ✅
|
|
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create `routes/guest.routes.js`
|
|
- [x] Create `controllers/guest.controller.js`
|
|
- [x] Implement `startGuestSession` function
|
|
- [x] Generate unique guest_id (`guest_{timestamp}_{random}`)
|
|
- [x] Generate session token (JWT with 24h expiry)
|
|
- [x] Set expiry (24 hours default, configurable)
|
|
- [x] Store IP address and user agent
|
|
- [x] Return available categories (JavaScript, Angular, React)
|
|
- [x] Return quiz restrictions (max 3 quizzes, feature flags)
|
|
- [x] Write tests (7 test scenarios, all passing)
|
|
|
|
#### API Endpoints:
|
|
```
|
|
POST /api/guest/start-session
|
|
Body: { deviceId? }
|
|
Response: { guestId, sessionToken, expiresAt, restrictions, availableCategories }
|
|
|
|
GET /api/guest/session/:guestId
|
|
Response: { guestId, expiresAt, expiresIn, restrictions, availableCategories }
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Guest Session Operations
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Guest session created with unique ID
|
|
- ✅ Token generated and valid for 24h
|
|
- ✅ Guest restrictions applied (max 3 quizzes)
|
|
- ✅ Feature restrictions set (no bookmarks, no progress tracking)
|
|
- ✅ Guest-accessible categories returned (JavaScript, Angular, React)
|
|
- ✅ Session retrieval and validation working
|
|
- ✅ All 7 tests passing
|
|
|
|
---
|
|
|
|
### Task 16: Guest Quiz Limit Check ✅
|
|
**Priority**: High | **Status**: Completed | **Estimated Time**: 1.5 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create guest authentication middleware (`middleware/guest.middleware.js`)
|
|
- [x] Implement `verifyGuestToken` middleware with session validation
|
|
- [x] Implement `checkQuizLimit` function in controller
|
|
- [x] Verify guest session exists and not expired (middleware)
|
|
- [x] Check quizzes_attempted vs max_quizzes
|
|
- [x] Return remaining quizzes and calculations
|
|
- [x] Calculate and return reset time (time until session expiry)
|
|
- [x] Return upgrade prompt when limit reached
|
|
- [x] Write comprehensive tests (8 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/guest/quiz-limit
|
|
Headers: { X-Guest-Token: <token> }
|
|
Response: {
|
|
guestId,
|
|
quizLimit: { maxQuizzes, quizzesAttempted, quizzesRemaining, hasReachedLimit },
|
|
session: { expiresAt, timeRemaining, resetTime },
|
|
upgradePrompt?: { message, benefits[], callToAction }
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Guest token middleware validates JWT and session
|
|
- ✅ Middleware checks session exists, not expired, not converted
|
|
- ✅ Quiz limit calculation accurate (max - attempted)
|
|
- ✅ Time remaining calculated correctly (hours and minutes)
|
|
- ✅ Upgrade prompt shown when limit reached (5 benefits listed)
|
|
- ✅ Proper error handling (401, 404, 410 status codes)
|
|
- ✅ All 8 tests passing (valid token, no token, invalid token, non-existent guest, structure validation, calculations, limit reached scenario)
|
|
|
|
---
|
|
|
|
### Task 17: Guest to User Conversion ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Review existing guest migration logic in auth registration
|
|
- [x] Create standalone `convertGuestToUser` endpoint
|
|
- [x] Add guest middleware protection (verifies guest session)
|
|
- [x] Implement transaction-based data migration
|
|
- [x] Create new user account with password hashing
|
|
- [x] Migrate quiz sessions from guest to user
|
|
- [x] Calculate and update user stats from migrated sessions
|
|
- [x] Mark guest session as converted (isConverted=true)
|
|
- [x] Handle duplicate email/username validation
|
|
- [x] Handle rollback on error
|
|
- [x] Generate JWT token for new user
|
|
- [x] Write comprehensive tests (10 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/guest/convert
|
|
Headers: { X-Guest-Token: <token> }
|
|
Body: { username, email, password }
|
|
Response: {
|
|
user: { id, username, email, role },
|
|
token: <JWT>,
|
|
migration: {
|
|
quizzesTransferred: number,
|
|
stats: { totalQuizzes, quizzesPassed, accuracy }
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Transaction Example
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Guest session validated via middleware (must not be expired or converted)
|
|
- ✅ Input validation (username alphanumeric 3-50 chars, valid email, password 8+ chars)
|
|
- ✅ Duplicate email/username checks with 400 status
|
|
- ✅ User created with transaction rollback on error
|
|
- ✅ Quiz sessions migrated from guestSessionId to userId
|
|
- ✅ User stats calculated from migrated completed quizzes
|
|
- ✅ Guest session marked as converted with convertedUserId
|
|
- ✅ JWT token generated for immediate login
|
|
- ✅ Already converted sessions rejected with 410 status
|
|
- ✅ Converted user can login with new credentials
|
|
- ✅ All 10 tests passing (validation, success, duplicates, converted session, login)
|
|
|
|
#### Notes:
|
|
- Guest migration also supported in registration endpoint (Task 11) via optional guestSessionId parameter
|
|
- This standalone endpoint allows guests to convert without re-entering data
|
|
- Transaction ensures atomic operation - either all data migrates or nothing changes
|
|
- Password automatically hashed by User model beforeCreate hook
|
|
|
|
---
|
|
|
|
## Category Management Phase
|
|
|
|
### Task 18: Get All Categories ✅
|
|
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create `routes/category.routes.js`
|
|
- [x] Create `controllers/category.controller.js`
|
|
- [x] Create optional auth middleware (`optionalAuth`)
|
|
- [x] Implement `getAllCategories` function
|
|
- [x] Filter by isActive
|
|
- [x] Include questionCount from model
|
|
- [x] Handle guest vs registered user view (guestAccessible filter)
|
|
- [x] Order by displayOrder and name
|
|
- [x] Write comprehensive tests (7 scenarios)
|
|
- [ ] Add caching (Redis optional - deferred)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/categories
|
|
Headers: { Authorization: Bearer <token> } (optional)
|
|
Response: {
|
|
success: true,
|
|
count: number,
|
|
data: [{ id, name, slug, description, icon, color, questionCount, displayOrder, guestAccessible }],
|
|
message: string
|
|
}
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Category Operations
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Public endpoint accessible without authentication
|
|
- ✅ Optional auth middleware attaches user if token provided
|
|
- ✅ Guest users see only guest-accessible categories (3: JavaScript, Angular, React)
|
|
- ✅ Authenticated users see all active categories (7 total, including Node.js, TypeScript, SQL, System Design)
|
|
- ✅ Only active categories returned (isActive=true)
|
|
- ✅ Categories ordered by displayOrder, then name
|
|
- ✅ Response includes questionCount for each category
|
|
- ✅ Proper response structure with success, count, data, message
|
|
- ✅ All 7 tests passing (guest view, auth view, filtering, ordering, structure validation)
|
|
|
|
#### Notes:
|
|
- Created `optionalAuth` middleware in auth.middleware.js for public endpoints with optional authentication
|
|
- Guest users see 3 categories, authenticated users see 7 (4 additional auth-only categories)
|
|
- Redis caching deferred to Task 45 (Database Optimization)
|
|
|
|
---
|
|
|
|
### Task 19: Get Category Details ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 1 hour
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `getCategoryById` function
|
|
- [x] Include related questions preview (first 5 questions)
|
|
- [x] Return category stats (difficulty breakdown, accuracy)
|
|
- [x] Write tests (9 comprehensive scenarios)
|
|
- [x] Add UUID validation for category IDs
|
|
- [x] Implement guest vs authenticated access control
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/categories/:id
|
|
Headers: { Authorization: Bearer <token> } (optional)
|
|
Response: {
|
|
success: true,
|
|
data: {
|
|
category: { id, name, slug, description, icon, color, questionCount, displayOrder, guestAccessible },
|
|
questionPreview: [{ id, questionText, questionType, difficulty, points, accuracy }],
|
|
stats: {
|
|
totalQuestions,
|
|
questionsByDifficulty: { easy, medium, hard },
|
|
totalAttempts,
|
|
totalCorrect,
|
|
averageAccuracy
|
|
}
|
|
},
|
|
message: string
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Category retrieved by UUID (not integer ID)
|
|
- ✅ Question preview limited to 5 questions ordered by creation date
|
|
- ✅ Stats calculated from all active questions in category
|
|
- ✅ Guest users can access guest-accessible categories
|
|
- ✅ Guest users blocked from auth-only categories with 403 status
|
|
- ✅ Authenticated users can access all active categories
|
|
- ✅ Invalid UUID format returns 400 status
|
|
- ✅ Non-existent category returns 404 status
|
|
- ✅ Question accuracy calculated per question
|
|
- ✅ Difficulty breakdown shows easy/medium/hard counts
|
|
- ✅ All 9 tests passing
|
|
|
|
#### Notes:
|
|
- Categories use UUID primary keys, not integers
|
|
- UUID validation accepts format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
|
- Question displayOrder column doesn't exist, ordered by createdAt instead
|
|
- Test revealed login response structure: `response.data.data.token`
|
|
|
|
---
|
|
|
|
### Task 20: Create/Update/Delete Category (Admin) ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `createCategory` (admin only)
|
|
- [x] Auto-generate slug from name (via model hook)
|
|
- [x] Validate unique slug and name
|
|
- [x] Implement `updateCategory` (admin only)
|
|
- [x] Implement `deleteCategory` (soft delete)
|
|
- [x] Update guest_accessible flag
|
|
- [x] Add authorization middleware (verifyToken + isAdmin)
|
|
- [x] Write comprehensive tests (14 scenarios)
|
|
|
|
#### API Endpoints:
|
|
```
|
|
POST /api/categories (admin)
|
|
Body: { name, slug?, description?, icon?, color?, guestAccessible?, displayOrder? }
|
|
Response: { success, data: { id, name, slug, ... }, message }
|
|
|
|
PUT /api/categories/:id (admin)
|
|
Body: { name?, slug?, description?, icon?, color?, guestAccessible?, displayOrder?, isActive? }
|
|
Response: { success, data: { id, name, slug, ... }, message }
|
|
|
|
DELETE /api/categories/:id (admin)
|
|
Response: { success, data: { id, name, questionCount }, message }
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Admin can create new categories with all fields
|
|
- ✅ Slug auto-generated from name if not provided
|
|
- ✅ Custom slug supported with uniqueness validation
|
|
- ✅ Duplicate name/slug rejected with 400 status
|
|
- ✅ Missing required name field rejected with 400 status
|
|
- ✅ Admin can update any category field
|
|
- ✅ Update validates name/slug uniqueness
|
|
- ✅ Non-existent category returns 404 status
|
|
- ✅ Soft delete sets isActive to false (not physical delete)
|
|
- ✅ Deleted category not shown in active category list
|
|
- ✅ Already deleted category cannot be deleted again
|
|
- ✅ Question count included in delete response
|
|
- ✅ Non-admin users blocked from all operations (403 status)
|
|
- ✅ Unauthenticated requests blocked (401 status)
|
|
- ✅ All 14 tests passing
|
|
|
|
#### Notes:
|
|
- Uses existing `isAdmin` middleware for authorization
|
|
- Soft delete preserves data integrity (questions still reference category)
|
|
- Slug generation handled by Category model beforeValidate hook
|
|
- Default color is '#3B82F6' (blue) if not provided
|
|
- displayOrder defaults to 0 if not provided
|
|
|
|
---
|
|
|
|
## Question Management Phase
|
|
|
|
### Task 21: Get Questions by Category ✅
|
|
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Create `routes/question.routes.js`
|
|
- [x] Create `controllers/question.controller.js`
|
|
- [x] Implement `getQuestionsByCategory` function
|
|
- [x] Filter by difficulty (optional: easy, medium, hard)
|
|
- [x] Filter by visibility (guest vs authenticated user)
|
|
- [x] Random selection support (random=true query param)
|
|
- [x] Pagination support (limit parameter, max 50)
|
|
- [x] Write comprehensive tests (14 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/questions/category/:categoryId?difficulty=easy&limit=10&random=true
|
|
Headers: { Authorization: Bearer <token> } (optional)
|
|
Response: {
|
|
success: true,
|
|
count: number,
|
|
total: number,
|
|
category: { id, name, slug, icon, color },
|
|
filters: { difficulty, limit, random },
|
|
data: [{
|
|
id, questionText, questionType, options,
|
|
difficulty, points, timesAttempted, timesCorrect,
|
|
accuracy, explanation, tags, createdAt,
|
|
category: { id, name, slug, icon, color }
|
|
}],
|
|
message: string
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Public endpoint with optional authentication
|
|
- ✅ Guest users can access guest-accessible categories (JavaScript, Angular, React)
|
|
- ✅ Guest users blocked from auth-only categories with 403 status
|
|
- ✅ Authenticated users can access all active categories
|
|
- ✅ UUID validation for category IDs (400 for invalid format)
|
|
- ✅ Non-existent category returns 404 status
|
|
- ✅ Difficulty filter works (easy, medium, hard)
|
|
- ✅ Invalid difficulty values ignored (defaults to 'all')
|
|
- ✅ Limit parameter enforced (default 10, max 50)
|
|
- ✅ Random selection works (random=true query param)
|
|
- ✅ Combined filters work (difficulty + limit + random)
|
|
- ✅ Question accuracy calculated (timesCorrect / timesAttempted * 100)
|
|
- ✅ Correct answer NOT exposed in response
|
|
- ✅ Category info included in response
|
|
- ✅ Total count returned (with filters applied)
|
|
- ✅ All 14 tests passing
|
|
|
|
#### Implementation Notes:
|
|
- Uses `optionalAuth` middleware for public access with auth benefits
|
|
- UUID regex validation: `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i`
|
|
- Random ordering via `sequelize.random()`
|
|
- Default ordering by `createdAt ASC` when random=false
|
|
- Limit validation: `Math.min(Math.max(parseInt(limit) || 10, 1), 50)`
|
|
- Accuracy calculation per question with 0 default for unattempted questions
|
|
- Category association included via `include` with alias 'category'
|
|
- correctAnswer explicitly excluded from response attributes
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Question Operations
|
|
|
|
---
|
|
|
|
### Task 22: Get Question by ID ✅
|
|
**Priority**: High | **Status**: Completed | **Estimated Time**: 1 hour
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `getQuestionById` function
|
|
- [x] Check visibility permissions (guest vs authenticated)
|
|
- [x] Don't expose correct_answer in response
|
|
- [x] Include category info with association
|
|
- [x] Write comprehensive tests (12 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/questions/:id
|
|
Headers: { Authorization: Bearer <token> } (optional)
|
|
Response: {
|
|
success: true,
|
|
data: {
|
|
id, questionText, questionType, options,
|
|
difficulty, points, explanation, tags, keywords,
|
|
accuracy, createdAt, updatedAt,
|
|
statistics: { timesAttempted, timesCorrect, accuracy },
|
|
category: { id, name, slug, icon, color, guestAccessible }
|
|
},
|
|
message: string
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Public endpoint with optional authentication
|
|
- ✅ UUID validation for question IDs (400 for invalid format)
|
|
- ✅ Non-existent question returns 404 status
|
|
- ✅ Guest users can access guest-accessible questions
|
|
- ✅ Guest users blocked from auth-only questions with 403 status
|
|
- ✅ Authenticated users can access all active questions
|
|
- ✅ Inactive category questions return 404 status
|
|
- ✅ Correct answer NOT exposed in response
|
|
- ✅ Category information included via association
|
|
- ✅ Question accuracy calculated (timesCorrect / timesAttempted * 100)
|
|
- ✅ Statistics object included (timesAttempted, timesCorrect, accuracy)
|
|
- ✅ All question types supported (multiple, trueFalse, written)
|
|
- ✅ Options array present for multiple choice questions
|
|
- ✅ Tags and keywords fields included (can be null or array)
|
|
- ✅ Points validated by difficulty (easy=5, medium=10, hard=15)
|
|
- ✅ All 12 tests passing
|
|
|
|
#### Implementation Notes:
|
|
- Added `getQuestionById` function to question.controller.js
|
|
- Uses `optionalAuth` middleware for public access with auth benefits
|
|
- UUID regex validation: `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i`
|
|
- Category association includes guestAccessible flag for access control
|
|
- correctAnswer explicitly excluded from query attributes
|
|
- Statistics object added for clearer attempt/success tracking
|
|
- Category's isActive flag removed from response (internal use only)
|
|
- Route added to question.routes.js as `GET /:id`
|
|
|
|
---
|
|
|
|
### Task 23: Question Search (Full-Text) ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `searchQuestions` function
|
|
- [x] Use MySQL MATCH AGAINST for full-text search
|
|
- [x] Filter by category (optional UUID)
|
|
- [x] Filter by difficulty (optional: easy, medium, hard)
|
|
- [x] Highlight matching text with ** markers
|
|
- [x] Pagination support with page and limit
|
|
- [x] Write comprehensive tests (14 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/questions/search?q=javascript&category=uuid&difficulty=medium&limit=20&page=1
|
|
Headers: { Authorization: Bearer <token> } (optional)
|
|
Response: {
|
|
success: true,
|
|
count: number,
|
|
total: number,
|
|
page: number,
|
|
totalPages: number,
|
|
limit: number,
|
|
query: string,
|
|
filters: { category: uuid|null, difficulty: string|null },
|
|
data: [{
|
|
id, questionText, highlightedText, questionType, options,
|
|
difficulty, points, accuracy, explanation, tags,
|
|
relevance, createdAt,
|
|
category: { id, name, slug, icon, color }
|
|
}],
|
|
message: string
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Full-text search using MySQL MATCH AGAINST on question_text and explanation
|
|
- ✅ Search query required (400 if missing or empty)
|
|
- ✅ Guest users see only guest-accessible category results
|
|
- ✅ Authenticated users see all category results
|
|
- ✅ Category filter by UUID (optional, validated)
|
|
- ✅ Difficulty filter (easy, medium, hard) (optional)
|
|
- ✅ Combined filters work (category + difficulty)
|
|
- ✅ Invalid category UUID returns 400
|
|
- ✅ Pagination support with page and limit parameters
|
|
- ✅ Default limit 20, max limit 100
|
|
- ✅ Results ordered by relevance DESC, then createdAt DESC
|
|
- ✅ Relevance score included in each result
|
|
- ✅ Text highlighting applied with ** markers for matched terms
|
|
- ✅ Response includes total, totalPages, page metadata
|
|
- ✅ Correct answer NOT exposed in results
|
|
- ✅ All 14 tests passing
|
|
|
|
#### Implementation Notes:
|
|
- Added `searchQuestions` function to question.controller.js (240+ lines)
|
|
- Uses raw SQL with `sequelize.query()` for MATCH AGAINST full-text search
|
|
- Full-text index exists on questions(question_text, explanation)
|
|
- Text highlighting helper function splits search term into words
|
|
- Words shorter than 3 characters excluded from highlighting
|
|
- Pagination uses LIMIT and OFFSET for efficient paging
|
|
- Two queries: one for results, one for total count
|
|
- Route added as `GET /search` (must come before `/:id` to avoid conflicts)
|
|
- Guest accessibility checked via category join in WHERE clause
|
|
- JSON fields (options, tags) parsed from database strings
|
|
- Accuracy calculated per question (timesCorrect / timesAttempted * 100)
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Search Questions
|
|
|
|
---
|
|
|
|
### Task 24: Create Question (Admin) ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `createQuestion` (admin only)
|
|
- [x] Validate question type (multiple/trueFalse/written)
|
|
- [x] Validate options for multiple choice (2-6 options)
|
|
- [x] Store options, keywords, tags as JSON
|
|
- [x] Auto-calculate points based on difficulty
|
|
- [x] Validate category exists and is active
|
|
- [x] Increment category question count
|
|
- [x] Write comprehensive tests (16 scenarios)
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/admin/questions
|
|
Headers: { Authorization: Bearer <admin-token> }
|
|
Body: {
|
|
questionText: string (required),
|
|
questionType: 'multiple' | 'trueFalse' | 'written' (required),
|
|
options: [{ id, text }] (required for multiple choice),
|
|
correctAnswer: string (required),
|
|
difficulty: 'easy' | 'medium' | 'hard' (required),
|
|
points: number (optional, auto-calculated if not provided),
|
|
explanation: string (optional),
|
|
categoryId: uuid (required),
|
|
tags: string[] (optional),
|
|
keywords: string[] (optional)
|
|
}
|
|
Response: {
|
|
success: true,
|
|
data: {
|
|
id, questionText, questionType, options,
|
|
difficulty, points, explanation, tags, keywords,
|
|
category: { id, name, slug, icon, color },
|
|
createdAt
|
|
},
|
|
message: "Question created successfully"
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Admin-only access (verifyToken + isAdmin middleware)
|
|
- ✅ Non-admin users blocked with 403 status
|
|
- ✅ Unauthenticated requests blocked with 401 status
|
|
- ✅ Required fields validated (questionText, questionType, correctAnswer, difficulty, categoryId)
|
|
- ✅ Question type validated (multiple, trueFalse, written)
|
|
- ✅ Difficulty validated (easy, medium, hard)
|
|
- ✅ Category UUID format validated
|
|
- ✅ Category existence checked (404 if not found)
|
|
- ✅ Inactive categories rejected
|
|
- ✅ Multiple choice questions require options array
|
|
- ✅ Options must have 2-6 items with id and text fields
|
|
- ✅ Correct answer must match one of the option IDs
|
|
- ✅ True/False questions validate correctAnswer as 'true' or 'false'
|
|
- ✅ Points auto-calculated: easy=5, medium=10, hard=15
|
|
- ✅ Custom points supported (overrides auto-calculation)
|
|
- ✅ Tags and keywords stored as JSON arrays
|
|
- ✅ Category questionCount incremented
|
|
- ✅ Question includes category info in response
|
|
- ✅ Created by admin userId tracked
|
|
- ✅ Correct answer NOT exposed in response
|
|
- ✅ All 16 tests passing
|
|
|
|
#### Implementation Notes:
|
|
- Added `createQuestion` function to question.controller.js (260+ lines)
|
|
- Created admin.routes.js for admin-only endpoints
|
|
- Route: POST `/api/admin/questions` with verifyToken + isAdmin
|
|
- Registered admin routes in server.js at `/api/admin`
|
|
- Comprehensive validation for all question types
|
|
- Options validation: min 2, max 6 options
|
|
- Each option requires `id` and `text` fields
|
|
- True/False answers validated as string 'true' or 'false'
|
|
- Points auto-calculation based on difficulty
|
|
- Category.increment('questionCount') updates count
|
|
- Question reloaded with category association after creation
|
|
- createdBy field set to req.user.userId from JWT
|
|
- JSON fields (options, tags, keywords) handled by Sequelize getters/setters
|
|
|
|
#### Reference:
|
|
See `interview_quiz_user_story.md` - User Story 5.1
|
|
|
|
---
|
|
|
|
### Task 25: Update/Delete Question (Admin) ✅
|
|
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [x] Implement `updateQuestion` (admin only)
|
|
- [x] Validate changes (partial updates supported)
|
|
- [x] Implement `deleteQuestion` (soft delete)
|
|
- [x] Update category counts on category change and delete
|
|
- [x] Write comprehensive tests (26 scenarios)
|
|
|
|
#### API Endpoints:
|
|
```
|
|
PUT /api/admin/questions/:id
|
|
Headers: { Authorization: Bearer <admin-token> }
|
|
Body: {
|
|
questionText?: string,
|
|
questionType?: 'multiple' | 'trueFalse' | 'written',
|
|
options?: [{ id, text }],
|
|
correctAnswer?: string,
|
|
difficulty?: 'easy' | 'medium' | 'hard',
|
|
points?: number,
|
|
explanation?: string,
|
|
categoryId?: uuid,
|
|
tags?: string[],
|
|
keywords?: string[],
|
|
isActive?: boolean
|
|
}
|
|
Response: {
|
|
success: true,
|
|
data: {
|
|
id, questionText, questionType, options,
|
|
difficulty, points, explanation, tags, keywords,
|
|
category: { id, name, slug, icon, color },
|
|
isActive, updatedAt
|
|
},
|
|
message: "Question updated successfully"
|
|
}
|
|
|
|
DELETE /api/admin/questions/:id
|
|
Headers: { Authorization: Bearer <admin-token> }
|
|
Response: {
|
|
success: true,
|
|
data: {
|
|
id, questionText,
|
|
category: { id, name }
|
|
},
|
|
message: "Question deleted successfully"
|
|
}
|
|
```
|
|
|
|
#### Acceptance Criteria:
|
|
- ✅ Admin-only access (verifyToken + isAdmin middleware)
|
|
- ✅ Non-admin users blocked with 403 status
|
|
- ✅ Unauthenticated requests blocked with 401 status
|
|
- ✅ Partial updates supported (only provided fields updated)
|
|
- ✅ Question text validation (non-empty after trim)
|
|
- ✅ Question type validation (multiple, trueFalse, written)
|
|
- ✅ Options validation for multiple choice (2-6 options)
|
|
- ✅ Correct answer validation matches option IDs
|
|
- ✅ True/False answer validation ('true' or 'false')
|
|
- ✅ Difficulty validation (easy, medium, hard)
|
|
- ✅ Auto-points calculation when difficulty changes
|
|
- ✅ Custom points override supported
|
|
- ✅ Category UUID validation and existence check
|
|
- ✅ Category counts updated when category changes
|
|
- ✅ Invalid UUID format returns 400
|
|
- ✅ Non-existent question returns 404
|
|
- ✅ Soft delete sets isActive to false (not physical delete)
|
|
- ✅ Already deleted question cannot be deleted again
|
|
- ✅ Category question count decremented on delete
|
|
- ✅ Deleted questions not accessible via API
|
|
- ✅ Correct answer NOT exposed in response
|
|
- ✅ All 26 tests passing (19 update + 7 delete scenarios)
|
|
|
|
#### Implementation Notes:
|
|
- Added `updateQuestion` function to question.controller.js (200+ lines)
|
|
- Added `deleteQuestion` function to question.controller.js (70+ lines)
|
|
- Routes added to admin.routes.js: PUT `/api/admin/questions/:id` and DELETE `/api/admin/questions/:id`
|
|
- Partial update pattern: only fields provided in request body are updated
|
|
- Effective type validation: uses updated questionType if provided, else existing
|
|
- Category count management: decrement old category, increment new category on change
|
|
- Soft delete preserves data integrity (questions still exist in database)
|
|
- Category.decrement('questionCount') on delete maintains accurate counts
|
|
- UUID validation for both question ID and category ID
|
|
- Empty/whitespace-only question text rejected
|
|
- Points auto-calculated when difficulty changes (unless custom points provided)
|
|
- Tags and keywords optional (can be null or arrays)
|
|
- isActive flag allows manual activation/deactivation
|
|
- Test suite covers all validation scenarios and edge cases
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Update/Delete Operations
|
|
|
|
---
|
|
|
|
## Quiz Session Management Phase
|
|
|
|
### Task 26: Start Quiz Session
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create `routes/quiz.routes.js`
|
|
- [ ] Create `controllers/quiz.controller.js`
|
|
- [ ] Implement `startQuizSession` function
|
|
- [ ] Check guest quiz limit (if guest)
|
|
- [ ] Create quiz session record
|
|
- [ ] Select random questions from category
|
|
- [ ] Create quiz_session_questions junction records
|
|
- [ ] Return session ID and questions (without correct answers)
|
|
- [ ] Increment guest quizzes_attempted
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/quiz/start
|
|
Body: { categoryId, questionCount, difficulty }
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Start Quiz Session
|
|
|
|
---
|
|
|
|
### Task 27: Submit Answer
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `submitAnswer` function
|
|
- [ ] Validate session exists and in-progress
|
|
- [ ] Check if question belongs to session
|
|
- [ ] Check if already answered
|
|
- [ ] Compare answer with correct_answer
|
|
- [ ] Save to quiz_answers table
|
|
- [ ] Update quiz session score if correct
|
|
- [ ] Increment question times_attempted
|
|
- [ ] Return immediate feedback (isCorrect, explanation)
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/quiz/submit
|
|
Body: { quizSessionId, questionId, userAnswer, timeSpent }
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Submit Answer
|
|
|
|
---
|
|
|
|
### Task 28: Complete Quiz Session
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `completeQuizSession` function
|
|
- [ ] Calculate final score
|
|
- [ ] Calculate percentage
|
|
- [ ] Calculate time taken
|
|
- [ ] Update session status to 'completed'
|
|
- [ ] Set end_time and completed_at
|
|
- [ ] Update user stats (if registered)
|
|
- [ ] Return detailed results
|
|
- [ ] Check for achievements
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
POST /api/quiz/complete
|
|
Body: { sessionId }
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Complete Quiz Session
|
|
|
|
---
|
|
|
|
### Task 29: Get Session Details
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 1.5 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getSessionDetails` function
|
|
- [ ] Return session info
|
|
- [ ] Include questions and answers
|
|
- [ ] Include category details
|
|
- [ ] Check authorization (own session only)
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/quiz/session/:sessionId
|
|
```
|
|
|
|
---
|
|
|
|
### Task 30: Review Completed Quiz
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `reviewQuizSession` function
|
|
- [ ] Return all questions with user answers
|
|
- [ ] Include correct answers and explanations
|
|
- [ ] Mark correct/incorrect visually
|
|
- [ ] Include time spent per question
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/quiz/review/:sessionId
|
|
```
|
|
|
|
---
|
|
|
|
## User Dashboard & Analytics Phase
|
|
|
|
### Task 31: Get User Dashboard
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create `routes/user.routes.js`
|
|
- [ ] Create `controllers/user.controller.js`
|
|
- [ ] Implement `getUserDashboard` function
|
|
- [ ] Return user stats (total quizzes, accuracy, streak)
|
|
- [ ] Return recent quiz sessions (last 10)
|
|
- [ ] Return category-wise performance
|
|
- [ ] Calculate overall accuracy
|
|
- [ ] Include achievements
|
|
- [ ] Add caching
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/users/:userId/dashboard
|
|
```
|
|
|
|
#### Reference:
|
|
See `interview_quiz_user_story.md` - User Story 4.1
|
|
|
|
---
|
|
|
|
### Task 32: Get Quiz History
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getQuizHistory` function
|
|
- [ ] Pagination support
|
|
- [ ] Filter by category
|
|
- [ ] Filter by date range
|
|
- [ ] Sort by date or score
|
|
- [ ] Return session summaries
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/users/:userId/history?page=1&limit=10&category=Angular
|
|
```
|
|
|
|
---
|
|
|
|
### Task 33: Update User Profile
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `updateUserProfile` function
|
|
- [ ] Allow username change (check uniqueness)
|
|
- [ ] Allow profile_image upload (future: integrate with S3)
|
|
- [ ] Password change (verify old password)
|
|
- [ ] Email change (verify new email)
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
PUT /api/users/:userId
|
|
```
|
|
|
|
---
|
|
|
|
## Bookmark Management Phase
|
|
|
|
### Task 34: Add/Remove Bookmark
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `addBookmark` function
|
|
- [ ] Check if already bookmarked
|
|
- [ ] Create user_bookmarks record
|
|
- [ ] Implement `removeBookmark` function
|
|
- [ ] Delete user_bookmarks record
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoints:
|
|
```
|
|
POST /api/users/:userId/bookmarks
|
|
DELETE /api/users/:userId/bookmarks/:questionId
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Bookmark Operations
|
|
|
|
---
|
|
|
|
### Task 35: Get User Bookmarks
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 1.5 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getUserBookmarks` function
|
|
- [ ] Include question details
|
|
- [ ] Include category info
|
|
- [ ] Sort by bookmarked_at
|
|
- [ ] Pagination
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/users/:userId/bookmarks
|
|
```
|
|
|
|
---
|
|
|
|
## Admin Features Phase
|
|
|
|
### Task 36: Admin Statistics Dashboard
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create `routes/admin.routes.js`
|
|
- [ ] Create `controllers/admin.controller.js`
|
|
- [ ] Implement `getSystemStatistics` function
|
|
- [ ] Count total users
|
|
- [ ] Count active users (last 7 days)
|
|
- [ ] Count total quiz sessions
|
|
- [ ] Get popular categories
|
|
- [ ] Calculate average score
|
|
- [ ] Get user growth data
|
|
- [ ] Add authorization (admin only)
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/admin/statistics
|
|
```
|
|
|
|
#### Reference:
|
|
See `SEQUELIZE_QUICK_REFERENCE.md` - Admin Operations
|
|
|
|
---
|
|
|
|
### Task 37: Guest Settings Management
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getGuestSettings` function
|
|
- [ ] Implement `updateGuestSettings` function
|
|
- [ ] Validate settings (max quizzes, expiry hours)
|
|
- [ ] Update public categories list
|
|
- [ ] Update feature restrictions
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoints:
|
|
```
|
|
GET /api/admin/guest-settings
|
|
PUT /api/admin/guest-settings
|
|
```
|
|
|
|
---
|
|
|
|
### Task 38: User Management (Admin)
|
|
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getAllUsers` function (paginated)
|
|
- [ ] Implement `getUserById` function
|
|
- [ ] Implement `updateUserRole` function
|
|
- [ ] Implement `deactivateUser` function
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoints:
|
|
```
|
|
GET /api/admin/users
|
|
GET /api/admin/users/:userId
|
|
PUT /api/admin/users/:userId/role
|
|
DELETE /api/admin/users/:userId
|
|
```
|
|
|
|
---
|
|
|
|
### Task 39: Guest Analytics
|
|
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Implement `getGuestAnalytics` function
|
|
- [ ] Count total guest sessions
|
|
- [ ] Calculate guest-to-user conversion rate
|
|
- [ ] Average quizzes taken before conversion
|
|
- [ ] Guest bounce rate
|
|
- [ ] Write tests
|
|
|
|
#### API Endpoint:
|
|
```
|
|
GET /api/admin/guest-analytics
|
|
```
|
|
|
|
---
|
|
|
|
## Testing & Optimization Phase
|
|
|
|
### Task 40: Unit Tests
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 5 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Setup Jest testing framework
|
|
- [ ] Write tests for auth controllers
|
|
- [ ] Write tests for quiz controllers
|
|
- [ ] Write tests for user controllers
|
|
- [ ] Write tests for admin controllers
|
|
- [ ] Mock database calls
|
|
- [ ] Achieve 80%+ code coverage
|
|
|
|
---
|
|
|
|
### Task 41: Integration Tests
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 4 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Setup Supertest for API testing
|
|
- [ ] Test complete registration flow
|
|
- [ ] Test complete quiz flow (start -> answer -> complete)
|
|
- [ ] Test guest to user conversion
|
|
- [ ] Test authorization scenarios
|
|
- [ ] Test error scenarios
|
|
|
|
---
|
|
|
|
### Task 42: API Documentation
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Install Swagger/OpenAPI
|
|
- [ ] Document all endpoints
|
|
- [ ] Add request/response examples
|
|
- [ ] Add authentication details
|
|
- [ ] Generate interactive API docs
|
|
- [ ] Host at `/api-docs`
|
|
|
|
---
|
|
|
|
### Task 43: Error Handling & Logging
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create centralized error handler middleware
|
|
- [ ] Handle Sequelize errors gracefully
|
|
- [ ] Setup logging with Winston/Morgan
|
|
- [ ] Log all requests (development)
|
|
- [ ] Log errors with stack traces
|
|
- [ ] Setup log rotation
|
|
|
|
---
|
|
|
|
### Task 44: Rate Limiting & Security
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Install express-rate-limit
|
|
- [ ] Add rate limiting to auth endpoints
|
|
- [ ] Add rate limiting to API endpoints
|
|
- [ ] Setup Helmet for security headers
|
|
- [ ] Add input sanitization
|
|
- [ ] Add CORS configuration
|
|
- [ ] Test security measures
|
|
|
|
---
|
|
|
|
### Task 45: Database Optimization
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Review and optimize all queries
|
|
- [ ] Add missing indexes
|
|
- [ ] Implement query result caching (Redis)
|
|
- [ ] Use eager loading to avoid N+1 queries
|
|
- [ ] Optimize full-text search queries
|
|
- [ ] Run EXPLAIN on complex queries
|
|
- [ ] Benchmark query performance
|
|
|
|
---
|
|
|
|
### Task 46: Performance Testing
|
|
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Setup load testing tool (Apache JMeter or Artillery)
|
|
- [ ] Test concurrent user scenarios
|
|
- [ ] Test database under load
|
|
- [ ] Monitor response times
|
|
- [ ] Identify bottlenecks
|
|
- [ ] Optimize as needed
|
|
|
|
---
|
|
|
|
## Deployment Preparation Phase
|
|
|
|
### Task 47: Docker Configuration
|
|
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create `Dockerfile` for backend
|
|
- [ ] Create `docker-compose.yml` with MySQL service
|
|
- [ ] Configure environment variables for Docker
|
|
- [ ] Test Docker build and run
|
|
- [ ] Create docker-compose for development
|
|
|
|
---
|
|
|
|
### Task 48: CI/CD Setup
|
|
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 3 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create GitHub Actions workflow
|
|
- [ ] Setup automated testing on PR
|
|
- [ ] Setup automated deployment (staging)
|
|
- [ ] Add environment secrets
|
|
- [ ] Test CI/CD pipeline
|
|
|
|
---
|
|
|
|
### Task 49: Production Configuration
|
|
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] Create production environment config
|
|
- [ ] Setup connection pooling for production
|
|
- [ ] Configure SSL for database connection
|
|
- [ ] Setup monitoring (New Relic/DataDog)
|
|
- [ ] Configure backup strategy
|
|
- [ ] Create deployment documentation
|
|
|
|
---
|
|
|
|
### Task 50: Final Testing & Documentation
|
|
**Priority**: High | **Status**: Not Started | **Estimated Time**: 4 hours
|
|
|
|
#### Subtasks:
|
|
- [ ] End-to-end testing of all features
|
|
- [ ] Create API usage examples
|
|
- [ ] Write README.md with setup instructions
|
|
- [ ] Document environment variables
|
|
- [ ] Create troubleshooting guide
|
|
- [ ] Code review and refactoring
|
|
- [ ] Performance optimization
|
|
- [ ] Security audit
|
|
|
|
---
|
|
|
|
## Task Summary
|
|
|
|
**Total Tasks**: 50
|
|
**Estimated Total Time**: 100-120 hours (2-3 months part-time)
|
|
|
|
### Priority Breakdown:
|
|
- **High Priority**: 25 tasks (Core functionality)
|
|
- **Medium Priority**: 18 tasks (Important features)
|
|
- **Low Priority**: 7 tasks (Nice to have)
|
|
|
|
### Phase Breakdown:
|
|
1. **Project Setup**: Tasks 1-3 (4-6 hours)
|
|
2. **Database Schema**: Tasks 4-10 (15-20 hours)
|
|
3. **Authentication**: Tasks 11-14 (8-10 hours)
|
|
4. **Guest Management**: Tasks 15-17 (6-8 hours)
|
|
5. **Category Management**: Tasks 18-20 (6-8 hours)
|
|
6. **Question Management**: Tasks 21-25 (10-12 hours)
|
|
7. **Quiz Sessions**: Tasks 26-30 (11-14 hours)
|
|
8. **User Dashboard**: Tasks 31-33 (7-9 hours)
|
|
9. **Bookmarks**: Tasks 34-35 (3-4 hours)
|
|
10. **Admin Features**: Tasks 36-39 (10-12 hours)
|
|
11. **Testing & Optimization**: Tasks 40-46 (21-25 hours)
|
|
12. **Deployment**: Tasks 47-50 (11-13 hours)
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
### Recommended Order:
|
|
1. Start with **Task 1** (Project Setup)
|
|
2. Complete **Tasks 2-3** (Database & Environment)
|
|
3. Work through **Tasks 4-10** (Database Schema) sequentially
|
|
4. Then proceed with feature development in order
|
|
|
|
### Daily Workflow:
|
|
1. Pick a task based on priority
|
|
2. Read the task requirements and references
|
|
3. Implement the feature
|
|
4. Write tests
|
|
5. Update task status
|
|
6. Commit with descriptive message
|
|
7. Move to next task
|
|
|
|
---
|
|
|
|
**Good luck with the development! 🚀**
|