@reldens/server-utils 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/api-reference.md +333 -0
- package/.claude/package-architecture.md +177 -0
- package/CLAUDE.md +46 -214
- package/lib/app-server-factory/cors-configurer.js +10 -0
- package/lib/app-server-factory/development-mode-detector.js +25 -0
- package/lib/app-server-factory/protocol-enforcer.js +11 -0
- package/lib/app-server-factory/rate-limit-configurer.js +10 -0
- package/lib/app-server-factory/reverse-proxy-configurer.js +10 -0
- package/lib/app-server-factory/security-configurer.js +18 -0
- package/lib/app-server-factory.js +86 -10
- package/lib/cdn-request-handler.js +130 -0
- package/lib/event-dispatcher.js +26 -0
- package/lib/http2-cdn-server.js +110 -139
- package/lib/request-logger.js +42 -0
- package/package.json +2 -2
package/CLAUDE.md
CHANGED
|
@@ -21,242 +21,74 @@ npm test
|
|
|
21
21
|
|
|
22
22
|
## Architecture
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
See `.claude/package-architecture.md` for detailed information about:
|
|
25
|
+
- Design philosophy and independence principles
|
|
26
|
+
- Callback-based extensibility pattern
|
|
27
|
+
- Available callbacks (onError, onRequestSuccess, onRequestError, onEvent)
|
|
28
|
+
- Integration patterns
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
- Singleton wrapper for Node.js fs and path modules
|
|
28
|
-
- Used instead of direct require('fs') or require('path') throughout Reldens
|
|
29
|
-
- Key file operations:
|
|
30
|
-
- `exists(filePath)` - Check if file/directory exists
|
|
31
|
-
- `readFile(filePath)` - Read file contents
|
|
32
|
-
- `writeFile(filePath, content)` - Write file
|
|
33
|
-
- `createFolder(folderPath)` - Create directory recursively
|
|
34
|
-
- `remove(fullPath)` - Remove file/folder recursively
|
|
35
|
-
- `copyFile(from, to)` - Copy file
|
|
36
|
-
- `copyFolderSync(from, to)` - Copy folder recursively
|
|
37
|
-
- `moveFile(from, to)` - Move/rename file
|
|
38
|
-
- Path operations:
|
|
39
|
-
- `joinPaths(...paths)` - Join path segments
|
|
40
|
-
- `getFileName(filePath)` - Get file name (basename)
|
|
41
|
-
- `getFolderName(filePath)` - Get directory name (dirname)
|
|
42
|
-
- `getRelativePath(from, to)` - Calculate relative path
|
|
43
|
-
- `normalizePath(filePath)` - Normalize path
|
|
44
|
-
- `isAbsolutePath(filePath)` - Check if path is absolute
|
|
45
|
-
- File inspection:
|
|
46
|
-
- `isFile(filePath)` - Check if path is a file
|
|
47
|
-
- `isFolder(dirPath)` - Check if path is a folder
|
|
48
|
-
- `getFileSize(filePath)` - Get file size in bytes
|
|
49
|
-
- `getFileStats(filePath)` - Get file stats
|
|
50
|
-
- `getFilesInFolder(dirPath, extensions)` - List files with optional filtering
|
|
51
|
-
- `fetchSubFoldersList(folder, options)` - Get subfolder list
|
|
52
|
-
- JSON operations:
|
|
53
|
-
- `fetchFileJson(filePath)` - Read and parse JSON file
|
|
54
|
-
- `isValidJson(filePath)` - Validate JSON file
|
|
55
|
-
- Security features:
|
|
56
|
-
- `generateSecureFilename(originalName)` - Generate secure random filename
|
|
57
|
-
- `validateFileType(filePath, type, allowedTypes, maxSize)` - Validate file
|
|
58
|
-
- `detectFileType(filePath)` - Detect MIME type from magic numbers
|
|
59
|
-
- `quarantineFile(filePath, reason)` - Move suspicious file to quarantine
|
|
60
|
-
- `isValidPath(filePath)` - Validate path for security
|
|
61
|
-
- `sanitizePath(filePath)` - Sanitize path string
|
|
62
|
-
- Advanced operations:
|
|
63
|
-
- `walkDirectory(dirPath, callback)` - Recursively process directory tree
|
|
64
|
-
- `getDirectorySize(dirPath)` - Calculate total directory size
|
|
65
|
-
- `emptyDirectory(dirPath)` - Remove all contents from directory
|
|
66
|
-
- `compareFiles(file1, file2)` - Compare file contents
|
|
67
|
-
- `appendToFile(filePath, content)` - Append to file
|
|
68
|
-
- `prependToFile(filePath, content)` - Prepend to file
|
|
69
|
-
- `replaceInFile(filePath, searchValue, replaceValue)` - Replace in file
|
|
70
|
-
- `createReadStream(filePath, options)` - Create readable stream for file
|
|
71
|
-
- `getFileModificationTime(filePath)` - Get file last modification time
|
|
72
|
-
- All methods include built-in error handling, no need for try/catch
|
|
73
|
-
- DO NOT use FileHandler.exists to validate other FileHandler methods
|
|
74
|
-
- DO NOT enclose FileHandler methods in try/catch blocks
|
|
75
|
-
|
|
76
|
-
**AppServerFactory** (`lib/app-server-factory.js`):
|
|
77
|
-
- Creates Express app servers with modular security components
|
|
78
|
-
- Supports HTTP, HTTPS, HTTP/2
|
|
79
|
-
- Key features:
|
|
80
|
-
- Virtual host management with SNI support
|
|
81
|
-
- HTTP/2 CDN server integration
|
|
82
|
-
- Reverse proxy with WebSocket support
|
|
83
|
-
- Development mode detection and configuration
|
|
84
|
-
- Custom error handling via onError callback
|
|
85
|
-
- Security configurers (modular):
|
|
86
|
-
- `DevelopmentModeDetector` - Auto-detect development environment
|
|
87
|
-
- `ProtocolEnforcer` - HTTP/HTTPS protocol enforcement
|
|
88
|
-
- `SecurityConfigurer` - Helmet integration and XSS protection
|
|
89
|
-
- `CorsConfigurer` - CORS with dynamic origin validation
|
|
90
|
-
- `RateLimitConfigurer` - Global and endpoint-specific rate limiting
|
|
91
|
-
- `ReverseProxyConfigurer` - Domain-based reverse proxy
|
|
92
|
-
- Configuration properties:
|
|
93
|
-
- `onError` - Custom error handler callback for server errors
|
|
94
|
-
- Methods:
|
|
95
|
-
- `createAppServer(config)` - Create and configure server
|
|
96
|
-
- `addDomain(domainConfig)` - Add virtual host domain
|
|
97
|
-
- `addDevelopmentDomain(domain)` - Add development domain
|
|
98
|
-
- `enableServeHome(app, callback)` - Enable homepage serving
|
|
99
|
-
- `serveStatics(app, staticPath)` - Serve static files
|
|
100
|
-
- `enableCSP(cspOptions)` - Enable Content Security Policy
|
|
101
|
-
- `listen(port)` - Start server listening
|
|
102
|
-
- `close()` - Gracefully close server
|
|
30
|
+
See `.claude/api-reference.md` for complete API documentation of all classes and methods.
|
|
103
31
|
|
|
104
|
-
|
|
105
|
-
- Singleton for cryptographic operations
|
|
106
|
-
- Password hashing:
|
|
107
|
-
- `encryptPassword(password)` - Hash password with PBKDF2 (100k iterations, SHA-512)
|
|
108
|
-
- `validatePassword(password, storedPassword)` - Validate password against hash
|
|
109
|
-
- Data encryption:
|
|
110
|
-
- `encryptData(data, key)` - Encrypt data with AES-256-GCM
|
|
111
|
-
- `decryptData(encryptedData, key)` - Decrypt AES-256-GCM data
|
|
112
|
-
- `generateSecretKey()` - Generate 256-bit secret key
|
|
113
|
-
- Token generation:
|
|
114
|
-
- `generateSecureToken(length)` - Generate cryptographically secure token
|
|
115
|
-
- `generateTOTP(secret, timeStep)` - Generate time-based OTP
|
|
116
|
-
- Hashing and verification:
|
|
117
|
-
- `hashData(data, algorithm)` - Hash with SHA-256, SHA-512, or MD5
|
|
118
|
-
- `generateHMAC(data, secret, algorithm)` - Generate HMAC signature
|
|
119
|
-
- `verifyHMAC(data, secret, signature, algorithm)` - Verify HMAC signature
|
|
120
|
-
- `constantTimeCompare(a, b)` - Constant-time string comparison
|
|
32
|
+
## Core Classes Summary
|
|
121
33
|
|
|
122
|
-
**
|
|
123
|
-
- File upload handling with Multer
|
|
124
|
-
- Multi-level security validation:
|
|
125
|
-
- Filename security validation
|
|
126
|
-
- MIME type validation
|
|
127
|
-
- File extension validation
|
|
128
|
-
- File size validation
|
|
129
|
-
- Content validation using magic numbers
|
|
130
|
-
- Dangerous extension filtering
|
|
131
|
-
- Features:
|
|
132
|
-
- Multiple file upload support with field mapping
|
|
133
|
-
- Secure filename generation option
|
|
134
|
-
- Automatic cleanup on validation failure
|
|
135
|
-
- Custom error response handling
|
|
136
|
-
- Per-field destination mapping
|
|
137
|
-
- Methods:
|
|
138
|
-
- `createUploader(fields, buckets, allowedFileTypes)` - Create upload middleware
|
|
139
|
-
- `validateFilenameSecurity(filename)` - Validate filename
|
|
140
|
-
- `validateFile(file, allowedType, callback)` - Validate during upload
|
|
141
|
-
- `validateFileContents(file, allowedType)` - Validate after upload
|
|
142
|
-
- `convertToRegex(key)` - Convert MIME type patterns to regex
|
|
34
|
+
**FileHandler** - Singleton wrapper for Node.js fs and path modules. Used instead of direct require('fs') or require('path') throughout Reldens.
|
|
143
35
|
|
|
144
|
-
**
|
|
145
|
-
- HTTP/2 secure server for CDN-like static file serving
|
|
146
|
-
- Features:
|
|
147
|
-
- Optimized for CSS, JavaScript, images, and fonts
|
|
148
|
-
- Dynamic CORS origin validation with regex pattern support
|
|
149
|
-
- Configurable cache headers per file extension
|
|
150
|
-
- Comprehensive MIME type detection
|
|
151
|
-
- HTTP/1.1 fallback support
|
|
152
|
-
- Security headers (X-Content-Type-Options, X-Frame-Options, Vary)
|
|
153
|
-
- Query string stripping for cache optimization
|
|
154
|
-
- Comprehensive error handling (server, TLS, session, stream errors)
|
|
155
|
-
- Custom error handler callback support
|
|
156
|
-
- Configuration properties:
|
|
157
|
-
- `onError` - Custom error handler callback for server errors
|
|
158
|
-
- Methods:
|
|
159
|
-
- `create()` - Create HTTP/2 secure server
|
|
160
|
-
- `listen()` - Start listening on configured port
|
|
161
|
-
- `close()` - Gracefully close server
|
|
162
|
-
- `handleStream(stream, headers)` - Handle HTTP/2 stream
|
|
163
|
-
- `handleHttp1Request(req, res)` - Handle HTTP/1.1 fallback requests
|
|
164
|
-
- `resolveFilePath(requestPath)` - Resolve file path from request
|
|
165
|
-
- `setupEventHandlers()` - Configure server error event handlers
|
|
36
|
+
**AppServerFactory** - Creates Express app servers with modular security components. Supports HTTP, HTTPS, HTTP/2, virtual hosts, CDN integration, reverse proxy, and callback-based logging.
|
|
166
37
|
|
|
167
|
-
|
|
38
|
+
**Encryptor** - Singleton for cryptographic operations including password hashing, data encryption, token generation, and HMAC verification.
|
|
168
39
|
|
|
169
|
-
**
|
|
170
|
-
- Centralized error handling for all server components
|
|
171
|
-
- Static class with standardized error handling interface
|
|
172
|
-
- Methods:
|
|
173
|
-
- `handleError(onErrorCallback, instanceName, instance, key, error, context)` - Handle and delegate errors
|
|
174
|
-
- Used by Http2CdnServer, AppServerFactory, and ReverseProxyConfigurer
|
|
175
|
-
- Supports custom error callbacks for application-specific error processing
|
|
176
|
-
- Provides structured error context with instance details and metadata
|
|
40
|
+
**UploaderFactory** - File upload handling with Multer and multi-level security validation.
|
|
177
41
|
|
|
178
|
-
**
|
|
179
|
-
- Static class providing default configurations
|
|
180
|
-
- `mimeTypes` - Comprehensive MIME type mappings for common file extensions
|
|
181
|
-
- `cacheConfig` - Default cache max-age settings (1 year for CSS/JS/fonts, 30 days for images)
|
|
42
|
+
**Http2CdnServer** - HTTP/2 secure server for CDN-like static file serving with CORS, cache headers, and callback-based logging.
|
|
182
43
|
|
|
183
|
-
|
|
184
|
-
- Static utility methods for server operations
|
|
185
|
-
- `getCacheConfigForPath(path, cacheConfig)` - Get cache config for file path
|
|
186
|
-
- `validateOrigin(origin, corsOrigins, corsAllowAll)` - Validate CORS origin (supports strings and RegExp)
|
|
187
|
-
- `stripQueryString(url)` - Remove query string from URL
|
|
44
|
+
## Utility Classes Summary
|
|
188
45
|
|
|
189
|
-
**
|
|
190
|
-
- Centralized header management
|
|
191
|
-
- HTTP/2 headers configuration
|
|
192
|
-
- Express security headers
|
|
193
|
-
- Cache control headers
|
|
194
|
-
- Proxy forwarding headers
|
|
195
|
-
- `buildCacheControlHeader(maxAge)` - Build cache control header string
|
|
46
|
+
**RequestLogger** - Express middleware for request logging that invokes callbacks based on status codes.
|
|
196
47
|
|
|
197
|
-
|
|
48
|
+
**EventDispatcher** - Static utility for dispatching lifecycle events with structured event data.
|
|
198
49
|
|
|
199
|
-
**
|
|
200
|
-
- Auto-detects development environment
|
|
201
|
-
- Checks NODE_ENV, domain patterns, and configured domains
|
|
202
|
-
- Built-in patterns: localhost, 127.0.0.1, .local, .test, .dev, .staging, etc.
|
|
203
|
-
- `detect(config)` - Detect if in development mode
|
|
204
|
-
- `matchesPattern(domain)` - Check if domain matches development pattern
|
|
50
|
+
**ServerErrorHandler** - Centralized error handling with structured error context.
|
|
205
51
|
|
|
206
|
-
**
|
|
207
|
-
- Enforces HTTP/HTTPS protocol consistency
|
|
208
|
-
- Development mode awareness
|
|
209
|
-
- Automatic redirects when protocol doesn't match configuration
|
|
210
|
-
- `setup(app, config)` - Setup protocol enforcement middleware
|
|
52
|
+
**ServerDefaultConfigurations** - Static class providing MIME types and cache configurations.
|
|
211
53
|
|
|
212
|
-
**
|
|
213
|
-
- Helmet integration with CSP management
|
|
214
|
-
- XSS protection with request body sanitization
|
|
215
|
-
- Development-friendly CSP configuration
|
|
216
|
-
- Supports CSP directive merging or override
|
|
217
|
-
- `setupHelmet(app, config)` - Setup Helmet middleware
|
|
218
|
-
- `setupXssProtection(app, config)` - Setup XSS protection
|
|
219
|
-
- `enableCSP(app, cspOptions)` - Enable Content Security Policy
|
|
220
|
-
- `addExternalDomainsToCsp(directives, externalDomains)` - Add external domains to CSP
|
|
54
|
+
**ServerFactoryUtils** - Static utility methods for cache config, CORS validation, and URL manipulation.
|
|
221
55
|
|
|
222
|
-
**
|
|
223
|
-
- Dynamic CORS origin validation
|
|
224
|
-
- Development domain support with automatic port variations
|
|
225
|
-
- Credential support configuration
|
|
226
|
-
- `setup(app, config)` - Setup CORS middleware
|
|
227
|
-
- `extractDevelopmentOrigins(domainMapping)` - Extract development origins
|
|
228
|
-
- `normalizeHost(originOrHost)` - Normalize host string
|
|
56
|
+
**ServerHeaders** - Centralized header management for HTTP/2, security, cache, and proxy headers.
|
|
229
57
|
|
|
230
|
-
|
|
231
|
-
- Global and endpoint-specific rate limiting
|
|
232
|
-
- Development mode multiplier for lenient limits
|
|
233
|
-
- IP-based key generation option
|
|
234
|
-
- `setup(app, config)` - Setup global rate limiting
|
|
235
|
-
- `createHomeLimiter()` - Create homepage-specific limiter
|
|
58
|
+
## Security Configurers Summary
|
|
236
59
|
|
|
237
|
-
|
|
238
|
-
-
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
-
|
|
244
|
-
- 502 Bad Gateway for ECONNREFUSED errors
|
|
245
|
-
- 504 Gateway Timeout for ETIMEDOUT/ESOCKETTIMEDOUT errors
|
|
246
|
-
- 500 Internal Server Error for other proxy errors
|
|
247
|
-
- Custom error callback support via ServerErrorHandler
|
|
248
|
-
- Methods:
|
|
249
|
-
- `setup(app, config)` - Setup reverse proxy
|
|
250
|
-
- `createProxyMiddleware(rule)` - Create proxy middleware for rule
|
|
251
|
-
- `handleProxyError(err, req, res)` - Handle proxy errors with status codes
|
|
252
|
-
- `validateProxyRule(rule)` - Validate proxy rule configuration
|
|
253
|
-
- `extractHostname(req)` - Extract hostname from request
|
|
60
|
+
Located in `lib/app-server-factory/`:
|
|
61
|
+
- **DevelopmentModeDetector** - Auto-detect development environment
|
|
62
|
+
- **ProtocolEnforcer** - HTTP/HTTPS protocol enforcement
|
|
63
|
+
- **SecurityConfigurer** - Helmet integration and XSS protection
|
|
64
|
+
- **CorsConfigurer** - CORS with dynamic origin validation
|
|
65
|
+
- **RateLimitConfigurer** - Global and endpoint-specific rate limiting
|
|
66
|
+
- **ReverseProxyConfigurer** - Domain-based reverse proxy with WebSocket support
|
|
254
67
|
|
|
255
68
|
## Important Notes
|
|
256
69
|
|
|
70
|
+
### FileHandler Usage
|
|
257
71
|
- **ALWAYS use FileHandler** instead of Node.js fs/path modules
|
|
258
72
|
- FileHandler methods have built-in error handling - no try/catch needed
|
|
259
73
|
- DO NOT enclose FileHandler methods in try/catch blocks
|
|
260
74
|
- DO NOT use FileHandler.exists to validate other FileHandler methods (e.g., before createFolder)
|
|
75
|
+
|
|
76
|
+
### Callback System
|
|
77
|
+
- Package provides callback hooks (onError, onRequestSuccess, onRequestError, onEvent)
|
|
78
|
+
- Applications provide implementations via configuration
|
|
79
|
+
- Package does NOT import @reldens/utils Logger
|
|
80
|
+
- Applications wire callbacks to their own logging/monitoring systems
|
|
81
|
+
|
|
82
|
+
### Server Configuration
|
|
261
83
|
- AppServerFactory handles all Express server configuration
|
|
262
84
|
- All server utilities support both HTTP and HTTPS
|
|
85
|
+
- Virtual hosts with SNI support for multiple domains
|
|
86
|
+
- HTTP/2 CDN server integration for static file serving
|
|
87
|
+
- Reverse proxy with WebSocket support
|
|
88
|
+
|
|
89
|
+
### Security
|
|
90
|
+
- Multi-level validation for file uploads
|
|
91
|
+
- XSS protection with request body sanitization
|
|
92
|
+
- CSP management with Helmet integration
|
|
93
|
+
- CORS with dynamic origin validation (supports strings and RegExp)
|
|
94
|
+
- Rate limiting with development mode awareness
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const cors = require('cors');
|
|
8
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
8
9
|
|
|
9
10
|
class CorsConfigurer
|
|
10
11
|
{
|
|
@@ -18,6 +19,7 @@ class CorsConfigurer
|
|
|
18
19
|
this.corsHeaders = ['Content-Type','Authorization'];
|
|
19
20
|
this.developmentCorsOrigins = [];
|
|
20
21
|
this.developmentPorts = [];
|
|
22
|
+
this.onEvent = null;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
setup(app, config)
|
|
@@ -28,6 +30,7 @@ class CorsConfigurer
|
|
|
28
30
|
this.corsMethods = config.corsMethods || this.corsMethods;
|
|
29
31
|
this.corsHeaders = config.corsHeaders || this.corsHeaders;
|
|
30
32
|
this.developmentPorts = config.developmentPorts || this.developmentPorts;
|
|
33
|
+
this.onEvent = config.onEvent || null;
|
|
31
34
|
if(!this.useCors){
|
|
32
35
|
return;
|
|
33
36
|
}
|
|
@@ -58,6 +61,13 @@ class CorsConfigurer
|
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
app.use(cors(corsOptions));
|
|
64
|
+
EventDispatcher.dispatch(
|
|
65
|
+
this.onEvent,
|
|
66
|
+
'cors-configured',
|
|
67
|
+
'corsConfigurer',
|
|
68
|
+
this,
|
|
69
|
+
{useCors: this.useCors, isDevelopmentMode: this.isDevelopmentMode, corsOrigin: this.corsOrigin}
|
|
70
|
+
);
|
|
61
71
|
}
|
|
62
72
|
|
|
63
73
|
extractDevelopmentOrigins(domainMapping)
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
8
|
+
|
|
7
9
|
class DevelopmentModeDetector
|
|
8
10
|
{
|
|
9
11
|
|
|
@@ -27,10 +29,12 @@ class DevelopmentModeDetector
|
|
|
27
29
|
];
|
|
28
30
|
this.developmentEnvironments = ['development', 'dev', 'test'];
|
|
29
31
|
this.env = process?.env?.NODE_ENV || 'production';
|
|
32
|
+
this.onEvent = null;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
detect(config = {})
|
|
33
36
|
{
|
|
37
|
+
this.onEvent = config.onEvent || null;
|
|
34
38
|
if(config.developmentPatterns){
|
|
35
39
|
this.developmentPatterns = config.developmentPatterns;
|
|
36
40
|
}
|
|
@@ -38,11 +42,25 @@ class DevelopmentModeDetector
|
|
|
38
42
|
this.developmentEnvironments = config.developmentEnvironments;
|
|
39
43
|
}
|
|
40
44
|
if(this.developmentEnvironments.includes(this.env)){
|
|
45
|
+
EventDispatcher.dispatch(
|
|
46
|
+
this.onEvent,
|
|
47
|
+
'development-mode-detected',
|
|
48
|
+
'developmentModeDetector',
|
|
49
|
+
this,
|
|
50
|
+
{detectionMethod: 'environment', env: this.env}
|
|
51
|
+
);
|
|
41
52
|
return true;
|
|
42
53
|
}
|
|
43
54
|
if(config.developmentDomains && 0 < config.developmentDomains.length){
|
|
44
55
|
for(let domain of config.developmentDomains){
|
|
45
56
|
if(this.matchesPattern(domain)){
|
|
57
|
+
EventDispatcher.dispatch(
|
|
58
|
+
this.onEvent,
|
|
59
|
+
'development-mode-detected',
|
|
60
|
+
'developmentModeDetector',
|
|
61
|
+
this,
|
|
62
|
+
{detectionMethod: 'developmentDomain', domain: domain}
|
|
63
|
+
);
|
|
46
64
|
return true;
|
|
47
65
|
}
|
|
48
66
|
}
|
|
@@ -53,6 +71,13 @@ class DevelopmentModeDetector
|
|
|
53
71
|
continue;
|
|
54
72
|
}
|
|
55
73
|
if(this.matchesPattern(domainConfig.hostname)){
|
|
74
|
+
EventDispatcher.dispatch(
|
|
75
|
+
this.onEvent,
|
|
76
|
+
'development-mode-detected',
|
|
77
|
+
'developmentModeDetector',
|
|
78
|
+
this,
|
|
79
|
+
{detectionMethod: 'domainPattern', hostname: domainConfig.hostname}
|
|
80
|
+
);
|
|
56
81
|
return true;
|
|
57
82
|
}
|
|
58
83
|
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
8
|
+
|
|
7
9
|
class ProtocolEnforcer
|
|
8
10
|
{
|
|
9
11
|
|
|
@@ -12,6 +14,7 @@ class ProtocolEnforcer
|
|
|
12
14
|
this.isDevelopmentMode = false;
|
|
13
15
|
this.useHttps = false;
|
|
14
16
|
this.enforceProtocol = true;
|
|
17
|
+
this.onEvent = null;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
setup(app, config)
|
|
@@ -19,6 +22,7 @@ class ProtocolEnforcer
|
|
|
19
22
|
this.isDevelopmentMode = config.isDevelopmentMode || false;
|
|
20
23
|
this.useHttps = config.useHttps || false;
|
|
21
24
|
this.enforceProtocol = config.enforceProtocol !== false;
|
|
25
|
+
this.onEvent = config.onEvent || null;
|
|
22
26
|
app.use((req, res, next) => {
|
|
23
27
|
let forwardedProto = req.get('X-Forwarded-Proto');
|
|
24
28
|
let protocol = (forwardedProto || req.protocol || '').toLowerCase();
|
|
@@ -41,6 +45,13 @@ class ProtocolEnforcer
|
|
|
41
45
|
res.set('X-Forwarded-Proto', protocol);
|
|
42
46
|
next();
|
|
43
47
|
});
|
|
48
|
+
EventDispatcher.dispatch(
|
|
49
|
+
this.onEvent,
|
|
50
|
+
'protocol-enforcement-enabled',
|
|
51
|
+
'protocolEnforcer',
|
|
52
|
+
this,
|
|
53
|
+
{isDevelopmentMode: this.isDevelopmentMode, useHttps: this.useHttps, enforceProtocol: this.enforceProtocol}
|
|
54
|
+
);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const rateLimit = require('express-rate-limit');
|
|
8
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
8
9
|
|
|
9
10
|
class RateLimitConfigurer
|
|
10
11
|
{
|
|
@@ -19,6 +20,7 @@ class RateLimitConfigurer
|
|
|
19
20
|
this.applyKeyGenerator = false;
|
|
20
21
|
this.tooManyRequestsMessage = 'Too many requests, please try again later.';
|
|
21
22
|
this.rateLimit = rateLimit;
|
|
23
|
+
this.onEvent = null;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
setup(app, config)
|
|
@@ -30,6 +32,7 @@ class RateLimitConfigurer
|
|
|
30
32
|
this.developmentMultiplier = Number(config.developmentMultiplier || this.developmentMultiplier);
|
|
31
33
|
this.applyKeyGenerator = config.applyKeyGenerator || false;
|
|
32
34
|
this.tooManyRequestsMessage = config.tooManyRequestsMessage || this.tooManyRequestsMessage;
|
|
35
|
+
this.onEvent = config.onEvent || null;
|
|
33
36
|
if(!this.globalRateLimit){
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
@@ -49,6 +52,13 @@ class RateLimitConfigurer
|
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
app.use(this.rateLimit(limiterParams));
|
|
55
|
+
EventDispatcher.dispatch(
|
|
56
|
+
this.onEvent,
|
|
57
|
+
'rate-limiting-configured',
|
|
58
|
+
'rateLimitConfigurer',
|
|
59
|
+
this,
|
|
60
|
+
{globalRateLimit: this.globalRateLimit, maxRequests: limiterParams.limit, windowMs: this.windowMs}
|
|
61
|
+
);
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
createHomeLimiter()
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
8
8
|
const { ServerHeaders } = require('../server-headers');
|
|
9
9
|
const { ServerErrorHandler } = require('../server-error-handler');
|
|
10
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
10
11
|
|
|
11
12
|
class ReverseProxyConfigurer
|
|
12
13
|
{
|
|
@@ -24,6 +25,7 @@ class ReverseProxyConfigurer
|
|
|
24
25
|
};
|
|
25
26
|
this.serverHeaders = new ServerHeaders();
|
|
26
27
|
this.onError = null;
|
|
28
|
+
this.onEvent = null;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
setup(app, config)
|
|
@@ -33,10 +35,18 @@ class ReverseProxyConfigurer
|
|
|
33
35
|
this.reverseProxyRules = config.reverseProxyRules || [];
|
|
34
36
|
this.reverseProxyOptions = config.reverseProxyOptions || this.reverseProxyOptions;
|
|
35
37
|
this.onError = config.onError || null;
|
|
38
|
+
this.onEvent = config.onEvent || null;
|
|
36
39
|
if(0 === this.reverseProxyRules.length){
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
this.applyRules(app);
|
|
43
|
+
EventDispatcher.dispatch(
|
|
44
|
+
this.onEvent,
|
|
45
|
+
'reverse-proxy-configured',
|
|
46
|
+
'reverseProxyConfigurer',
|
|
47
|
+
this,
|
|
48
|
+
{rulesCount: this.reverseProxyRules.length, useVirtualHosts: this.useVirtualHosts}
|
|
49
|
+
);
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
applyRules(app)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const helmet = require('helmet');
|
|
8
8
|
const sanitizeHtml = require('sanitize-html');
|
|
9
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
9
10
|
|
|
10
11
|
class SecurityConfigurer
|
|
11
12
|
{
|
|
@@ -18,15 +19,24 @@ class SecurityConfigurer
|
|
|
18
19
|
this.helmetConfig = false;
|
|
19
20
|
this.helmetOptions = {};
|
|
20
21
|
this.sanitizeOptions = {allowedTags: [], allowedAttributes: {}};
|
|
22
|
+
this.onEvent = null;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
setupHelmet(app, config)
|
|
24
26
|
{
|
|
25
27
|
this.useHelmet = config.useHelmet !== false;
|
|
28
|
+
this.onEvent = config.onEvent || null;
|
|
26
29
|
if(!this.useHelmet){
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
29
32
|
app.use(helmet(this.mapHelmetOptions(config)));
|
|
33
|
+
EventDispatcher.dispatch(
|
|
34
|
+
this.onEvent,
|
|
35
|
+
'helmet-configured',
|
|
36
|
+
'securityConfigurer',
|
|
37
|
+
this,
|
|
38
|
+
{useHelmet: this.useHelmet, isDevelopmentMode: this.isDevelopmentMode}
|
|
39
|
+
);
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
mapHelmetOptions(config)
|
|
@@ -131,6 +141,7 @@ class SecurityConfigurer
|
|
|
131
141
|
{
|
|
132
142
|
this.useXssProtection = config.useXssProtection !== false;
|
|
133
143
|
this.sanitizeOptions = config.sanitizeOptions || this.sanitizeOptions;
|
|
144
|
+
this.onEvent = config.onEvent || null;
|
|
134
145
|
if(!this.useXssProtection){
|
|
135
146
|
return;
|
|
136
147
|
}
|
|
@@ -143,6 +154,13 @@ class SecurityConfigurer
|
|
|
143
154
|
}
|
|
144
155
|
next();
|
|
145
156
|
});
|
|
157
|
+
EventDispatcher.dispatch(
|
|
158
|
+
this.onEvent,
|
|
159
|
+
'xss-protection-enabled',
|
|
160
|
+
'securityConfigurer',
|
|
161
|
+
this,
|
|
162
|
+
{useXssProtection: this.useXssProtection}
|
|
163
|
+
);
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
sanitizeRequestBody(body)
|