mastercontroller 1.2.12 → 1.2.14
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/settings.local.json +12 -0
- package/MasterAction.js +297 -73
- package/MasterControl.js +112 -19
- package/MasterHtml.js +101 -14
- package/MasterRouter.js +281 -66
- package/MasterTemplate.js +96 -3
- package/README.md +0 -44
- package/error/ErrorBoundary.js +353 -0
- package/error/HydrationMismatch.js +265 -0
- package/error/MasterBackendErrorHandler.js +769 -0
- package/{MasterError.js → error/MasterError.js} +2 -2
- package/error/MasterErrorHandler.js +487 -0
- package/error/MasterErrorLogger.js +360 -0
- package/error/MasterErrorMiddleware.js +407 -0
- package/error/SSRErrorHandler.js +273 -0
- package/monitoring/MasterCache.js +400 -0
- package/monitoring/MasterMemoryMonitor.js +188 -0
- package/monitoring/MasterProfiler.js +409 -0
- package/monitoring/PerformanceMonitor.js +233 -0
- package/package.json +3 -3
- package/security/CSPConfig.js +319 -0
- package/security/EventHandlerValidator.js +464 -0
- package/security/MasterSanitizer.js +429 -0
- package/security/MasterValidator.js +546 -0
- package/security/SecurityMiddleware.js +486 -0
- package/security/SessionSecurity.js +416 -0
- package/ssr/hydration-client.js +93 -0
- package/ssr/runtime-ssr.cjs +553 -0
- package/ssr/ssr-shims.js +73 -0
- package/examples/FileServingExample.js +0 -88
package/MasterHtml.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// version 0.0.
|
|
1
|
+
// version 0.0.25
|
|
2
2
|
|
|
3
3
|
var master = require('./MasterControl');
|
|
4
4
|
var fs = require('fs');
|
|
@@ -7,6 +7,14 @@ var toolClass = require('./MasterTools');
|
|
|
7
7
|
var temp = new tempClass();
|
|
8
8
|
var tools = new toolClass();
|
|
9
9
|
|
|
10
|
+
// Enhanced error handling
|
|
11
|
+
const { handleTemplateError } = require('./error/MasterBackendErrorHandler');
|
|
12
|
+
const { safeReadFile, safeFileExists } = require('./error/MasterErrorMiddleware');
|
|
13
|
+
const { logger } = require('./error/MasterErrorLogger');
|
|
14
|
+
|
|
15
|
+
// Security - Sanitization
|
|
16
|
+
const { sanitizeTemplateHTML, sanitizeUserHTML, escapeHTML } = require('./security/MasterSanitizer');
|
|
17
|
+
|
|
10
18
|
class html {
|
|
11
19
|
|
|
12
20
|
javaScriptSerializer(name, obj){
|
|
@@ -17,21 +25,41 @@ class html {
|
|
|
17
25
|
|
|
18
26
|
// render partial views
|
|
19
27
|
renderPartial(path, data){
|
|
20
|
-
|
|
28
|
+
try {
|
|
29
|
+
var partialViewUrl = `/app/views/${path}`;
|
|
30
|
+
var fullPath = master.router.currentRoute.root + partialViewUrl;
|
|
31
|
+
|
|
32
|
+
const fileResult = safeReadFile(fs, fullPath);
|
|
33
|
+
|
|
34
|
+
if (!fileResult.success) {
|
|
35
|
+
logger.warn({
|
|
36
|
+
code: 'MC_ERR_VIEW_NOT_FOUND',
|
|
37
|
+
message: `Partial view not found: ${path}`,
|
|
38
|
+
file: fullPath
|
|
39
|
+
});
|
|
40
|
+
return `<!-- Partial view not found: ${path} -->`;
|
|
41
|
+
}
|
|
21
42
|
|
|
22
|
-
|
|
23
|
-
|
|
43
|
+
var partialView = null;
|
|
44
|
+
if(master.overwrite.isTemplate){
|
|
45
|
+
partialView = master.overwrite.templateRender(data, "renderPartialView");
|
|
46
|
+
}
|
|
47
|
+
else{
|
|
48
|
+
partialView = temp.htmlBuilder(fileResult.content, data);
|
|
49
|
+
}
|
|
24
50
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
return partialView;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
const mcError = handleTemplateError(error, path, data);
|
|
54
|
+
logger.error({
|
|
55
|
+
code: mcError.code,
|
|
56
|
+
message: mcError.message,
|
|
57
|
+
file: path,
|
|
58
|
+
originalError: error
|
|
59
|
+
});
|
|
60
|
+
return `<!-- Error rendering partial: ${path} -->`;
|
|
31
61
|
}
|
|
32
62
|
|
|
33
|
-
return partialView;
|
|
34
|
-
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
// render all your link tags styles given the folder location
|
|
@@ -447,7 +475,7 @@ class html {
|
|
|
447
475
|
return rangeField;
|
|
448
476
|
}
|
|
449
477
|
|
|
450
|
-
// allows you to add data object to params
|
|
478
|
+
// allows you to add data object to params
|
|
451
479
|
addDataToParams(data){
|
|
452
480
|
|
|
453
481
|
//loop through data and add it to new oobjects prototype
|
|
@@ -457,7 +485,66 @@ class html {
|
|
|
457
485
|
master.view.extend(newObj);
|
|
458
486
|
}
|
|
459
487
|
}
|
|
460
|
-
|
|
488
|
+
|
|
489
|
+
// ==================== Security Methods ====================
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Sanitize user-generated HTML content
|
|
493
|
+
* Use this for any HTML that comes from user input
|
|
494
|
+
* @param {string} html - HTML content to sanitize
|
|
495
|
+
* @returns {string} - Sanitized HTML
|
|
496
|
+
*/
|
|
497
|
+
sanitizeHTML(html) {
|
|
498
|
+
return sanitizeUserHTML(html);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Escape HTML special characters
|
|
503
|
+
* Use this to display user input as text (not HTML)
|
|
504
|
+
* @param {string} text - Text to escape
|
|
505
|
+
* @returns {string} - Escaped text safe for display
|
|
506
|
+
*/
|
|
507
|
+
escapeHTML(text) {
|
|
508
|
+
return escapeHTML(text);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Render user content safely
|
|
513
|
+
* Sanitizes HTML and wraps in container
|
|
514
|
+
* @param {string} content - User-generated content
|
|
515
|
+
* @param {string} containerTag - HTML tag to wrap content (default: div)
|
|
516
|
+
* @param {object} attrs - Attributes for container
|
|
517
|
+
* @returns {string} - Safe HTML
|
|
518
|
+
*/
|
|
519
|
+
renderUserContent(content, containerTag = 'div', attrs = {}) {
|
|
520
|
+
const sanitized = sanitizeUserHTML(content);
|
|
521
|
+
|
|
522
|
+
let attrStr = '';
|
|
523
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
524
|
+
attrStr += ` ${key}="${escapeHTML(String(value))}"`;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return `<${containerTag}${attrStr}>${sanitized}</${containerTag}>`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Create safe text node content
|
|
532
|
+
* @param {string} text - Text content
|
|
533
|
+
* @returns {string} - HTML-escaped text
|
|
534
|
+
*/
|
|
535
|
+
textNode(text) {
|
|
536
|
+
return escapeHTML(text);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Create safe attribute value
|
|
541
|
+
* @param {string} value - Attribute value
|
|
542
|
+
* @returns {string} - Escaped and quoted value
|
|
543
|
+
*/
|
|
544
|
+
safeAttr(value) {
|
|
545
|
+
return `"${escapeHTML(String(value))}"`;
|
|
546
|
+
}
|
|
547
|
+
|
|
461
548
|
}
|
|
462
549
|
|
|
463
550
|
master.extendView("html", html);
|
package/MasterRouter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// version 0.0.
|
|
1
|
+
// version 0.0.250
|
|
2
2
|
|
|
3
3
|
var master = require('./MasterControl');
|
|
4
4
|
var toolClass = require('./MasterTools');
|
|
@@ -7,6 +7,17 @@ var path = require('path');
|
|
|
7
7
|
var currentRoute = {};
|
|
8
8
|
var tools = new toolClass();
|
|
9
9
|
|
|
10
|
+
// Enhanced error handling
|
|
11
|
+
const { handleRoutingError, handleControllerError, sendErrorResponse } = require('./error/MasterBackendErrorHandler');
|
|
12
|
+
const { logger } = require('./error/MasterErrorLogger');
|
|
13
|
+
const { performanceTracker, errorHandlerMiddleware } = require('./error/MasterErrorMiddleware');
|
|
14
|
+
|
|
15
|
+
// Security - Input validation and sanitization
|
|
16
|
+
const { validator, detectPathTraversal, detectSQLInjection, detectCommandInjection } = require('./security/MasterValidator');
|
|
17
|
+
const { escapeHTML } = require('./security/MasterSanitizer');
|
|
18
|
+
|
|
19
|
+
const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.master === 'development';
|
|
20
|
+
|
|
10
21
|
var normalizePaths = function(requestPath, routePath, requestParams){
|
|
11
22
|
var obj = {
|
|
12
23
|
requestPath : "",
|
|
@@ -21,8 +32,14 @@ var tools = new toolClass();
|
|
|
21
32
|
routeItem = routePathList[i];
|
|
22
33
|
if(routeItem){
|
|
23
34
|
if(routeItem.indexOf(":") > -1){
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
const paramName = routeItem.replace(":", "");
|
|
36
|
+
const paramValue = requestItem;
|
|
37
|
+
|
|
38
|
+
// Security: Sanitize route parameter
|
|
39
|
+
const sanitizedValue = sanitizeRouteParam(paramName, paramValue);
|
|
40
|
+
|
|
41
|
+
requestParams[paramName] = sanitizedValue;
|
|
42
|
+
routePathList[i] = sanitizedValue;
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
}
|
|
@@ -32,10 +49,66 @@ var tools = new toolClass();
|
|
|
32
49
|
return obj;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Sanitize route parameter to prevent injection attacks
|
|
54
|
+
*/
|
|
55
|
+
var sanitizeRouteParam = function(paramName, paramValue) {
|
|
56
|
+
if (!paramValue || typeof paramValue !== 'string') {
|
|
57
|
+
return paramValue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for path traversal attempts
|
|
61
|
+
const pathCheck = detectPathTraversal(paramValue);
|
|
62
|
+
if (!pathCheck.safe) {
|
|
63
|
+
logger.warn({
|
|
64
|
+
code: 'MC_SECURITY_PATH_TRAVERSAL',
|
|
65
|
+
message: 'Path traversal attempt detected in route parameter',
|
|
66
|
+
param: paramName,
|
|
67
|
+
value: paramValue
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Remove dangerous content
|
|
71
|
+
return paramValue.replace(/\.\./g, '').replace(/\.\//g, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check for SQL injection attempts
|
|
75
|
+
const sqlCheck = detectSQLInjection(paramValue);
|
|
76
|
+
if (!sqlCheck.safe) {
|
|
77
|
+
logger.warn({
|
|
78
|
+
code: 'MC_SECURITY_SQL_INJECTION',
|
|
79
|
+
message: 'SQL injection attempt detected in route parameter',
|
|
80
|
+
param: paramName,
|
|
81
|
+
value: paramValue
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Escape to prevent injection
|
|
85
|
+
return escapeHTML(paramValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check for command injection attempts
|
|
89
|
+
const cmdCheck = detectCommandInjection(paramValue);
|
|
90
|
+
if (!cmdCheck.safe) {
|
|
91
|
+
logger.warn({
|
|
92
|
+
code: 'MC_SECURITY_COMMAND_INJECTION',
|
|
93
|
+
message: 'Command injection attempt detected in route parameter',
|
|
94
|
+
param: paramName,
|
|
95
|
+
value: paramValue
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Remove dangerous characters
|
|
99
|
+
return paramValue.replace(/[;&|`$()]/g, '');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Basic sanitization for all params
|
|
103
|
+
return escapeHTML(paramValue);
|
|
104
|
+
}
|
|
105
|
+
|
|
35
106
|
var processRoutes = function(requestObject, emitter, routeObject){
|
|
36
107
|
var routeList = routeObject.routes;
|
|
37
108
|
var root = routeObject.root;
|
|
38
109
|
var isComponent = routeObject.isComponent;
|
|
110
|
+
var currentRouteBeingProcessed = null; // Track current route for better error messages
|
|
111
|
+
|
|
39
112
|
try{
|
|
40
113
|
// Ensure routes is an array
|
|
41
114
|
if(!Array.isArray(routeList)){
|
|
@@ -51,56 +124,117 @@ var tools = new toolClass();
|
|
|
51
124
|
if(routeList.length > 0){
|
|
52
125
|
// loop through routes
|
|
53
126
|
for(var item in routeList){
|
|
127
|
+
// Store current route for error handling
|
|
128
|
+
currentRouteBeingProcessed = {
|
|
129
|
+
path: routeList[item].path,
|
|
130
|
+
toController: routeList[item].toController,
|
|
131
|
+
toAction: routeList[item].toAction,
|
|
132
|
+
type: routeList[item].type
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
requestObject.toController = routeList[item].toController;
|
|
137
|
+
requestObject.toAction = routeList[item].toAction;
|
|
138
|
+
|
|
139
|
+
// FIX: Create a clean copy of params for each route test to prevent parameter pollution
|
|
140
|
+
// This prevents parameters from non-matching routes from accumulating in requestObject.params
|
|
141
|
+
var testParams = Object.assign({}, requestObject.params);
|
|
142
|
+
var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, testParams);
|
|
143
|
+
|
|
144
|
+
// if we find the route that matches the request
|
|
145
|
+
if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
|
|
146
|
+
// Only commit the extracted params if this route actually matches
|
|
147
|
+
requestObject.params = testParams;
|
|
148
|
+
|
|
149
|
+
// call Constraint
|
|
150
|
+
if(typeof routeList[item].constraint === "function"){
|
|
151
|
+
|
|
152
|
+
var newObj = {};
|
|
153
|
+
//tools.combineObjects(newObj, master.controllerList);
|
|
154
|
+
newObj.next = function(){
|
|
155
|
+
currentRoute.root = root;
|
|
156
|
+
currentRoute.pathName = requestObject.pathName;
|
|
157
|
+
currentRoute.toAction = requestObject.toAction;
|
|
158
|
+
currentRoute.toController = requestObject.toController;
|
|
159
|
+
currentRoute.response = requestObject.response;
|
|
160
|
+
currentRoute.isComponent = isComponent;
|
|
161
|
+
currentRoute.routeDef = currentRouteBeingProcessed; // Add route definition
|
|
162
|
+
emitter.emit("routeConstraintGood", requestObject);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Wrap constraint execution with error handling
|
|
166
|
+
try {
|
|
167
|
+
routeList[item].constraint.call(newObj, requestObject);
|
|
168
|
+
} catch(constraintError) {
|
|
169
|
+
const routeError = handleRoutingError(
|
|
170
|
+
requestObject.pathName,
|
|
171
|
+
[],
|
|
172
|
+
{
|
|
173
|
+
type: 'CONSTRAINT_ERROR',
|
|
174
|
+
route: currentRouteBeingProcessed,
|
|
175
|
+
error: constraintError
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
logger.error({
|
|
179
|
+
code: 'MC_ERR_ROUTE_CONSTRAINT',
|
|
180
|
+
message: `Route constraint failed for ${currentRouteBeingProcessed.path}`,
|
|
181
|
+
route: currentRouteBeingProcessed,
|
|
182
|
+
error: constraintError.message,
|
|
183
|
+
stack: constraintError.stack
|
|
184
|
+
});
|
|
185
|
+
throw constraintError;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}else{
|
|
54
190
|
|
|
55
|
-
requestObject.toController = routeList[item].toController;
|
|
56
|
-
requestObject.toAction = routeList[item].toAction;
|
|
57
|
-
var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, requestObject.params);
|
|
58
|
-
// if we find the route that matches the request
|
|
59
|
-
if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
|
|
60
|
-
|
|
61
|
-
// call Constraint
|
|
62
|
-
if(typeof routeList[item].constraint === "function"){
|
|
63
|
-
|
|
64
|
-
var newObj = {};
|
|
65
|
-
//tools.combineObjects(newObj, master.controllerList);
|
|
66
|
-
newObj.next = function(){
|
|
67
191
|
currentRoute.root = root;
|
|
68
192
|
currentRoute.pathName = requestObject.pathName;
|
|
69
193
|
currentRoute.toAction = requestObject.toAction;
|
|
70
194
|
currentRoute.toController = requestObject.toController;
|
|
71
195
|
currentRoute.response = requestObject.response;
|
|
72
196
|
currentRoute.isComponent = isComponent;
|
|
197
|
+
currentRoute.routeDef = currentRouteBeingProcessed; // Add route definition
|
|
73
198
|
emitter.emit("routeConstraintGood", requestObject);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
emitter.emit("routeConstraintGood", requestObject);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if(pathObj.requestPath === pathObj.routePath && "options" ===requestObject.type.toLowerCase()){
|
|
205
|
+
// this means that the request is correct but its an options request means its the browser checking to see if the request is allowed
|
|
206
|
+
// Commit the params for OPTIONS requests too
|
|
207
|
+
requestObject.params = testParams;
|
|
208
|
+
requestObject.response.writeHead(200, {'Content-Type': 'application/json'});
|
|
209
|
+
requestObject.response.end(JSON.stringify({"done": "true"}));
|
|
86
210
|
return true;
|
|
87
211
|
}
|
|
88
|
-
|
|
89
|
-
|
|
212
|
+
} catch(routeProcessError) {
|
|
213
|
+
// Log the specific route that failed
|
|
214
|
+
logger.error({
|
|
215
|
+
code: 'MC_ERR_ROUTE_PROCESS',
|
|
216
|
+
message: `Failed to process route: ${currentRouteBeingProcessed.path}`,
|
|
217
|
+
route: currentRouteBeingProcessed,
|
|
218
|
+
requestPath: requestObject.pathName,
|
|
219
|
+
error: routeProcessError.message,
|
|
220
|
+
stack: routeProcessError.stack
|
|
221
|
+
});
|
|
90
222
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
requestObject.response.writeHead(200, {'Content-Type': 'application/json'});
|
|
94
|
-
requestObject.response.end(JSON.stringify({"done": "true"}));
|
|
95
|
-
return true;
|
|
223
|
+
// Re-throw to be caught by outer try-catch
|
|
224
|
+
throw routeProcessError;
|
|
96
225
|
}
|
|
97
|
-
|
|
226
|
+
|
|
98
227
|
};
|
|
99
228
|
return -1;
|
|
100
229
|
}
|
|
101
230
|
}
|
|
102
231
|
catch(e){
|
|
103
|
-
|
|
232
|
+
// Enhanced error message with route context
|
|
233
|
+
const errorDetails = currentRouteBeingProcessed
|
|
234
|
+
? `\n\nFailing Route:\n Path: ${currentRouteBeingProcessed.path}\n Controller: ${currentRouteBeingProcessed.toController}#${currentRouteBeingProcessed.toAction}\n Method: ${currentRouteBeingProcessed.type.toUpperCase()}\n\nRequest:\n Path: ${requestObject.pathName}\n Method: ${requestObject.type.toUpperCase()}`
|
|
235
|
+
: '';
|
|
236
|
+
|
|
237
|
+
throw new Error(`Error processing routes: ${e.message}${errorDetails}\n\nOriginal Stack:\n${e.stack}`);
|
|
104
238
|
}
|
|
105
239
|
};
|
|
106
240
|
|
|
@@ -257,40 +391,113 @@ class MasterRouter {
|
|
|
257
391
|
|
|
258
392
|
_call(requestObject){
|
|
259
393
|
|
|
394
|
+
// Start performance tracking
|
|
395
|
+
const requestId = `${Date.now()}-${Math.random()}`;
|
|
396
|
+
performanceTracker.start(requestId, requestObject);
|
|
397
|
+
|
|
260
398
|
tools.combineObjects(master.requestList, requestObject);
|
|
261
399
|
requestObject = master.requestList;
|
|
262
400
|
var Control = null;
|
|
401
|
+
|
|
263
402
|
try{
|
|
264
|
-
|
|
265
|
-
}catch(e){
|
|
403
|
+
// Try to load controller
|
|
266
404
|
try{
|
|
267
|
-
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.
|
|
268
|
-
}catch(
|
|
269
|
-
|
|
405
|
+
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.firstLetterlowercase(requestObject.toController)}Controller`));
|
|
406
|
+
}catch(e){
|
|
407
|
+
try{
|
|
408
|
+
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.firstLetterUppercase(requestObject.toController)}Controller`));
|
|
409
|
+
}catch(e2){
|
|
410
|
+
// Controller not found - handle error
|
|
411
|
+
const error = handleControllerError(
|
|
412
|
+
new Error(`Controller not found: ${requestObject.toController}Controller`),
|
|
413
|
+
requestObject.toController,
|
|
414
|
+
requestObject.toAction,
|
|
415
|
+
requestObject.pathName,
|
|
416
|
+
currentRoute.routeDef // Pass route definition
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
sendErrorResponse(requestObject.response, error, requestObject.pathName);
|
|
420
|
+
performanceTracker.end(requestId);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
270
423
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
424
|
+
|
|
425
|
+
tools.combineObjectPrototype(Control, master.controllerList);
|
|
426
|
+
Control.prototype.__namespace = Control.name;
|
|
427
|
+
Control.prototype.__requestObject = requestObject;
|
|
428
|
+
Control.prototype.__currentRoute = currentRoute;
|
|
429
|
+
Control.prototype.__response = requestObject.response;
|
|
430
|
+
Control.prototype.__request = requestObject.request;
|
|
431
|
+
var control = new Control(requestObject);
|
|
432
|
+
var _callEmit = new EventEmitter();
|
|
433
|
+
|
|
434
|
+
_callEmit.on("controller", function(){
|
|
435
|
+
try {
|
|
436
|
+
control.next = function(){
|
|
437
|
+
control.__callAfterAction(control, requestObject);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Check if action exists
|
|
441
|
+
if (typeof control[requestObject.toAction] !== 'function') {
|
|
442
|
+
throw new Error(`Action '${requestObject.toAction}' not found in controller ${requestObject.toController}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Wrap action with error handling
|
|
446
|
+
const wrappedAction = errorHandlerMiddleware(
|
|
447
|
+
control[requestObject.toAction],
|
|
448
|
+
requestObject.toController,
|
|
449
|
+
requestObject.toAction
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// Execute action
|
|
453
|
+
Promise.resolve(wrappedAction.call(control, requestObject))
|
|
454
|
+
.then(() => {
|
|
455
|
+
performanceTracker.end(requestId);
|
|
456
|
+
})
|
|
457
|
+
.catch((error) => {
|
|
458
|
+
const mcError = handleControllerError(
|
|
459
|
+
error,
|
|
460
|
+
requestObject.toController,
|
|
461
|
+
requestObject.toAction,
|
|
462
|
+
requestObject.pathName,
|
|
463
|
+
currentRoute.routeDef // Pass route definition
|
|
464
|
+
);
|
|
465
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
466
|
+
performanceTracker.end(requestId);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
} catch (error) {
|
|
470
|
+
// Action execution error
|
|
471
|
+
const mcError = handleControllerError(
|
|
472
|
+
error,
|
|
473
|
+
requestObject.toController,
|
|
474
|
+
requestObject.toAction,
|
|
475
|
+
requestObject.pathName,
|
|
476
|
+
currentRoute.routeDef // Pass route definition
|
|
477
|
+
);
|
|
478
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
479
|
+
performanceTracker.end(requestId);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// check if before function is avaliable and wait for it to return
|
|
484
|
+
if(control.__hasBeforeAction(control, requestObject)){
|
|
485
|
+
control.__callBeforeAction(control, requestObject, _callEmit);
|
|
486
|
+
}else{
|
|
487
|
+
_callEmit.emit("controller");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
} catch (error) {
|
|
491
|
+
// General error
|
|
492
|
+
const mcError = handleControllerError(
|
|
493
|
+
error,
|
|
494
|
+
requestObject.toController,
|
|
495
|
+
requestObject.toAction,
|
|
496
|
+
requestObject.pathName,
|
|
497
|
+
currentRoute.routeDef // Pass route definition
|
|
498
|
+
);
|
|
499
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
500
|
+
performanceTracker.end(requestId);
|
|
294
501
|
}
|
|
295
502
|
|
|
296
503
|
}
|
|
@@ -319,8 +526,16 @@ class MasterRouter {
|
|
|
319
526
|
}
|
|
320
527
|
|
|
321
528
|
if(routeFound === false){
|
|
322
|
-
|
|
323
|
-
|
|
529
|
+
// Enhanced 404 handling
|
|
530
|
+
const allRoutes = [];
|
|
531
|
+
for (const route of routes) {
|
|
532
|
+
if (this._routes[route] && this._routes[route].routes) {
|
|
533
|
+
allRoutes.push(...this._routes[route].routes);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const mcError = handleRoutingError(requestObject.pathName, allRoutes);
|
|
538
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
324
539
|
}
|
|
325
540
|
|
|
326
541
|
}
|