mastercontroller 1.3.4 → 1.3.6
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 +3 -1
- package/MasterAction.js +36 -36
- package/MasterActionFilters.js +2 -2
- package/MasterControl.js +4 -0
- package/MasterHtml.js +2 -2
- package/MasterRouter.js +1 -1
- package/package.json +1 -1
- package/security/SessionSecurity.js +84 -0
- package/test-v1.3.4-fixes.js +129 -0
- package/CIRCULAR-DEPENDENCY-FIX-v1.3.4.md +0 -480
- package/error/ErrorBoundary.js +0 -353
- package/error/HydrationMismatch.js +0 -265
- package/error/MasterBackendErrorHandler.js +0 -769
- package/error/MasterError.js +0 -240
- package/error/MasterError.js.tmp +0 -0
- package/error/MasterErrorHandler.js +0 -487
- package/error/MasterErrorLogger.js +0 -360
- package/error/MasterErrorMiddleware.js +0 -407
- package/error/MasterErrorRenderer.js +0 -536
- package/error/MasterErrorRenderer.js.tmp +0 -0
- package/error/SSRErrorHandler.js +0 -273
- package/test/security/filters.test.js +0 -276
- package/test/security/https.test.js +0 -214
- package/test/security/path-traversal.test.js +0 -222
- package/test/security/xss.test.js +0 -190
|
@@ -1,769 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MasterBackendErrorHandler - Comprehensive backend error handling
|
|
3
|
-
* Handles routing, controller, template, and request errors
|
|
4
|
-
* Version: 1.0.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { MasterControllerError } = require('./MasterErrorHandler');
|
|
8
|
-
const { logger } = require('./MasterErrorLogger');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
|
|
12
|
-
const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.master === 'development';
|
|
13
|
-
|
|
14
|
-
// Backend-specific error codes
|
|
15
|
-
const BACKEND_ERROR_CODES = {
|
|
16
|
-
MC_ERR_ROUTE_NOT_FOUND: {
|
|
17
|
-
title: '404 - Route Not Found',
|
|
18
|
-
httpCode: 404,
|
|
19
|
-
severity: 'warning'
|
|
20
|
-
},
|
|
21
|
-
MC_ERR_ROUTE_CONSTRAINT: {
|
|
22
|
-
title: 'Route Constraint Failed',
|
|
23
|
-
httpCode: 500,
|
|
24
|
-
severity: 'error'
|
|
25
|
-
},
|
|
26
|
-
MC_ERR_ROUTE_PROCESS: {
|
|
27
|
-
title: 'Route Processing Failed',
|
|
28
|
-
httpCode: 500,
|
|
29
|
-
severity: 'error'
|
|
30
|
-
},
|
|
31
|
-
MC_ERR_ROUTE_PARAM_SANITIZATION: {
|
|
32
|
-
title: 'Route Parameter Sanitization Failed',
|
|
33
|
-
httpCode: 400,
|
|
34
|
-
severity: 'error'
|
|
35
|
-
},
|
|
36
|
-
MC_ERR_CONTROLLER_NOT_FOUND: {
|
|
37
|
-
title: 'Controller Not Found',
|
|
38
|
-
httpCode: 500,
|
|
39
|
-
severity: 'error'
|
|
40
|
-
},
|
|
41
|
-
MC_ERR_ACTION_NOT_FOUND: {
|
|
42
|
-
title: 'Action Not Found',
|
|
43
|
-
httpCode: 500,
|
|
44
|
-
severity: 'error'
|
|
45
|
-
},
|
|
46
|
-
MC_ERR_TEMPLATE_NOT_FOUND: {
|
|
47
|
-
title: 'Template File Not Found',
|
|
48
|
-
httpCode: 500,
|
|
49
|
-
severity: 'error'
|
|
50
|
-
},
|
|
51
|
-
MC_ERR_TEMPLATE_RENDER: {
|
|
52
|
-
title: 'Template Rendering Failed',
|
|
53
|
-
httpCode: 500,
|
|
54
|
-
severity: 'error'
|
|
55
|
-
},
|
|
56
|
-
MC_ERR_VIEW_NOT_FOUND: {
|
|
57
|
-
title: 'View File Not Found',
|
|
58
|
-
httpCode: 500,
|
|
59
|
-
severity: 'error'
|
|
60
|
-
},
|
|
61
|
-
MC_ERR_CONTROLLER_EXCEPTION: {
|
|
62
|
-
title: 'Controller Action Failed',
|
|
63
|
-
httpCode: 500,
|
|
64
|
-
severity: 'error'
|
|
65
|
-
},
|
|
66
|
-
MC_ERR_REQUEST_PARSE: {
|
|
67
|
-
title: 'Request Parse Error',
|
|
68
|
-
httpCode: 400,
|
|
69
|
-
severity: 'error'
|
|
70
|
-
},
|
|
71
|
-
MC_ERR_VALIDATION: {
|
|
72
|
-
title: 'Validation Error',
|
|
73
|
-
httpCode: 422,
|
|
74
|
-
severity: 'warning'
|
|
75
|
-
},
|
|
76
|
-
MC_ERR_DATABASE: {
|
|
77
|
-
title: 'Database Error',
|
|
78
|
-
httpCode: 500,
|
|
79
|
-
severity: 'error'
|
|
80
|
-
},
|
|
81
|
-
MC_ERR_FILE_READ: {
|
|
82
|
-
title: 'File Read Error',
|
|
83
|
-
httpCode: 500,
|
|
84
|
-
severity: 'error'
|
|
85
|
-
},
|
|
86
|
-
MC_ERR_MIDDLEWARE: {
|
|
87
|
-
title: 'Middleware Error',
|
|
88
|
-
httpCode: 500,
|
|
89
|
-
severity: 'error'
|
|
90
|
-
},
|
|
91
|
-
MC_ERR_SESSION: {
|
|
92
|
-
title: 'Session Error',
|
|
93
|
-
httpCode: 500,
|
|
94
|
-
severity: 'error'
|
|
95
|
-
},
|
|
96
|
-
MC_ERR_UNAUTHORIZED: {
|
|
97
|
-
title: 'Unauthorized Access',
|
|
98
|
-
httpCode: 401,
|
|
99
|
-
severity: 'warning'
|
|
100
|
-
},
|
|
101
|
-
MC_ERR_FORBIDDEN: {
|
|
102
|
-
title: 'Forbidden',
|
|
103
|
-
httpCode: 403,
|
|
104
|
-
severity: 'warning'
|
|
105
|
-
},
|
|
106
|
-
MC_ERR_METHOD_NOT_ALLOWED: {
|
|
107
|
-
title: 'Method Not Allowed',
|
|
108
|
-
httpCode: 405,
|
|
109
|
-
severity: 'warning'
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Render 404 error page
|
|
115
|
-
*/
|
|
116
|
-
function render404Page(requestPath, suggestions = []) {
|
|
117
|
-
const title = '404 - Page Not Found';
|
|
118
|
-
|
|
119
|
-
if (isDevelopment) {
|
|
120
|
-
return `
|
|
121
|
-
<!DOCTYPE html>
|
|
122
|
-
<html lang="en">
|
|
123
|
-
<head>
|
|
124
|
-
<meta charset="UTF-8">
|
|
125
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
126
|
-
<title>${title}</title>
|
|
127
|
-
<style>
|
|
128
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
129
|
-
body {
|
|
130
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
131
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
132
|
-
min-height: 100vh;
|
|
133
|
-
display: flex;
|
|
134
|
-
align-items: center;
|
|
135
|
-
justify-content: center;
|
|
136
|
-
padding: 20px;
|
|
137
|
-
}
|
|
138
|
-
.container {
|
|
139
|
-
background: white;
|
|
140
|
-
border-radius: 16px;
|
|
141
|
-
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
142
|
-
max-width: 600px;
|
|
143
|
-
width: 100%;
|
|
144
|
-
overflow: hidden;
|
|
145
|
-
}
|
|
146
|
-
.header {
|
|
147
|
-
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
148
|
-
padding: 40px;
|
|
149
|
-
text-align: center;
|
|
150
|
-
color: white;
|
|
151
|
-
}
|
|
152
|
-
.header h1 {
|
|
153
|
-
font-size: 72px;
|
|
154
|
-
font-weight: 900;
|
|
155
|
-
margin-bottom: 10px;
|
|
156
|
-
}
|
|
157
|
-
.header p {
|
|
158
|
-
font-size: 20px;
|
|
159
|
-
opacity: 0.9;
|
|
160
|
-
}
|
|
161
|
-
.content {
|
|
162
|
-
padding: 40px;
|
|
163
|
-
}
|
|
164
|
-
.path {
|
|
165
|
-
background: #f3f4f6;
|
|
166
|
-
padding: 16px;
|
|
167
|
-
border-radius: 8px;
|
|
168
|
-
font-family: 'Courier New', monospace;
|
|
169
|
-
font-size: 14px;
|
|
170
|
-
color: #1f2937;
|
|
171
|
-
word-break: break-all;
|
|
172
|
-
margin-bottom: 24px;
|
|
173
|
-
}
|
|
174
|
-
.suggestions {
|
|
175
|
-
margin: 24px 0;
|
|
176
|
-
}
|
|
177
|
-
.suggestions h3 {
|
|
178
|
-
font-size: 16px;
|
|
179
|
-
color: #374151;
|
|
180
|
-
margin-bottom: 12px;
|
|
181
|
-
}
|
|
182
|
-
.suggestions ul {
|
|
183
|
-
list-style: none;
|
|
184
|
-
}
|
|
185
|
-
.suggestions li {
|
|
186
|
-
background: #ecfdf5;
|
|
187
|
-
padding: 12px 16px;
|
|
188
|
-
margin-bottom: 8px;
|
|
189
|
-
border-radius: 6px;
|
|
190
|
-
border-left: 3px solid #10b981;
|
|
191
|
-
}
|
|
192
|
-
.suggestions a {
|
|
193
|
-
color: #059669;
|
|
194
|
-
text-decoration: none;
|
|
195
|
-
font-weight: 600;
|
|
196
|
-
}
|
|
197
|
-
.suggestions a:hover {
|
|
198
|
-
text-decoration: underline;
|
|
199
|
-
}
|
|
200
|
-
.actions {
|
|
201
|
-
display: flex;
|
|
202
|
-
gap: 12px;
|
|
203
|
-
margin-top: 24px;
|
|
204
|
-
}
|
|
205
|
-
.btn {
|
|
206
|
-
flex: 1;
|
|
207
|
-
padding: 12px 24px;
|
|
208
|
-
border: none;
|
|
209
|
-
border-radius: 8px;
|
|
210
|
-
font-weight: 600;
|
|
211
|
-
cursor: pointer;
|
|
212
|
-
text-align: center;
|
|
213
|
-
text-decoration: none;
|
|
214
|
-
transition: all 0.2s;
|
|
215
|
-
}
|
|
216
|
-
.btn-primary {
|
|
217
|
-
background: #3b82f6;
|
|
218
|
-
color: white;
|
|
219
|
-
}
|
|
220
|
-
.btn-primary:hover {
|
|
221
|
-
background: #2563eb;
|
|
222
|
-
}
|
|
223
|
-
.btn-secondary {
|
|
224
|
-
background: #e5e7eb;
|
|
225
|
-
color: #374151;
|
|
226
|
-
}
|
|
227
|
-
.btn-secondary:hover {
|
|
228
|
-
background: #d1d5db;
|
|
229
|
-
}
|
|
230
|
-
.footer {
|
|
231
|
-
padding: 20px 40px;
|
|
232
|
-
background: #f9fafb;
|
|
233
|
-
text-align: center;
|
|
234
|
-
color: #6b7280;
|
|
235
|
-
font-size: 14px;
|
|
236
|
-
}
|
|
237
|
-
</style>
|
|
238
|
-
</head>
|
|
239
|
-
<body>
|
|
240
|
-
<div class="container">
|
|
241
|
-
<div class="header">
|
|
242
|
-
<h1>404</h1>
|
|
243
|
-
<p>Page Not Found</p>
|
|
244
|
-
</div>
|
|
245
|
-
<div class="content">
|
|
246
|
-
<div class="path">${requestPath}</div>
|
|
247
|
-
<p style="color: #6b7280; line-height: 1.6;">
|
|
248
|
-
The page you're looking for doesn't exist or has been moved.
|
|
249
|
-
</p>
|
|
250
|
-
${suggestions.length > 0 ? `
|
|
251
|
-
<div class="suggestions">
|
|
252
|
-
<h3>Did you mean?</h3>
|
|
253
|
-
<ul>
|
|
254
|
-
${suggestions.map(s => `<li><a href="${s.path}">${s.path}</a></li>`).join('')}
|
|
255
|
-
</ul>
|
|
256
|
-
</div>
|
|
257
|
-
` : ''}
|
|
258
|
-
<div class="actions">
|
|
259
|
-
<a href="/" class="btn btn-primary">Go Home</a>
|
|
260
|
-
<a href="javascript:history.back()" class="btn btn-secondary">Go Back</a>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
<div class="footer">
|
|
264
|
-
MasterController Framework
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
</body>
|
|
268
|
-
</html>
|
|
269
|
-
`.trim();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Production 404
|
|
273
|
-
return `
|
|
274
|
-
<!DOCTYPE html>
|
|
275
|
-
<html lang="en">
|
|
276
|
-
<head>
|
|
277
|
-
<meta charset="UTF-8">
|
|
278
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
279
|
-
<title>${title}</title>
|
|
280
|
-
<style>
|
|
281
|
-
body {
|
|
282
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
283
|
-
background: #f9fafb;
|
|
284
|
-
min-height: 100vh;
|
|
285
|
-
display: flex;
|
|
286
|
-
align-items: center;
|
|
287
|
-
justify-content: center;
|
|
288
|
-
padding: 20px;
|
|
289
|
-
margin: 0;
|
|
290
|
-
}
|
|
291
|
-
.container {
|
|
292
|
-
text-align: center;
|
|
293
|
-
max-width: 500px;
|
|
294
|
-
}
|
|
295
|
-
h1 {
|
|
296
|
-
font-size: 120px;
|
|
297
|
-
color: #3b82f6;
|
|
298
|
-
margin: 0;
|
|
299
|
-
}
|
|
300
|
-
p {
|
|
301
|
-
font-size: 20px;
|
|
302
|
-
color: #6b7280;
|
|
303
|
-
margin: 20px 0 40px;
|
|
304
|
-
}
|
|
305
|
-
a {
|
|
306
|
-
display: inline-block;
|
|
307
|
-
padding: 12px 32px;
|
|
308
|
-
background: #3b82f6;
|
|
309
|
-
color: white;
|
|
310
|
-
text-decoration: none;
|
|
311
|
-
border-radius: 8px;
|
|
312
|
-
font-weight: 600;
|
|
313
|
-
}
|
|
314
|
-
a:hover {
|
|
315
|
-
background: #2563eb;
|
|
316
|
-
}
|
|
317
|
-
</style>
|
|
318
|
-
</head>
|
|
319
|
-
<body>
|
|
320
|
-
<div class="container">
|
|
321
|
-
<h1>404</h1>
|
|
322
|
-
<p>Page not found</p>
|
|
323
|
-
<a href="/">Return Home</a>
|
|
324
|
-
</div>
|
|
325
|
-
</body>
|
|
326
|
-
</html>
|
|
327
|
-
`.trim();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Render 500 error page
|
|
332
|
-
*/
|
|
333
|
-
function render500Page(error, requestPath) {
|
|
334
|
-
const title = '500 - Server Error';
|
|
335
|
-
|
|
336
|
-
if (isDevelopment) {
|
|
337
|
-
return `
|
|
338
|
-
<!DOCTYPE html>
|
|
339
|
-
<html lang="en">
|
|
340
|
-
<head>
|
|
341
|
-
<meta charset="UTF-8">
|
|
342
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
343
|
-
<title>${title}</title>
|
|
344
|
-
<style>
|
|
345
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
346
|
-
body {
|
|
347
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
348
|
-
background: #1f2937;
|
|
349
|
-
color: #f3f4f6;
|
|
350
|
-
padding: 20px;
|
|
351
|
-
}
|
|
352
|
-
.container {
|
|
353
|
-
max-width: 1200px;
|
|
354
|
-
margin: 0 auto;
|
|
355
|
-
}
|
|
356
|
-
.header {
|
|
357
|
-
background: #dc2626;
|
|
358
|
-
padding: 30px;
|
|
359
|
-
border-radius: 8px;
|
|
360
|
-
margin-bottom: 20px;
|
|
361
|
-
}
|
|
362
|
-
.header h1 {
|
|
363
|
-
font-size: 36px;
|
|
364
|
-
margin-bottom: 10px;
|
|
365
|
-
}
|
|
366
|
-
.header p {
|
|
367
|
-
font-size: 18px;
|
|
368
|
-
opacity: 0.9;
|
|
369
|
-
}
|
|
370
|
-
.section {
|
|
371
|
-
background: #374151;
|
|
372
|
-
padding: 24px;
|
|
373
|
-
border-radius: 8px;
|
|
374
|
-
margin-bottom: 20px;
|
|
375
|
-
}
|
|
376
|
-
.section h2 {
|
|
377
|
-
font-size: 20px;
|
|
378
|
-
margin-bottom: 16px;
|
|
379
|
-
color: #60a5fa;
|
|
380
|
-
}
|
|
381
|
-
.code {
|
|
382
|
-
background: #1f2937;
|
|
383
|
-
padding: 16px;
|
|
384
|
-
border-radius: 4px;
|
|
385
|
-
font-family: 'Courier New', monospace;
|
|
386
|
-
font-size: 14px;
|
|
387
|
-
overflow-x: auto;
|
|
388
|
-
border-left: 4px solid #dc2626;
|
|
389
|
-
}
|
|
390
|
-
.path {
|
|
391
|
-
color: #fbbf24;
|
|
392
|
-
font-family: 'Courier New', monospace;
|
|
393
|
-
}
|
|
394
|
-
.stack {
|
|
395
|
-
color: #f87171;
|
|
396
|
-
white-space: pre-wrap;
|
|
397
|
-
line-height: 1.6;
|
|
398
|
-
}
|
|
399
|
-
.btn {
|
|
400
|
-
display: inline-block;
|
|
401
|
-
padding: 10px 20px;
|
|
402
|
-
background: #3b82f6;
|
|
403
|
-
color: white;
|
|
404
|
-
text-decoration: none;
|
|
405
|
-
border-radius: 6px;
|
|
406
|
-
margin-top: 20px;
|
|
407
|
-
}
|
|
408
|
-
.btn:hover {
|
|
409
|
-
background: #2563eb;
|
|
410
|
-
}
|
|
411
|
-
</style>
|
|
412
|
-
</head>
|
|
413
|
-
<body>
|
|
414
|
-
<div class="container">
|
|
415
|
-
<div class="header">
|
|
416
|
-
<h1>❌ 500 - Server Error</h1>
|
|
417
|
-
<p>An error occurred while processing your request</p>
|
|
418
|
-
</div>
|
|
419
|
-
|
|
420
|
-
<div class="section">
|
|
421
|
-
<h2>Request Path</h2>
|
|
422
|
-
<div class="code">
|
|
423
|
-
<span class="path">${requestPath || '(unknown)'}</span>
|
|
424
|
-
</div>
|
|
425
|
-
</div>
|
|
426
|
-
|
|
427
|
-
${error ? `
|
|
428
|
-
<div class="section">
|
|
429
|
-
<h2>Error Message</h2>
|
|
430
|
-
<div class="code">
|
|
431
|
-
${error.message || 'Unknown error'}
|
|
432
|
-
</div>
|
|
433
|
-
</div>
|
|
434
|
-
|
|
435
|
-
${error.stack ? `
|
|
436
|
-
<div class="section">
|
|
437
|
-
<h2>Stack Trace</h2>
|
|
438
|
-
<div class="code">
|
|
439
|
-
<div class="stack">${error.stack}</div>
|
|
440
|
-
</div>
|
|
441
|
-
</div>
|
|
442
|
-
` : ''}
|
|
443
|
-
` : ''}
|
|
444
|
-
|
|
445
|
-
<a href="/" class="btn">Go Home</a>
|
|
446
|
-
</div>
|
|
447
|
-
</body>
|
|
448
|
-
</html>
|
|
449
|
-
`.trim();
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Production 500
|
|
453
|
-
return `
|
|
454
|
-
<!DOCTYPE html>
|
|
455
|
-
<html lang="en">
|
|
456
|
-
<head>
|
|
457
|
-
<meta charset="UTF-8">
|
|
458
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
459
|
-
<title>${title}</title>
|
|
460
|
-
<style>
|
|
461
|
-
body {
|
|
462
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
463
|
-
background: #f9fafb;
|
|
464
|
-
min-height: 100vh;
|
|
465
|
-
display: flex;
|
|
466
|
-
align-items: center;
|
|
467
|
-
justify-content: center;
|
|
468
|
-
padding: 20px;
|
|
469
|
-
margin: 0;
|
|
470
|
-
}
|
|
471
|
-
.container {
|
|
472
|
-
text-align: center;
|
|
473
|
-
max-width: 500px;
|
|
474
|
-
}
|
|
475
|
-
.icon {
|
|
476
|
-
font-size: 80px;
|
|
477
|
-
margin-bottom: 20px;
|
|
478
|
-
}
|
|
479
|
-
h1 {
|
|
480
|
-
font-size: 32px;
|
|
481
|
-
color: #1f2937;
|
|
482
|
-
margin-bottom: 16px;
|
|
483
|
-
}
|
|
484
|
-
p {
|
|
485
|
-
font-size: 18px;
|
|
486
|
-
color: #6b7280;
|
|
487
|
-
margin-bottom: 32px;
|
|
488
|
-
line-height: 1.6;
|
|
489
|
-
}
|
|
490
|
-
a {
|
|
491
|
-
display: inline-block;
|
|
492
|
-
padding: 12px 32px;
|
|
493
|
-
background: #3b82f6;
|
|
494
|
-
color: white;
|
|
495
|
-
text-decoration: none;
|
|
496
|
-
border-radius: 8px;
|
|
497
|
-
font-weight: 600;
|
|
498
|
-
}
|
|
499
|
-
a:hover {
|
|
500
|
-
background: #2563eb;
|
|
501
|
-
}
|
|
502
|
-
</style>
|
|
503
|
-
</head>
|
|
504
|
-
<body>
|
|
505
|
-
<div class="container">
|
|
506
|
-
<div class="icon">😞</div>
|
|
507
|
-
<h1>Something went wrong</h1>
|
|
508
|
-
<p>We're sorry, but something went wrong on our end. We've been notified and are working to fix it.</p>
|
|
509
|
-
<a href="/">Return Home</a>
|
|
510
|
-
</div>
|
|
511
|
-
</body>
|
|
512
|
-
</html>
|
|
513
|
-
`.trim();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Handle controller errors
|
|
518
|
-
*/
|
|
519
|
-
function handleControllerError(error, controllerName, actionName, requestPath, routeDef = null) {
|
|
520
|
-
// Build details message with route information if available
|
|
521
|
-
let details = `Action: ${actionName}\nPath: ${requestPath}`;
|
|
522
|
-
|
|
523
|
-
if (routeDef) {
|
|
524
|
-
details += `\n\nRoute Definition:\n Path: ${routeDef.path}\n Controller: ${routeDef.toController}#${routeDef.toAction}\n Method: ${routeDef.type.toUpperCase()}`;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const mcError = new MasterControllerError({
|
|
528
|
-
code: error.message && error.message.includes('not found') ? 'MC_ERR_CONTROLLER_NOT_FOUND' : 'MC_ERR_CONTROLLER_EXCEPTION',
|
|
529
|
-
message: error.message && error.message.includes('not found')
|
|
530
|
-
? `Controller not found: ${controllerName}Controller`
|
|
531
|
-
: `Controller action threw an error: ${error.message}`,
|
|
532
|
-
file: `app/controllers/${controllerName}Controller.js`,
|
|
533
|
-
details: details,
|
|
534
|
-
originalError: error,
|
|
535
|
-
context: {
|
|
536
|
-
controller: controllerName,
|
|
537
|
-
action: actionName,
|
|
538
|
-
requestPath: requestPath,
|
|
539
|
-
route: routeDef
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
if (isDevelopment) {
|
|
544
|
-
console.error(mcError.format());
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
logger.error({
|
|
548
|
-
code: mcError.code,
|
|
549
|
-
message: mcError.message,
|
|
550
|
-
controller: controllerName,
|
|
551
|
-
action: actionName,
|
|
552
|
-
route: requestPath,
|
|
553
|
-
routeDef: routeDef,
|
|
554
|
-
originalError: error
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
return mcError;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Handle routing errors
|
|
562
|
-
*/
|
|
563
|
-
function handleRoutingError(requestPath, routes = [], errorContext = null) {
|
|
564
|
-
// Check if this is a route processing error (not a 404)
|
|
565
|
-
if (errorContext && errorContext.type === 'CONSTRAINT_ERROR') {
|
|
566
|
-
const route = errorContext.route;
|
|
567
|
-
const mcError = new MasterControllerError({
|
|
568
|
-
code: 'MC_ERR_ROUTE_CONSTRAINT',
|
|
569
|
-
message: `Route constraint failed: ${route.path} → ${route.toController}#${route.toAction}`,
|
|
570
|
-
details: `The constraint function for this route threw an error:\n\n${errorContext.error.message}\n\nRoute Definition:\n Path: ${route.path}\n Controller: ${route.toController}\n Action: ${route.toAction}\n Method: ${route.type.toUpperCase()}\n\nRequest:\n Path: ${requestPath}`,
|
|
571
|
-
file: `config/routes.js`,
|
|
572
|
-
context: {
|
|
573
|
-
route: route,
|
|
574
|
-
requestPath: requestPath,
|
|
575
|
-
errorType: 'constraint'
|
|
576
|
-
},
|
|
577
|
-
originalError: errorContext.error
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
if (isDevelopment) {
|
|
581
|
-
console.error(mcError.format());
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
logger.error({
|
|
585
|
-
code: mcError.code,
|
|
586
|
-
message: mcError.message,
|
|
587
|
-
route: route,
|
|
588
|
-
requestPath: requestPath,
|
|
589
|
-
originalError: errorContext.error
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
return mcError;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// Check if this is a route processing error
|
|
596
|
-
if (errorContext && errorContext.type === 'ROUTE_PROCESS_ERROR') {
|
|
597
|
-
const route = errorContext.route;
|
|
598
|
-
const mcError = new MasterControllerError({
|
|
599
|
-
code: 'MC_ERR_ROUTE_PROCESS',
|
|
600
|
-
message: `Failed to process route: ${route.path} → ${route.toController}#${route.toAction}`,
|
|
601
|
-
details: `An error occurred while processing this route:\n\n${errorContext.error.message}\n\nRoute Definition:\n Path: ${route.path}\n Controller: ${route.toController}\n Action: ${route.toAction}\n Method: ${route.type.toUpperCase()}\n\nRequest:\n Path: ${requestPath}\n Method: ${errorContext.requestMethod || 'GET'}`,
|
|
602
|
-
file: `config/routes.js`,
|
|
603
|
-
context: {
|
|
604
|
-
route: route,
|
|
605
|
-
requestPath: requestPath,
|
|
606
|
-
errorType: 'processing'
|
|
607
|
-
},
|
|
608
|
-
originalError: errorContext.error
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
if (isDevelopment) {
|
|
612
|
-
console.error(mcError.format());
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
logger.error({
|
|
616
|
-
code: mcError.code,
|
|
617
|
-
message: mcError.message,
|
|
618
|
-
route: route,
|
|
619
|
-
requestPath: requestPath,
|
|
620
|
-
originalError: errorContext.error
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
return mcError;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Standard 404 error
|
|
627
|
-
const mcError = new MasterControllerError({
|
|
628
|
-
code: 'MC_ERR_ROUTE_NOT_FOUND',
|
|
629
|
-
message: `No route found for: ${requestPath}`,
|
|
630
|
-
details: `The requested path does not match any defined routes.\n\nRegistered routes: ${routes.length}\n\nCheck your route definitions in config/routes.js`,
|
|
631
|
-
suggestions: findSimilarRoutes(requestPath, routes),
|
|
632
|
-
context: { requestPath, availableRoutes: routes.length }
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
if (isDevelopment) {
|
|
636
|
-
console.warn(mcError.format());
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
logger.warn({
|
|
640
|
-
code: mcError.code,
|
|
641
|
-
message: mcError.message,
|
|
642
|
-
route: requestPath
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
return mcError;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* Find similar routes for suggestions
|
|
650
|
-
*/
|
|
651
|
-
function findSimilarRoutes(requestPath, routes) {
|
|
652
|
-
if (!routes || routes.length === 0) return [];
|
|
653
|
-
|
|
654
|
-
const { levenshteinDistance } = require('./MasterErrorHandler');
|
|
655
|
-
|
|
656
|
-
const similar = routes
|
|
657
|
-
.map(route => ({
|
|
658
|
-
path: route.path || route,
|
|
659
|
-
distance: levenshteinDistance(requestPath, route.path || route)
|
|
660
|
-
}))
|
|
661
|
-
.filter(r => r.distance <= 5)
|
|
662
|
-
.sort((a, b) => a.distance - b.distance)
|
|
663
|
-
.slice(0, 3)
|
|
664
|
-
.map(r => ({ path: r.path }));
|
|
665
|
-
|
|
666
|
-
return similar;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Handle template errors
|
|
671
|
-
*/
|
|
672
|
-
function handleTemplateError(error, templatePath, data) {
|
|
673
|
-
const mcError = new MasterControllerError({
|
|
674
|
-
code: error.code === 'ENOENT' ? 'MC_ERR_TEMPLATE_NOT_FOUND' : 'MC_ERR_TEMPLATE_RENDER',
|
|
675
|
-
message: error.code === 'ENOENT'
|
|
676
|
-
? `Template file not found: ${templatePath}`
|
|
677
|
-
: `Template rendering failed: ${error.message}`,
|
|
678
|
-
file: templatePath,
|
|
679
|
-
originalError: error,
|
|
680
|
-
details: error.code === 'ENOENT'
|
|
681
|
-
? 'The template file does not exist. Check the file path and ensure the file exists.'
|
|
682
|
-
: null
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
if (isDevelopment) {
|
|
686
|
-
console.error(mcError.format());
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
logger.error({
|
|
690
|
-
code: mcError.code,
|
|
691
|
-
message: mcError.message,
|
|
692
|
-
file: templatePath,
|
|
693
|
-
originalError: error
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
return mcError;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Handle file read errors
|
|
701
|
-
*/
|
|
702
|
-
function handleFileReadError(error, filePath) {
|
|
703
|
-
const mcError = new MasterControllerError({
|
|
704
|
-
code: 'MC_ERR_FILE_READ',
|
|
705
|
-
message: `Failed to read file: ${filePath}`,
|
|
706
|
-
file: filePath,
|
|
707
|
-
originalError: error,
|
|
708
|
-
details: error.code === 'ENOENT'
|
|
709
|
-
? 'File does not exist'
|
|
710
|
-
: error.code === 'EACCES'
|
|
711
|
-
? 'Permission denied'
|
|
712
|
-
: error.message
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
if (isDevelopment) {
|
|
716
|
-
console.error(mcError.format());
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
logger.error({
|
|
720
|
-
code: mcError.code,
|
|
721
|
-
message: mcError.message,
|
|
722
|
-
file: filePath,
|
|
723
|
-
originalError: error
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
return mcError;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Send error response to client
|
|
731
|
-
*/
|
|
732
|
-
function sendErrorResponse(response, error, requestPath) {
|
|
733
|
-
if (!response || response.headersSent) {
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
const httpCode = BACKEND_ERROR_CODES[error.code]?.httpCode || 500;
|
|
738
|
-
const isNotFound = httpCode === 404;
|
|
739
|
-
|
|
740
|
-
try {
|
|
741
|
-
if (isNotFound) {
|
|
742
|
-
response.writeHead(404, { 'Content-Type': 'text/html' });
|
|
743
|
-
response.end(render404Page(requestPath, error.suggestions || []));
|
|
744
|
-
} else {
|
|
745
|
-
response.writeHead(httpCode, { 'Content-Type': 'text/html' });
|
|
746
|
-
|
|
747
|
-
if (isDevelopment) {
|
|
748
|
-
// Development: Show detailed error
|
|
749
|
-
response.end(render500Page(error.originalError || error, requestPath));
|
|
750
|
-
} else {
|
|
751
|
-
// Production: Show friendly error
|
|
752
|
-
response.end(render500Page(null, requestPath));
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
} catch (sendError) {
|
|
756
|
-
console.error('[MasterController] Failed to send error response:', sendError);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
module.exports = {
|
|
761
|
-
BACKEND_ERROR_CODES,
|
|
762
|
-
render404Page,
|
|
763
|
-
render500Page,
|
|
764
|
-
handleControllerError,
|
|
765
|
-
handleRoutingError,
|
|
766
|
-
handleTemplateError,
|
|
767
|
-
handleFileReadError,
|
|
768
|
-
sendErrorResponse
|
|
769
|
-
};
|