File size: 3,589 Bytes
c904036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
 * API Versioning Middleware
 * Provides versioned API routes for backward compatibility
 * 
 * Usage:
 * - Current version: /api/v1/...
 * - Legacy (unversioned): /api/... (redirects to v1)
 * 
 * Version Header Support:
 * - Accept-Version: v1
 * - X-API-Version: v1
 */

const { logger } = require('../Middleware/errorHandler');

// Supported API versions
const SUPPORTED_VERSIONS = ['v1'];
const DEFAULT_VERSION = 'v1';
const CURRENT_VERSION = 'v1';

/**
 * Extract API version from request
 * Priority: URL path > Accept-Version header > X-API-Version header > default
 */
function extractVersion(req) {
  // Check URL path for version (e.g., /api/v1/users)
  const pathMatch = req.path.match(/^\/api\/(v\d+)\//);
  if (pathMatch && SUPPORTED_VERSIONS.includes(pathMatch[1])) {
    return pathMatch[1];
  }
  
  // Check Accept-Version header
  const acceptVersion = req.headers['accept-version'];
  if (acceptVersion && SUPPORTED_VERSIONS.includes(acceptVersion)) {
    return acceptVersion;
  }
  
  // Check X-API-Version header
  const xApiVersion = req.headers['x-api-version'];
  if (xApiVersion && SUPPORTED_VERSIONS.includes(xApiVersion)) {
    return xApiVersion;
  }
  
  return DEFAULT_VERSION;
}

/**
 * API versioning middleware
 * Attaches version info to request and sets response headers
 */
function apiVersioning(req, res, next) {
  const version = extractVersion(req);
  
  // Attach version to request
  req.apiVersion = version;
  
  // Set response headers
  res.setHeader('X-API-Version', version);
  res.setHeader('X-API-Current-Version', CURRENT_VERSION);
  
  // Deprecation warning for old versions (when we have v2+)
  if (version !== CURRENT_VERSION) {
    res.setHeader('X-API-Deprecated', 'true');
    res.setHeader('Warning', '299 - "API version deprecated, please upgrade"');
  }
  
  next();
}

/**
 * Create versioned router
 * Wraps an Express router with version-specific path prefix
 */
function createVersionedRouter(app, version, router) {
  app.use(`/api/${version}`, router);
  logger.info(`Registered API ${version} routes`);
}

/**
 * Legacy route redirect middleware
 * Redirects unversioned /api/... to /api/v1/...
 * Only for routes that don't already have a version
 */
function legacyRouteHandler(req, res, next) {
  // Skip if already versioned
  if (req.path.match(/^\/api\/v\d+\//)) {
    return next();
  }
  
  // Skip static assets and special routes
  const skipPaths = ['/api/health', '/api/docs', '/healthz'];
  if (skipPaths.some(path => req.path.startsWith(path))) {
    return next();
  }
  
  // For now, just continue (backward compatible)
  // In future, can redirect or log deprecation warnings
  req.apiVersion = DEFAULT_VERSION;
  next();
}

/**
 * Version check middleware for specific minimum version requirement
 * @param {string} minVersion - Minimum required version (e.g., 'v2')
 */
function requireMinVersion(minVersion) {
  return (req, res, next) => {
    const currentVersionNum = parseInt(req.apiVersion?.replace('v', '') || '1', 10);
    const requiredVersionNum = parseInt(minVersion.replace('v', ''), 10);
    
    if (currentVersionNum < requiredVersionNum) {
      return res.status(400).json({
        success: false,
        error: `This endpoint requires API version ${minVersion} or higher`,
        currentVersion: req.apiVersion,
        requiredVersion: minVersion
      });
    }
    
    next();
  };
}

module.exports = {
  apiVersioning,
  createVersionedRouter,
  legacyRouteHandler,
  requireMinVersion,
  extractVersion,
  SUPPORTED_VERSIONS,
  DEFAULT_VERSION,
  CURRENT_VERSION
};