REFACTOR_SUMMARY.md 9.8 KB

API Refactor Summary

Overview

This refactor modernizes the API response handling, error management, security, and observability features of the box-mgnt-api application.

Changes Implemented

1. ✅ Unified API Response Interface

Location: libs/common/src/interfaces/api-response.interface.ts

New Response Format:

interface ApiResponse<T> {
  success: boolean;
  code: string;
  message: string;
  data: T;
  timestamp: string;
}

Before:

{
  "error": "",
  "status": 1,
  "data": { ... }
}

After:

{
  "success": true,
  "code": "OK",
  "message": "success",
  "data": { ... },
  "timestamp": "2025-11-20T10:30:00.000Z"
}

Benefits:

  • Type-safe generic response with ApiResponse<T>
  • Clearer success/error semantics
  • Standardized error codes for client handling
  • Timestamp for audit trails

2. ✅ HTTP Status Code Preservation

Location: libs/common/src/filters/http-exception.filter.ts

Key Changes:

  • Replaced AllExceptionsFilter (always returned HTTP 200)
  • New HttpExceptionFilter preserves actual HTTP status codes
  • Maps status codes to semantic error codes

Status Code Mapping:

  • 400BAD_REQUEST
  • 401UNAUTHORIZED
  • 403FORBIDDEN
  • 404NOT_FOUND
  • 429RATE_LIMITED
  • 500INTERNAL_ERROR

Example Error Response:

HTTP/1.1 401 Unauthorized
{
  "success": false,
  "code": "UNAUTHORIZED",
  "message": "User not authenticated",
  "data": null,
  "timestamp": "2025-11-20T10:30:00.000Z"
}

3. ✅ Environment Configuration Validation

Location: apps/box-mgnt-api/src/config/env.validation.ts

Validated Variables:

  • MYSQL_URL - MySQL connection string (required, URL format)
  • MONGO_URL - MongoDB connection string (required, URL format)
  • JWT_SECRET - Secret for signing JWTs (required)
  • JWT_EXPIRES_IN_SECONDS - Token TTL (optional, 60-86400, default: 43200)
  • NODE_ENV - Environment (optional, enum: development/production/test)
  • PORT - Server port (optional, 1024-65535, default: 3000)
  • ENCRYPTION_KEY - For 2FA encryption (optional)

Benefits:

  • App fails fast on startup with invalid config
  • Clear error messages for missing/invalid values
  • Type-safe environment variables

Usage in app.module.ts:

ConfigModule.forRoot({
  isGlobal: true,
  envFilePath: ['.env.mgnt.dev', '.env'],
  validate: validateEnvironment, // ← Added
});

4. ✅ Correlation ID Tracking

Location: libs/common/src/interceptors/correlation.interceptor.ts

How it Works:

  • Extracts x-request-id or x-correlation-id from incoming headers
  • Generates UUID if not present
  • Attaches to request object as req.correlationId
  • Returns in response headers

Response Headers:

x-request-id: 550e8400-e29b-41d4-a716-446655440000
x-correlation-id: 550e8400-e29b-41d4-a716-446655440000

Benefits:

  • End-to-end request tracing
  • Easier debugging across distributed services
  • Log aggregation by correlation ID

5. ✅ Rate Limiting Guard

Location: libs/common/src/guards/rate-limit.guard.ts

Configuration:

  • Limit: 10 requests per window
  • Window: 60 seconds (1 minute)
  • Scope: Per IP + endpoint combination
  • Storage: In-memory (production should use Redis)

Applied to Endpoints:

  • POST /auth/login
  • POST /auth/login2fa
  • POST /auth/oauth-login
  • POST /auth/oauth-login-2fa
  • POST /auth/2fa/setup
  • POST /auth/2fa/enable

Rate Limit Response:

HTTP/1.1 429 Too Many Requests
{
  "success": false,
  "code": "RATE_LIMITED",
  "message": "Too many requests. Please try again in 45 seconds.",
  "data": null,
  "timestamp": "2025-11-20T10:30:00.000Z"
}

Features:

  • Automatic cleanup of expired buckets
  • Real IP detection (handles x-forwarded-for, x-real-ip)
  • Detailed logging of rate limit violations

6. ✅ MFA Guard (Separated)

Location: libs/common/src/guards/mfa.guard.ts

Purpose: Enforce MFA verification for protected endpoints

Logic:

  1. Check if user has 2FA enabled (user.twoFA field)
  2. If enabled, require req.mfaVerified === true
  3. Throw UNAUTHORIZED with code MFA_REQUIRED if not verified

Usage Example:

@UseGuards(JwtAuthGuard, MfaGuard)
@Get('sensitive-data')
async getSensitiveData(@AuthUser() user: User) {
  // Only accessible after MFA verification
}

Updated Module Wiring

CommonModule (libs/common/src/common.module.ts)

New Interceptors Order:

  1. CorrelationInterceptor (first - adds request ID)
  2. LoggingInterceptor (logs with correlation ID)
  3. OperationLogInterceptor (business operation logging)
  4. ResponseInterceptor (wraps response in ApiResponse)

