mastercontroller 1.3.13 → 1.3.15
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/MasterAction.js +302 -62
- package/MasterActionFilters.js +556 -82
- package/MasterControl.js +77 -44
- package/MasterCors.js +61 -19
- package/MasterPipeline.js +29 -6
- package/MasterRequest.js +579 -102
- package/MasterRouter.js +446 -75
- package/MasterSocket.js +380 -15
- package/MasterTemp.js +292 -10
- package/MasterTimeout.js +420 -64
- package/MasterTools.js +478 -77
- package/README.md +505 -0
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -29
- package/.github/workflows/ci.yml +0 -317
- package/PERFORMANCE_SECURITY_AUDIT.md +0 -677
- package/SENIOR_ENGINEER_AUDIT.md +0 -2477
- package/VERIFICATION_CHECKLIST.md +0 -726
- package/log/mastercontroller.log +0 -2
- package/test-json-empty-body.js +0 -76
- package/test-raw-body-preservation.js +0 -128
- package/test-v1.3.4-fixes.js +0 -129
package/MasterControl.js
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
// MasterControl - by Alexander rich
|
|
2
2
|
// version 1.0.252
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
const url = require('url');
|
|
5
|
+
const fileserver = require('fs');
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const tls = require('tls');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const globSearch = require("glob");
|
|
12
|
+
const crypto = require('crypto'); // CRITICAL FIX: For ETag generation
|
|
13
|
+
|
|
14
|
+
// HTTP Status Code Constants
|
|
15
|
+
const HTTP_STATUS = {
|
|
16
|
+
OK: 200,
|
|
17
|
+
MOVED_PERMANENTLY: 301,
|
|
18
|
+
FOUND: 302,
|
|
19
|
+
NOT_MODIFIED: 304,
|
|
20
|
+
BAD_REQUEST: 400,
|
|
21
|
+
FORBIDDEN: 403,
|
|
22
|
+
NOT_FOUND: 404,
|
|
23
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
24
|
+
INTERNAL_ERROR: 500
|
|
25
|
+
};
|
|
14
26
|
|
|
15
27
|
// Enhanced error handling - setup global handlers
|
|
16
28
|
const { setupGlobalErrorHandlers } = require('./error/MasterErrorMiddleware');
|
|
@@ -176,7 +188,10 @@ class MasterControl {
|
|
|
176
188
|
return false;
|
|
177
189
|
};
|
|
178
190
|
|
|
179
|
-
|
|
191
|
+
logger.info({
|
|
192
|
+
code: 'MC_INFO_PROTOTYPE_PROTECTION',
|
|
193
|
+
message: 'Prototype pollution protection initialized'
|
|
194
|
+
});
|
|
180
195
|
}
|
|
181
196
|
|
|
182
197
|
// extends class methods to be used inside of the view class using the THIS keyword
|
|
@@ -291,7 +306,7 @@ class MasterControl {
|
|
|
291
306
|
|
|
292
307
|
// Enhanced: Support both relative (to master.root) and absolute paths
|
|
293
308
|
// If folderLocation is absolute, use it directly; otherwise join with master.root
|
|
294
|
-
|
|
309
|
+
let rootFolderLocation;
|
|
295
310
|
if (path.isAbsolute(folderLocation)) {
|
|
296
311
|
// Absolute path provided - use it directly
|
|
297
312
|
rootFolderLocation = path.join(folderLocation, innerFolder);
|
|
@@ -301,16 +316,20 @@ class MasterControl {
|
|
|
301
316
|
}
|
|
302
317
|
|
|
303
318
|
// Structure is always: {rootFolderLocation}/config/initializers/config.js
|
|
304
|
-
|
|
319
|
+
const configPath =path.join(rootFolderLocation, 'config', 'initializers', 'config.js');
|
|
305
320
|
if(fs.existsSync(configPath)){
|
|
306
321
|
require(configPath);
|
|
307
322
|
}else{
|
|
308
|
-
|
|
323
|
+
logger.error({
|
|
324
|
+
code: 'MC_ERR_CONFIG_NOT_FOUND',
|
|
325
|
+
message: 'Cannot find config file',
|
|
326
|
+
path: configPath
|
|
327
|
+
});
|
|
309
328
|
}
|
|
310
329
|
|
|
311
330
|
// Structure is always: {rootFolderLocation}/config/routes.js
|
|
312
|
-
|
|
313
|
-
|
|
331
|
+
const routePath =path.join(rootFolderLocation, 'config', 'routes.js');
|
|
332
|
+
const routeObject ={
|
|
314
333
|
isComponent : true,
|
|
315
334
|
root : rootFolderLocation
|
|
316
335
|
}
|
|
@@ -318,7 +337,11 @@ class MasterControl {
|
|
|
318
337
|
if(fs.existsSync(routePath)){
|
|
319
338
|
require(routePath);
|
|
320
339
|
}else{
|
|
321
|
-
|
|
340
|
+
logger.error({
|
|
341
|
+
code: 'MC_ERR_ROUTES_NOT_FOUND',
|
|
342
|
+
message: 'Cannot find routes file',
|
|
343
|
+
path: routePath
|
|
344
|
+
});
|
|
322
345
|
}
|
|
323
346
|
}
|
|
324
347
|
|
|
@@ -335,7 +358,7 @@ class MasterControl {
|
|
|
335
358
|
|
|
336
359
|
if(settings.httpPort || settings.requestTimeout){
|
|
337
360
|
this.server.timeout = settings.requestTimeout;
|
|
338
|
-
|
|
361
|
+
const host =settings.hostname || settings.host || settings.http;
|
|
339
362
|
if(host){
|
|
340
363
|
this.server.listen(settings.httpPort, host);
|
|
341
364
|
}else{
|
|
@@ -391,7 +414,7 @@ class MasterControl {
|
|
|
391
414
|
// sets up https or http server protocals
|
|
392
415
|
setupServer(type, credentials ){
|
|
393
416
|
try {
|
|
394
|
-
|
|
417
|
+
const $that = this;
|
|
395
418
|
|
|
396
419
|
// SECURITY: Initialize prototype pollution protection
|
|
397
420
|
this._initPrototypePollutionProtection();
|
|
@@ -401,12 +424,10 @@ class MasterControl {
|
|
|
401
424
|
const internalModules = {
|
|
402
425
|
'MasterPipeline': './MasterPipeline',
|
|
403
426
|
'MasterTimeout': './MasterTimeout',
|
|
404
|
-
'MasterErrorRenderer': './error/MasterErrorRenderer',
|
|
405
427
|
'MasterAction': './MasterAction',
|
|
406
428
|
'MasterActionFilters': './MasterActionFilters',
|
|
407
429
|
'MasterRouter': './MasterRouter',
|
|
408
430
|
'MasterRequest': './MasterRequest',
|
|
409
|
-
'MasterError': './error/MasterError',
|
|
410
431
|
'MasterCors': './MasterCors',
|
|
411
432
|
'SessionSecurity': './security/SessionSecurity',
|
|
412
433
|
'MasterSocket': './MasterSocket',
|
|
@@ -422,8 +443,6 @@ class MasterControl {
|
|
|
422
443
|
const moduleRegistry = {
|
|
423
444
|
'pipeline': { path: './MasterPipeline', exportName: 'MasterPipeline' },
|
|
424
445
|
'timeout': { path: './MasterTimeout', exportName: 'MasterTimeout' },
|
|
425
|
-
'errorRenderer': { path: './error/MasterErrorRenderer', exportName: 'MasterErrorRenderer' },
|
|
426
|
-
'error': { path: './error/MasterError', exportName: 'MasterError' },
|
|
427
446
|
'router': { path: './MasterRouter', exportName: 'MasterRouter' },
|
|
428
447
|
'request': { path: './MasterRequest', exportName: 'MasterRequest' },
|
|
429
448
|
'cors': { path: './MasterCors', exportName: 'MasterCors' },
|
|
@@ -551,7 +570,7 @@ class MasterControl {
|
|
|
551
570
|
* @security CRITICAL: Always provide allowedHosts in production to prevent open redirect attacks
|
|
552
571
|
*/
|
|
553
572
|
startHttpToHttpsRedirect(redirectPort, bindHost, allowedHosts = []){
|
|
554
|
-
|
|
573
|
+
const $that = this;
|
|
555
574
|
|
|
556
575
|
// Security warning if no hosts specified
|
|
557
576
|
if (allowedHosts.length === 0) {
|
|
@@ -562,8 +581,8 @@ class MasterControl {
|
|
|
562
581
|
|
|
563
582
|
return http.createServer(function (req, res) {
|
|
564
583
|
try{
|
|
565
|
-
|
|
566
|
-
|
|
584
|
+
const host = req.headers['host'] || '';
|
|
585
|
+
const hostname = host.split(':')[0]; // Remove port number
|
|
567
586
|
|
|
568
587
|
// CRITICAL SECURITY: Validate host header to prevent open redirect attacks
|
|
569
588
|
if (allowedHosts.length > 0) {
|
|
@@ -711,7 +730,7 @@ class MasterControl {
|
|
|
711
730
|
* This includes: static files, body parsing, scoped services, routing, error handling
|
|
712
731
|
*/
|
|
713
732
|
_registerCoreMiddleware(){
|
|
714
|
-
|
|
733
|
+
const $that = this;
|
|
715
734
|
|
|
716
735
|
// 1. Static File Serving (with path traversal protection)
|
|
717
736
|
$that.pipeline.use(async (ctx, next) => {
|
|
@@ -754,17 +773,17 @@ class MasterControl {
|
|
|
754
773
|
return;
|
|
755
774
|
}
|
|
756
775
|
|
|
757
|
-
// Check if file exists
|
|
758
|
-
fs.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
776
|
+
// Check if file exists (use synchronous check for better performance in middleware)
|
|
777
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
778
|
+
ctx.response.statusCode = 404;
|
|
779
|
+
ctx.response.setHeader('Content-Type', 'text/plain');
|
|
780
|
+
ctx.response.end('Not Found');
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
765
783
|
|
|
766
|
-
|
|
767
|
-
|
|
784
|
+
// Get file stats
|
|
785
|
+
let finalPath = resolvedPath;
|
|
786
|
+
try {
|
|
768
787
|
const stats = fs.statSync(resolvedPath);
|
|
769
788
|
|
|
770
789
|
// If directory, try to serve index.html
|
|
@@ -881,7 +900,19 @@ class MasterControl {
|
|
|
881
900
|
}
|
|
882
901
|
});
|
|
883
902
|
}
|
|
884
|
-
})
|
|
903
|
+
} catch (error) {
|
|
904
|
+
// Handle file stat errors
|
|
905
|
+
logger.error({
|
|
906
|
+
code: 'MC_ERR_FILE_STAT',
|
|
907
|
+
message: 'Error accessing static file',
|
|
908
|
+
path: resolvedPath,
|
|
909
|
+
error: error.message
|
|
910
|
+
});
|
|
911
|
+
ctx.response.statusCode = 500;
|
|
912
|
+
ctx.response.setHeader('Content-Type', 'text/plain');
|
|
913
|
+
ctx.response.end('Internal Server Error');
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
885
916
|
|
|
886
917
|
return; // Terminal - don't call next()
|
|
887
918
|
}
|
|
@@ -972,7 +1003,7 @@ class MasterControl {
|
|
|
972
1003
|
}
|
|
973
1004
|
|
|
974
1005
|
async serverRun(req, res){
|
|
975
|
-
|
|
1006
|
+
const $that = this;
|
|
976
1007
|
console.log("path", `${req.method} ${req.url}`);
|
|
977
1008
|
|
|
978
1009
|
// Create request context for middleware pipeline
|
|
@@ -1020,7 +1051,7 @@ class MasterControl {
|
|
|
1020
1051
|
var rootFolderLocation = path.join(this.root, foldername);
|
|
1021
1052
|
|
|
1022
1053
|
// Structure is always: {rootFolderLocation}/routes.js
|
|
1023
|
-
|
|
1054
|
+
const routePath =path.join(rootFolderLocation, 'routes.js');
|
|
1024
1055
|
var route = {
|
|
1025
1056
|
isComponent : false,
|
|
1026
1057
|
root : `${this.root}`
|
|
@@ -1029,20 +1060,22 @@ class MasterControl {
|
|
|
1029
1060
|
if(fs.existsSync(routePath)){
|
|
1030
1061
|
require(routePath);
|
|
1031
1062
|
}else{
|
|
1032
|
-
|
|
1063
|
+
logger.error({
|
|
1064
|
+
code: 'MC_ERR_ROUTES_NOT_FOUND',
|
|
1065
|
+
message: 'Cannot find routes file',
|
|
1066
|
+
path: routePath
|
|
1067
|
+
});
|
|
1033
1068
|
}
|
|
1034
1069
|
}
|
|
1035
1070
|
|
|
1036
1071
|
|
|
1037
1072
|
// builds and calls all the required tools to have master running completely
|
|
1038
1073
|
addInternalTools(requiredList){
|
|
1039
|
-
if(requiredList
|
|
1074
|
+
if(Array.isArray(requiredList)){
|
|
1040
1075
|
// Map module names to their new organized paths
|
|
1041
1076
|
const modulePathMap = {
|
|
1042
1077
|
'MasterPipeline': './MasterPipeline',
|
|
1043
1078
|
'MasterTimeout': './MasterTimeout',
|
|
1044
|
-
'MasterErrorRenderer': './error/MasterErrorRenderer',
|
|
1045
|
-
'MasterError': './error/MasterError',
|
|
1046
1079
|
'MasterAction': './MasterAction',
|
|
1047
1080
|
'MasterActionFilters': './MasterActionFilters',
|
|
1048
1081
|
'MasterRouter': './MasterRouter',
|
package/MasterCors.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
// version 0.0.3 - robust origin handling (all envs), creds-safe reflection, function origins, extended Vary
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const { logger } = require('./error/MasterErrorLogger');
|
|
4
|
+
|
|
5
|
+
// HTTP Status Code Constants
|
|
6
|
+
const HTTP_STATUS = {
|
|
7
|
+
NO_CONTENT: 204,
|
|
8
|
+
BAD_REQUEST: 400
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// CORS Header Name Constants
|
|
12
|
+
const CORS_HEADERS = {
|
|
13
|
+
ALLOW_ORIGIN: 'Access-Control-Allow-Origin',
|
|
14
|
+
ALLOW_METHODS: 'Access-Control-Allow-Methods',
|
|
15
|
+
ALLOW_HEADERS: 'Access-Control-Allow-Headers',
|
|
16
|
+
ALLOW_CREDENTIALS: 'Access-Control-Allow-Credentials',
|
|
17
|
+
MAX_AGE: 'Access-Control-Max-Age',
|
|
18
|
+
EXPOSE_HEADERS: 'Access-Control-Expose-Headers',
|
|
19
|
+
REQUEST_HEADERS: 'Access-Control-Request-Headers',
|
|
20
|
+
REQUEST_METHOD: 'Access-Control-Request-Method',
|
|
21
|
+
VARY: 'Vary'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// todo - res.setHeader('Access-Control-Request-Method', '*');
|
|
4
25
|
class MasterCors{
|
|
5
26
|
|
|
6
27
|
// Lazy-load master to avoid circular dependency (Google-style lazy initialization)
|
|
@@ -16,7 +37,10 @@ class MasterCors{
|
|
|
16
37
|
this.options = options;
|
|
17
38
|
}
|
|
18
39
|
else{
|
|
19
|
-
|
|
40
|
+
logger.warn({
|
|
41
|
+
code: 'MC_CORS_OPTIONS_MISSING',
|
|
42
|
+
message: 'CORS options missing'
|
|
43
|
+
});
|
|
20
44
|
}
|
|
21
45
|
|
|
22
46
|
// Auto-register with pipeline if available
|
|
@@ -32,7 +56,15 @@ class MasterCors{
|
|
|
32
56
|
this.response = params.response;
|
|
33
57
|
this.request = params.request;
|
|
34
58
|
// Always signal that response may vary by Origin and requested headers/method
|
|
35
|
-
try {
|
|
59
|
+
try {
|
|
60
|
+
this.response.setHeader('Vary', 'Origin, Access-Control-Request-Headers, Access-Control-Request-Method');
|
|
61
|
+
} catch(error) {
|
|
62
|
+
logger.warn({
|
|
63
|
+
code: 'MC_CORS_VARY_HEADER_FAILED',
|
|
64
|
+
message: 'Failed to set Vary header',
|
|
65
|
+
error: error.message
|
|
66
|
+
});
|
|
67
|
+
}
|
|
36
68
|
this.configureOrigin();
|
|
37
69
|
this.configureMethods()
|
|
38
70
|
this.configureAllowedHeaders();
|
|
@@ -41,7 +73,10 @@ class MasterCors{
|
|
|
41
73
|
this.configureMaxAge();
|
|
42
74
|
}
|
|
43
75
|
else{
|
|
44
|
-
|
|
76
|
+
logger.warn({
|
|
77
|
+
code: 'MC_CORS_PARAMS_MISSING',
|
|
78
|
+
message: 'CORS response and request params missing'
|
|
79
|
+
});
|
|
45
80
|
}
|
|
46
81
|
}
|
|
47
82
|
|
|
@@ -63,7 +98,7 @@ class MasterCors{
|
|
|
63
98
|
|
|
64
99
|
if(this.options.origin === true){
|
|
65
100
|
// If credentials are enabled, reflect request origin per spec
|
|
66
|
-
|
|
101
|
+
const requestOrigin =this.request.headers.origin;
|
|
67
102
|
if (this.options.credentials === true && requestOrigin) {
|
|
68
103
|
this.setHeader('access-control-allow-origin', requestOrigin);
|
|
69
104
|
} else {
|
|
@@ -76,9 +111,9 @@ class MasterCors{
|
|
|
76
111
|
this.removeHeader('access-control-allow-origin');
|
|
77
112
|
}
|
|
78
113
|
|
|
79
|
-
if(this.options.origin
|
|
114
|
+
if(Array.isArray(this.options.origin)){
|
|
80
115
|
// Get the origin from the incoming request
|
|
81
|
-
|
|
116
|
+
const requestOrigin =this.request.headers.origin;
|
|
82
117
|
|
|
83
118
|
// Check if the request origin is in our allowed list
|
|
84
119
|
if(requestOrigin && this.options.origin.includes(requestOrigin)){
|
|
@@ -90,15 +125,22 @@ class MasterCors{
|
|
|
90
125
|
// Function predicate support: (origin, req) => boolean|string
|
|
91
126
|
if (typeof this.options.origin === 'function'){
|
|
92
127
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
128
|
+
const requestOrigin = this.request.headers.origin;
|
|
129
|
+
const res = this.options.origin(requestOrigin, this.request);
|
|
95
130
|
if (res === true && requestOrigin){
|
|
96
131
|
this.setHeader('access-control-allow-origin', requestOrigin);
|
|
97
132
|
}
|
|
98
133
|
else if (typeof res === 'string' && res){
|
|
99
134
|
this.setHeader('access-control-allow-origin', res);
|
|
100
135
|
}
|
|
101
|
-
} catch(
|
|
136
|
+
} catch(error) {
|
|
137
|
+
logger.error({
|
|
138
|
+
code: 'MC_CORS_ORIGIN_FUNCTION_ERROR',
|
|
139
|
+
message: 'Error in origin function predicate',
|
|
140
|
+
error: error.message,
|
|
141
|
+
stack: error.stack
|
|
142
|
+
});
|
|
143
|
+
}
|
|
102
144
|
}
|
|
103
145
|
|
|
104
146
|
}
|
|
@@ -106,16 +148,16 @@ class MasterCors{
|
|
|
106
148
|
|
|
107
149
|
configureMethods(){
|
|
108
150
|
if(this.options.methods){
|
|
109
|
-
if(this.options.methods
|
|
110
|
-
|
|
151
|
+
if(Array.isArray(this.options.methods)){
|
|
152
|
+
const elements =this.options.methods.join(", ");
|
|
111
153
|
this.setHeader('access-control-allow-methods', elements);
|
|
112
154
|
}
|
|
113
155
|
}
|
|
114
156
|
}
|
|
115
157
|
|
|
116
158
|
configureAllowedHeaders(){
|
|
117
|
-
|
|
118
|
-
|
|
159
|
+
const requestheader =this.request.headers["access-control-request-headers"];
|
|
160
|
+
const $that =this;
|
|
119
161
|
if(this.options.allowedHeaders){
|
|
120
162
|
|
|
121
163
|
if($that.options.allowedHeaders === true){
|
|
@@ -135,8 +177,8 @@ class MasterCors{
|
|
|
135
177
|
this.setHeader("access-control-allow-headers", $that.options.allowedHeaders);
|
|
136
178
|
}
|
|
137
179
|
|
|
138
|
-
if($that.options.allowedHeaders
|
|
139
|
-
|
|
180
|
+
if(Array.isArray($that.options.allowedHeaders)){
|
|
181
|
+
const elements =$that.options.allowedHeaders.join(", ");
|
|
140
182
|
$that.request.headers['access-control-allow-headers'] = elements;
|
|
141
183
|
this.setHeader("access-control-allow-headers", elements);
|
|
142
184
|
}
|
|
@@ -158,8 +200,8 @@ class MasterCors{
|
|
|
158
200
|
this.setHeader('access-control-expose-headers', this.options.exposeHeaders);
|
|
159
201
|
}
|
|
160
202
|
|
|
161
|
-
if(this.options.exposeHeaders
|
|
162
|
-
|
|
203
|
+
if(Array.isArray(this.options.exposeHeaders)){
|
|
204
|
+
const elements =this.options.exposeHeaders.join(", ");
|
|
163
205
|
this.setHeader('access-control-expose-headers', elements);
|
|
164
206
|
}
|
|
165
207
|
|
|
@@ -187,7 +229,7 @@ class MasterCors{
|
|
|
187
229
|
* Handles both preflight OPTIONS requests and regular requests
|
|
188
230
|
*/
|
|
189
231
|
middleware() {
|
|
190
|
-
|
|
232
|
+
const $that =this;
|
|
191
233
|
|
|
192
234
|
return async (ctx, next) => {
|
|
193
235
|
// Handle preflight OPTIONS request
|
package/MasterPipeline.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
const { logger } = require('./error/MasterErrorLogger');
|
|
5
5
|
|
|
6
|
+
// HTTP Status Code Constants
|
|
7
|
+
const HTTP_STATUS = {
|
|
8
|
+
INTERNAL_ERROR: 500
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
class MasterPipeline {
|
|
7
12
|
constructor() {
|
|
8
13
|
this.middleware = [];
|
|
@@ -214,7 +219,7 @@ class MasterPipeline {
|
|
|
214
219
|
});
|
|
215
220
|
|
|
216
221
|
if (!context.response.headersSent) {
|
|
217
|
-
context.response.statusCode =
|
|
222
|
+
context.response.statusCode = HTTP_STATUS.INTERNAL_ERROR;
|
|
218
223
|
context.response.end('Internal Server Error');
|
|
219
224
|
}
|
|
220
225
|
return;
|
|
@@ -285,7 +290,11 @@ class MasterPipeline {
|
|
|
285
290
|
folders.forEach(folder => {
|
|
286
291
|
const dir = path.join(this._master.root, folder);
|
|
287
292
|
if (!fs.existsSync(dir)) {
|
|
288
|
-
|
|
293
|
+
logger.warn({
|
|
294
|
+
code: 'MC_MIDDLEWARE_FOLDER_NOT_FOUND',
|
|
295
|
+
message: 'Middleware folder not found',
|
|
296
|
+
folder: folder
|
|
297
|
+
});
|
|
289
298
|
return;
|
|
290
299
|
}
|
|
291
300
|
|
|
@@ -305,16 +314,30 @@ class MasterPipeline {
|
|
|
305
314
|
}
|
|
306
315
|
// Pattern 2: module.exports = { register: (master) => {} }
|
|
307
316
|
else if (middleware.register && typeof middleware.register === 'function') {
|
|
308
|
-
middleware.register(
|
|
317
|
+
middleware.register(this._master);
|
|
309
318
|
}
|
|
310
319
|
else {
|
|
311
|
-
|
|
320
|
+
logger.warn({
|
|
321
|
+
code: 'MC_MIDDLEWARE_INVALID_EXPORT',
|
|
322
|
+
message: 'Invalid middleware export',
|
|
323
|
+
file: `${folder}/${file}`
|
|
324
|
+
});
|
|
312
325
|
return;
|
|
313
326
|
}
|
|
314
327
|
|
|
315
|
-
|
|
328
|
+
logger.info({
|
|
329
|
+
code: 'MC_MIDDLEWARE_LOADED',
|
|
330
|
+
message: 'Middleware loaded',
|
|
331
|
+
file: `${folder}/${file}`
|
|
332
|
+
});
|
|
316
333
|
} catch (err) {
|
|
317
|
-
|
|
334
|
+
logger.error({
|
|
335
|
+
code: 'MC_MIDDLEWARE_LOAD_FAILED',
|
|
336
|
+
message: 'Failed to load middleware',
|
|
337
|
+
file: `${folder}/${file}`,
|
|
338
|
+
error: err.message,
|
|
339
|
+
stack: err.stack
|
|
340
|
+
});
|
|
318
341
|
}
|
|
319
342
|
});
|
|
320
343
|
});
|