mastercontroller 1.2.11 → 1.2.13

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.
@@ -0,0 +1,769 @@
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
+ };