New Filter:

  • HttpExceptionFilter (replaces AllExceptionsFilter)

Migration Notes

Breaking Changes

Response Format

Before:

interface HttpResponse {
  error: string;
  status: 1 | 0;
  data: unknown | null;
}

After:

interface ApiResponse<T> {
  success: boolean;
  code: string;
  message: string;
  data: T;
  timestamp: string;
}

Frontend Update Required:

// Before
if (response.status === 1) {
  /* success */
}

// After
if (response.success) {
  /* success */
}

HTTP Status Codes

  • Errors now return proper HTTP status codes (401, 403, 404, etc.)
  • Frontend error handling should check response.status (HTTP) not response.body.status

Environment Variables

  • Add required env vars: MYSQL_URL, MONGO_URL, JWT_SECRET
  • Optional but recommended: JWT_EXPIRES_IN_SECONDS, ENCRYPTION_KEY

Testing Recommendations

1. Test Rate Limiting

# Should succeed first 10 times, then fail with 429
for i in {1..15}; do
  curl -X POST http://localhost:3000/mgnt/auth/login \
    -H "Content-Type: application/json" \
    -d '{"username":"test","password":"test"}'
  echo "\nRequest $i"
  sleep 1
done

2. Test Correlation IDs

# Send request with custom correlation ID
curl -H "x-request-id: my-custom-id-123" \
  http://localhost:3000/mgnt/auth/permission

# Check response headers contain same ID

3. Test Error Responses

# Should return 401 with proper format
curl -X GET http://localhost:3000/mgnt/users/me

# Response:
# HTTP/1.1 401 Unauthorized
# { "success": false, "code": "UNAUTHORIZED", ... }

4. Test Config Validation

# Remove JWT_SECRET from .env and restart
# Should fail with clear error message
pnpm start:mgnt
# Error: Environment validation failed:
# JWT_SECRET should not be empty

Performance Optimizations

Parallel Queries in AuthService

Already optimized in login() method:

const [roleIds, userMenus] = await Promise.all([
  this.userService.getUserRoleIds(user.id, true),
  this.userService.getUserMenus(user.id),
]);

Recommended Next Steps

  1. Add database indexes:

    CREATE INDEX idx_user_role_user_id ON sys_user_role(user_id);
    CREATE INDEX idx_user_role_role_id ON sys_user_role(role_id);
    CREATE INDEX idx_role_menu_role_id ON sys_role_menu(role_id);
    CREATE INDEX idx_menu_parent_id ON sys_menu(parent_id);
    
  2. Add caching layer for frequently accessed data:

    • User roles
    • Menu permissions
    • API permissions
  3. Implement Redis for rate limiting (production):

    // Replace in-memory Map with Redis
    await redis.incr(`ratelimit:${key}`);
    await redis.expire(`ratelimit:${key}`, windowSeconds);
    

Monitoring & Observability

Logging Enhancements

  • All errors now include correlation IDs
  • Rate limit violations logged at WARN level
  • Server errors (5xx) logged at ERROR level with stack traces

Metrics Recommendations

Add Prometheus metrics for:

  • Request count by endpoint
  • Request duration histogram
  • Rate limit hit count
  • MFA verification success/failure
  • Login success/failure by type

Security Improvements

  1. Rate Limiting: Prevents brute force attacks on login endpoints
  2. MFA Guard: Enforces 2FA verification for sensitive operations
  3. Config Validation: Prevents running with insecure defaults
  4. Correlation IDs: Enables security event tracking and forensics
  5. Proper HTTP Status Codes: Doesn't leak information (no more HTTP 200 for errors)

Files Created/Modified

Created Files

  • libs/common/src/interfaces/api-response.interface.ts
  • libs/common/src/filters/http-exception.filter.ts
  • libs/common/src/interceptors/correlation.interceptor.ts
  • libs/common/src/guards/rate-limit.guard.ts
  • libs/common/src/guards/mfa.guard.ts
  • apps/box-mgnt-api/src/config/env.validation.ts

Modified Files

  • libs/common/src/common.module.ts (wiring)
  • libs/common/src/interceptors/response.interceptor.ts (new format)
  • libs/common/src/interceptors/operation-log.interceptor.ts (type update)
  • libs/common/src/services/exception.service.ts (new format)
  • apps/box-mgnt-api/src/app.module.ts (config validation)
  • apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.controller.ts (rate limiting)

Rollback Plan

If issues arise, revert these commits in order:

  1. Remove rate limiting from auth controller
  2. Remove correlation interceptor from CommonModule
  3. Restore AllExceptionsFilter in CommonModule
  4. Restore old response format in ResponseInterceptor
  5. Remove config validation from app.module.ts

Questions or Issues?

Check the following if something doesn't work:

  1. Build errors: Run pnpm install and pnpm prisma:generate
  2. Type errors: Ensure tsconfig.base.json includes new paths
  3. Runtime errors: Check .env file has all required variables
  4. Rate limiting not working: Verify RateLimitGuard is before LocalAuthGuard
  5. Correlation IDs missing: Check CorrelationInterceptor is first in provider list