mastercontroller 1.3.1 → 1.3.3

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/README.md CHANGED
@@ -14,6 +14,7 @@ MasterController is a lightweight MVC-style server framework for Node.js with mi
14
14
  - [CORS](#cors)
15
15
  - [Sessions](#sessions)
16
16
  - [Security](#security)
17
+ - [File Conversion & Binary Data](#file-conversion--binary-data)
17
18
  - [Components](#components)
18
19
  - [Timeout System](#timeout-system)
19
20
  - [Error Handling](#error-handling)
@@ -55,7 +56,13 @@ const cors = require('./cors.json');
55
56
  master.cors.init(cors);
56
57
 
57
58
  // Initialize sessions (auto-registers with pipeline)
58
- master.sessions.init();
59
+ master.session.init({
60
+ cookieName: 'mc_session',
61
+ maxAge: 3600000,
62
+ httpOnly: true,
63
+ secure: true,
64
+ sameSite: 'strict'
65
+ });
59
66
 
60
67
  // Auto-discover custom middleware from middleware/ folder
61
68
  master.pipeline.discoverMiddleware('middleware');
@@ -790,11 +797,9 @@ master.cors.init({
790
797
 
791
798
  ## Sessions
792
799
 
793
- MasterController provides **two session systems**:
794
- - **`master.session`** (NEW) - Secure, Rails/Django-style sessions with automatic regeneration and protection (RECOMMENDED)
795
- - **`master.sessions`** (LEGACY) - Original cookie-based session API (backward compatibility only)
800
+ MasterController provides secure, Rails/Django-style sessions with automatic regeneration and protection.
796
801
 
797
- ### Secure Sessions (NEW - Recommended)
802
+ ### Secure Sessions
798
803
 
799
804
  #### `master.session.init(options)`
800
805
 
@@ -914,105 +919,6 @@ master.session.init(settings);
914
919
  - MaxAge: 24 hours (convenient for development)
915
920
  - RegenerateInterval: 1 hour
916
921
 
917
- ---
918
-
919
- ### Legacy Sessions (Backward Compatibility)
920
-
921
- **⚠️ DEPRECATED: Use `master.session` (singular) for new projects.**
922
-
923
- The original `master.sessions` (plural) API is maintained for backward compatibility but lacks modern security features.
924
-
925
- #### `master.sessions.init(options)`
926
-
927
- Initialize legacy sessions (auto-registers with middleware pipeline).
928
-
929
- ```javascript
930
- master.sessions.init({
931
- secret: 'your-secret-key',
932
- maxAge: 900000, // 15 minutes
933
- httpOnly: true,
934
- secure: true, // HTTPS only
935
- sameSite: 'strict', // Must be string: 'strict', 'lax', or 'none'
936
- path: '/'
937
- });
938
- ```
939
-
940
- #### Legacy Session API
941
-
942
- **`master.sessions.set(name, data, response, secret, options)`** - Create a session
943
-
944
- ```javascript
945
- master.sessions.set('user', userData, obj.response);
946
- ```
947
-
948
- **`master.sessions.get(name, request, secret)`** - Retrieve session data
949
-
950
- ```javascript
951
- const user = master.sessions.get('user', obj.request);
952
- ```
953
-
954
- **`master.sessions.delete(name, response)`** - Delete a session
955
-
956
- ```javascript
957
- master.sessions.delete('user', obj.response);
958
- ```
959
-
960
- **`master.sessions.reset()`** - Clear all sessions
961
-
962
- ```javascript
963
- master.sessions.reset();
964
- ```
965
-
966
- #### Legacy Cookie Methods
967
-
968
- **`master.sessions.setCookie(name, value, response, options)`**
969
- ```javascript
970
- master.sessions.setCookie('theme', 'dark', obj.response);
971
- ```
972
-
973
- **`master.sessions.getCookie(name, request, secret)`**
974
- ```javascript
975
- const theme = master.sessions.getCookie('theme', obj.request);
976
- ```
977
-
978
- **`master.sessions.deleteCookie(name, response, options)`**
979
- ```javascript
980
- master.sessions.deleteCookie('theme', obj.response);
981
- ```
982
-
983
- #### Migration Guide: Legacy → Secure Sessions
984
-
985
- **Old (master.sessions):**
986
- ```javascript
987
- // Set
988
- master.sessions.set('user', userData, obj.response);
989
-
990
- // Get
991
- const user = master.sessions.get('user', obj.request);
992
-
993
- // Delete
994
- master.sessions.delete('user', obj.response);
995
- ```
996
-
997
- **New (master.session):**
998
- ```javascript
999
- // Set (Rails/Express style)
1000
- obj.request.session.user = userData;
1001
-
1002
- // Get
1003
- const user = obj.request.session.user;
1004
-
1005
- // Delete
1006
- master.session.destroy(obj.request, obj.response);
1007
- ```
1008
-
1009
- **Benefits of migration:**
1010
- - ✅ Automatic session regeneration (prevents fixation)
1011
- - ✅ 32-byte session IDs (stronger than 20-byte)
1012
- - ✅ Rolling sessions (better UX)
1013
- - ✅ Automatic cleanup (no memory leaks)
1014
- - ✅ Rails/Express-style API (more familiar)
1015
- - ✅ No broken encryption (legacy has crypto bugs)
1016
922
 
1017
923
  ---
1018
924
 
@@ -1123,220 +1029,1025 @@ class UsersController {
1123
1029
  - `detectSQLInjection(input)` - Detect SQL injection
1124
1030
  - `detectCommandInjection(input)` - Detect command injection
1125
1031
 
1126
- ---
1127
-
1128
- ## Components
1032
+ ### File Upload Security
1129
1033
 
1130
- Components are self-contained modules with their own routes, controllers, and views.
1034
+ MasterController v1.3.1 includes built-in protection against file upload attacks and DoS.
1131
1035
 
1132
- ### Structure
1036
+ #### Request Body Size Limits
1133
1037
 
1134
- ```
1135
- components/
1136
- user/
1137
- config/
1138
- initializers/
1139
- config.js
1140
- routes.js
1141
- app/
1142
- controllers/
1143
- authController.js
1144
- views/
1145
- auth/
1146
- login.html
1147
- models/
1148
- userContext.js
1038
+ **config/initializers/request.json:**
1039
+ ```json
1040
+ {
1041
+ "disableFormidableMultipartFormData": false,
1042
+ "formidable": {
1043
+ "multiples": true,
1044
+ "keepExtensions": true,
1045
+ "maxFileSize": 10485760, // 10MB per file
1046
+ "maxFieldsSize": 2097152, // 2MB total form fields
1047
+ "maxFields": 1000, // Max number of fields
1048
+ "allowEmptyFiles": false, // Reject empty files
1049
+ "minFileSize": 1 // Reject 0-byte files
1050
+ },
1051
+ "maxBodySize": 10485760, // 10MB for form-urlencoded
1052
+ "maxJsonSize": 1048576, // 1MB for JSON payloads
1053
+ "maxTextSize": 1048576 // 1MB for text/plain
1054
+ }
1149
1055
  ```
1150
1056
 
1151
- ### Register Component
1057
+ **DoS Protection:**
1058
+ - All request bodies are size-limited (prevents memory exhaustion)
1059
+ - Connections destroyed if limits exceeded
1060
+ - Configurable per content-type
1152
1061
 
1153
- ```javascript
1154
- // In config/initializers/config.js
1155
- master.component('components', 'user');
1156
- master.component('components', 'mail');
1157
- ```
1062
+ #### File Type Validation
1158
1063
 
1159
- ### Absolute Path Components
1064
+ **Always validate file types in your controllers:**
1160
1065
 
1161
1066
  ```javascript
1162
- // Load component from absolute path
1163
- master.component('/var/www/shared-components', 'analytics');
1164
- ```
1067
+ class UploadController {
1068
+ uploadImage(obj) {
1069
+ const file = obj.params.formData.files.avatar[0];
1165
1070
 
1166
- Components are isolated and can be reused across projects.
1071
+ // 1. Validate MIME type
1072
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
1073
+ if (!allowedTypes.includes(file.mimetype)) {
1074
+ this.json({ error: 'Only images allowed (JPEG, PNG, GIF, WebP)' });
1075
+ return;
1076
+ }
1167
1077
 
1168
- ---
1078
+ // 2. Validate file extension
1079
+ const allowedExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
1080
+ if (!allowedExts.includes(file.extension.toLowerCase())) {
1081
+ this.json({ error: 'Invalid file extension' });
1082
+ return;
1083
+ }
1169
1084
 
1170
- ## Timeout System
1085
+ // 3. Validate file size (additional check)
1086
+ const maxSize = 5 * 1024 * 1024; // 5MB
1087
+ if (file.size > maxSize) {
1088
+ this.json({ error: 'File too large (max 5MB)' });
1089
+ return;
1090
+ }
1171
1091
 
1172
- MasterController v2.0 includes a professional timeout system with per-request tracking (Rails/Django style).
1092
+ // 4. Generate safe filename (prevent path traversal)
1093
+ const crypto = require('crypto');
1094
+ const safeFilename = crypto.randomBytes(16).toString('hex') + file.extension;
1095
+ const uploadPath = path.join(master.root, 'uploads', safeFilename);
1173
1096
 
1174
- ### Configuration
1097
+ // 5. Move file
1098
+ fs.renameSync(file.filepath, uploadPath);
1175
1099
 
1176
- ```javascript
1177
- // config/initializers/config.js
1178
- master.timeout.init({
1179
- globalTimeout: 120000, // 120 seconds (2 minutes) default
1180
- enabled: true,
1181
- onTimeout: (ctx, timeoutInfo) => {
1182
- // Optional custom timeout handler
1183
- console.log(`Request timeout: ${timeoutInfo.path}`);
1100
+ this.json({ success: true, filename: safeFilename });
1184
1101
  }
1185
- });
1186
1102
 
1187
- // Register timeout middleware
1188
- master.pipeline.use(master.timeout.middleware());
1189
- ```
1103
+ uploadDocument(obj) {
1104
+ const file = obj.params.formData.files.document[0];
1190
1105
 
1191
- ### Route-Specific Timeouts
1106
+ // Allow PDF, DOC, DOCX only
1107
+ const allowedTypes = [
1108
+ 'application/pdf',
1109
+ 'application/msword',
1110
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
1111
+ ];
1192
1112
 
1193
- ```javascript
1194
- // Short timeout for API endpoints
1195
- master.timeout.setRouteTimeout('/api/*', 30000); // 30 seconds
1113
+ if (!allowedTypes.includes(file.mimetype)) {
1114
+ this.json({ error: 'Only PDF and Word documents allowed' });
1115
+ return;
1116
+ }
1117
+
1118
+ // Process upload...
1119
+ }
1120
+ }
1121
+ ```
1196
1122
 
1197
- // Long timeout for reports
1198
- master.timeout.setRouteTimeout('/admin/reports', 300000); // 5 minutes
1123
+ #### Formidable Custom Filter
1199
1124
 
1200
- // Very long timeout for batch operations
1201
- master.timeout.setRouteTimeout('/batch/process', 600000); // 10 minutes
1125
+ **Add file filter in request.json (formidable v3+):**
1126
+
1127
+ ```json
1128
+ {
1129
+ "formidable": {
1130
+ "filter": "function({ name, originalFilename, mimetype }) { return mimetype && mimetype.startsWith('image/'); }"
1131
+ }
1132
+ }
1202
1133
  ```
1203
1134
 
1204
- ### Timeout Statistics
1135
+ **Note:** JSON doesn't support functions, so filters must be configured in code:
1205
1136
 
1206
1137
  ```javascript
1207
- const stats = master.timeout.getStats();
1138
+ // config/initializers/config.js
1139
+ const formidableOptions = master.env.request.formidable;
1208
1140
 
1209
- console.log(stats);
1210
- // {
1211
- // enabled: true,
1212
- // globalTimeout: 120000,
1213
- // routeTimeouts: [
1214
- // { pattern: '/api/*', timeout: 30000 }
1215
- // ],
1216
- // activeRequests: 5,
1217
- // requests: [
1218
- // {
1219
- // requestId: 'req_1234567890_abc123',
1220
- // path: 'api/users',
1221
- // method: 'get',
1222
- // timeout: 30000,
1223
- // elapsed: 15000,
1224
- // remaining: 15000
1225
- // }
1226
- // ]
1227
- // }
1141
+ // Add runtime filter for images only
1142
+ formidableOptions.filter = function({ name, originalFilename, mimetype }) {
1143
+ return mimetype && mimetype.startsWith('image/');
1144
+ };
1145
+
1146
+ master.request.init({
1147
+ ...master.env.request,
1148
+ formidable: formidableOptions
1149
+ });
1228
1150
  ```
1229
1151
 
1230
- ### Disable/Enable Timeouts
1152
+ #### Security Best Practices
1153
+
1154
+ 1. **Always validate both MIME type AND file extension** (double check)
1155
+ 2. **Generate random filenames** (prevents overwriting and path traversal)
1156
+ 3. **Store uploads outside public directory** (prevent direct execution)
1157
+ 4. **Scan files for viruses** (use ClamAV or similar)
1158
+ 5. **Set proper file permissions** (chmod 644 for files, 755 for dirs)
1159
+ 6. **Never trust user-provided filenames** (can contain `../` or null bytes)
1160
+ 7. **Limit file sizes** (prevent disk space exhaustion)
1161
+ 8. **Delete temporary files** after processing
1162
+
1163
+ #### Delete Temporary Files
1231
1164
 
1232
1165
  ```javascript
1233
- // Disable for debugging
1234
- master.timeout.disable();
1166
+ class UploadController {
1167
+ upload(obj) {
1168
+ const file = obj.params.formData.files.upload[0];
1235
1169
 
1236
- // Re-enable
1237
- master.timeout.enable();
1170
+ try {
1171
+ // Validate and process...
1172
+
1173
+ // Delete temp file after processing
1174
+ master.request.deleteFileBuffer(file.filepath);
1175
+
1176
+ this.json({ success: true });
1177
+ } catch (error) {
1178
+ // Always cleanup on error
1179
+ master.request.deleteFileBuffer(file.filepath);
1180
+ this.json({ error: error.message });
1181
+ }
1182
+ }
1183
+ }
1238
1184
  ```
1239
1185
 
1240
1186
  ---
1241
1187
 
1242
- ## Error Handling
1188
+ ## File Conversion & Binary Data
1243
1189
 
1244
- MasterController v2.0 includes a professional error template system inspired by Rails and Django.
1190
+ MasterController v1.3.1 includes production-grade utilities for converting between files, base64, and binary data. These are essential for working with uploaded files, API responses, and data storage.
1245
1191
 
1246
- ### Error Renderer Configuration
1192
+ ### Quick Start
1247
1193
 
1248
1194
  ```javascript
1249
- // config/initializers/config.js
1250
- master.errorRenderer.init({
1251
- templateDir: 'public/errors', // Error templates directory
1252
- environment: master.environmentType,
1253
- showStackTrace: master.environmentType === 'development' // Dev only
1254
- });
1255
- ```
1195
+ // Convert uploaded file to base64 for API response
1196
+ class UploadController {
1197
+ uploadImage(obj) {
1198
+ const file = obj.params.formData.files.image[0];
1256
1199
 
1257
- ### Using Error Renderer
1200
+ // Convert to base64 (with data URI for <img> src)
1201
+ const base64 = master.tools.fileToBase64(file, {
1202
+ includeDataURI: true, // Adds "data:image/jpeg;base64," prefix
1203
+ maxSize: 5 * 1024 * 1024 // 5MB limit
1204
+ });
1258
1205
 
1259
- ```javascript
1260
- // In middleware
1261
- master.pipeline.use(async (ctx, next) => {
1262
- if (!isAuthenticated(ctx)) {
1263
- master.errorRenderer.send(ctx, 401, {
1264
- message: 'Please log in to access this resource',
1265
- suggestions: [
1266
- 'Sign in with your credentials',
1267
- 'Request a password reset if forgotten'
1268
- ]
1206
+ this.json({
1207
+ success: true,
1208
+ imageData: base64 // Can be used directly in <img src="">
1269
1209
  });
1270
- return;
1271
1210
  }
1272
- await next();
1211
+ }
1212
+ ```
1213
+
1214
+ ### File to Base64
1215
+
1216
+ #### `master.tools.fileToBase64(filePathOrFile, options)`
1217
+
1218
+ Convert a file to base64 string (binary-safe for all file types).
1219
+
1220
+ **Parameters:**
1221
+ - `filePathOrFile`: File path string OR formidable file object
1222
+ - `options`:
1223
+ - `includeDataURI` (boolean) - Prepend data URI (e.g., `data:image/jpeg;base64,`)
1224
+ - `maxSize` (number) - Maximum file size in bytes (default: 10MB)
1225
+
1226
+ **Returns:** Base64 string
1227
+
1228
+ **Examples:**
1229
+
1230
+ ```javascript
1231
+ // Convert file from file path
1232
+ const base64 = master.tools.fileToBase64('/path/to/image.jpg');
1233
+
1234
+ // Convert uploaded file with data URI
1235
+ const file = obj.params.formData.files.avatar[0];
1236
+ const dataURI = master.tools.fileToBase64(file, {
1237
+ includeDataURI: true,
1238
+ maxSize: 5 * 1024 * 1024 // 5MB
1273
1239
  });
1274
1240
 
1275
- // In controllers
1276
- class UsersController {
1277
- async show(obj) {
1278
- const userId = obj.params.userId;
1279
- const user = await this.db.query('SELECT * FROM users WHERE id = ?', [userId]);
1241
+ // Use in HTML email or response
1242
+ const html = `<img src="${dataURI}" alt="Avatar">`;
1280
1243
 
1281
- if (!user) {
1282
- master.errorRenderer.send(obj, 404, {
1283
- message: `User #${userId} not found`,
1284
- suggestions: [
1285
- 'Check the user ID',
1286
- 'Browse all users',
1287
- 'Search for the user by name'
1288
- ]
1289
- });
1290
- return;
1291
- }
1244
+ // Store in database
1245
+ await db.query('UPDATE users SET avatar = ? WHERE id = ?', [base64, userId]);
1246
+ ```
1292
1247
 
1293
- this.render('show', { user });
1248
+ **Error Handling:**
1249
+
1250
+ ```javascript
1251
+ try {
1252
+ const base64 = master.tools.fileToBase64(file);
1253
+ } catch (error) {
1254
+ if (error.message.includes('not found')) {
1255
+ console.error('File does not exist');
1256
+ } else if (error.message.includes('exceeds maximum')) {
1257
+ console.error('File too large');
1258
+ } else if (error.message.includes('directory')) {
1259
+ console.error('Path is a directory, not a file');
1294
1260
  }
1295
1261
  }
1296
1262
  ```
1297
1263
 
1298
- ### Error Templates
1264
+ ---
1299
1265
 
1300
- Create templates in `public/errors/`:
1266
+ ### Base64 to File
1301
1267
 
1302
- ```
1303
- public/errors/
1304
- ├── 400.html # Bad Request
1305
- ├── 401.html # Unauthorized
1306
- ├── 403.html # Forbidden
1307
- ├── 404.html # Not Found
1308
- ├── 405.html # Method Not Allowed
1309
- ├── 422.html # Unprocessable Entity
1310
- ├── 429.html # Too Many Requests
1311
- ├── 500.html # Internal Server Error
1312
- ├── 502.html # Bad Gateway
1313
- ├── 503.html # Service Unavailable
1314
- └── 504.html # Gateway Timeout
1315
- ```
1268
+ #### `master.tools.base64ToFile(base64String, outputPath, options)`
1316
1269
 
1317
- **Template Variables:**
1270
+ Convert base64 string to a file on disk (binary-safe).
1318
1271
 
1319
- ```html
1320
- <!DOCTYPE html>
1321
- <html>
1322
- <head>
1323
- <title>{{title}} ({{statusCode}})</title>
1324
- </head>
1325
- <body>
1326
- <h1>{{statusCode}} - {{title}}</h1>
1327
- <p>{{message}}</p>
1272
+ **Parameters:**
1273
+ - `base64String`: Base64 encoded string (with or without data URI prefix)
1274
+ - `outputPath`: Destination file path
1275
+ - `options`:
1276
+ - `overwrite` (boolean) - Allow overwriting existing files (default: false)
1277
+ - `createDir` (boolean) - Create parent directories if needed (default: true)
1328
1278
 
1329
- <!-- Conditionals (dev only) -->
1330
- {{#if showStackTrace}}
1331
- <pre>{{stack}}</pre>
1332
- {{/if}}
1279
+ **Returns:** `{ success: true, filePath: outputPath, size: number }`
1333
1280
 
1334
- <!-- Loops -->
1335
- {{#each suggestions}}
1336
- <li>{{this}}</li>
1337
- {{/each}}
1338
- </body>
1339
- </html>
1281
+ **Examples:**
1282
+
1283
+ ```javascript
1284
+ // Save base64 from API to file
1285
+ class ApiController {
1286
+ async saveImage(obj) {
1287
+ const base64Data = obj.params.formData.imageData;
1288
+
1289
+ // Save to disk
1290
+ const result = master.tools.base64ToFile(
1291
+ base64Data,
1292
+ './uploads/images/photo.jpg',
1293
+ { overwrite: false, createDir: true }
1294
+ );
1295
+
1296
+ this.json({
1297
+ success: true,
1298
+ path: result.filePath,
1299
+ size: result.size
1300
+ });
1301
+ }
1302
+ }
1303
+
1304
+ // Data URI with prefix (automatically handled)
1305
+ const dataURI = 'data:image/png;base64,iVBORw0KGgoAAAANS...';
1306
+ master.tools.base64ToFile(dataURI, './output.png');
1307
+
1308
+ // Pure base64 without prefix
1309
+ const pureBase64 = 'iVBORw0KGgoAAAANS...';
1310
+ master.tools.base64ToFile(pureBase64, './output.png');
1311
+ ```
1312
+
1313
+ ---
1314
+
1315
+ ### Buffer Operations
1316
+
1317
+ #### `master.tools.fileToBuffer(filePathOrFile, options)`
1318
+
1319
+ Convert file to Node.js Buffer (for in-memory processing).
1320
+
1321
+ **Parameters:**
1322
+ - `filePathOrFile`: File path string OR formidable file object
1323
+ - `options`:
1324
+ - `maxSize` (number) - Maximum file size (default: 10MB)
1325
+
1326
+ **Returns:** Node.js Buffer
1327
+
1328
+ **Examples:**
1329
+
1330
+ ```javascript
1331
+ // Read file into buffer
1332
+ const buffer = master.tools.fileToBuffer('./image.jpg');
1333
+
1334
+ // Process image with sharp library
1335
+ const sharp = require('sharp');
1336
+ const resized = await sharp(buffer)
1337
+ .resize(800, 600)
1338
+ .toBuffer();
1339
+
1340
+ // Convert buffer back to base64
1341
+ const base64 = master.tools.bytesToBase64(resized);
1342
+ ```
1343
+
1344
+ ---
1345
+
1346
+ #### `master.tools.fileToBytes(filePathOrFile, options)`
1347
+
1348
+ Convert file to Uint8Array (for Web APIs and TypedArrays).
1349
+
1350
+ **Parameters:**
1351
+ - `filePathOrFile`: File path string OR formidable file object
1352
+ - `options`:
1353
+ - `maxSize` (number) - Maximum file size (default: 10MB)
1354
+
1355
+ **Returns:** Uint8Array
1356
+
1357
+ **Examples:**
1358
+
1359
+ ```javascript
1360
+ // Get raw bytes
1361
+ const bytes = master.tools.fileToBytes('./document.pdf');
1362
+
1363
+ // Send over WebSocket as binary
1364
+ websocket.send(bytes);
1365
+
1366
+ // Use with crypto
1367
+ const crypto = require('crypto');
1368
+ const hash = crypto.createHash('sha256').update(bytes).digest('hex');
1369
+ ```
1370
+
1371
+ ---
1372
+
1373
+ #### `master.tools.bytesToBase64(bufferOrBytes, options)`
1374
+
1375
+ Convert Buffer or Uint8Array to base64 string.
1376
+
1377
+ **Parameters:**
1378
+ - `bufferOrBytes`: Node.js Buffer OR Uint8Array
1379
+ - `options`:
1380
+ - `includeDataURI` (boolean) - Prepend data URI
1381
+ - `mimetype` (string) - MIME type for data URI (required if includeDataURI=true)
1382
+
1383
+ **Returns:** Base64 string
1384
+
1385
+ **Examples:**
1386
+
1387
+ ```javascript
1388
+ const buffer = Buffer.from('Hello World');
1389
+ const base64 = master.tools.bytesToBase64(buffer);
1390
+ // → 'SGVsbG8gV29ybGQ='
1391
+
1392
+ // With data URI
1393
+ const base64WithURI = master.tools.bytesToBase64(buffer, {
1394
+ includeDataURI: true,
1395
+ mimetype: 'text/plain'
1396
+ });
1397
+ // → 'data:text/plain;base64,SGVsbG8gV29ybGQ='
1398
+ ```
1399
+
1400
+ ---
1401
+
1402
+ #### `master.tools.base64ToBytes(base64String)`
1403
+
1404
+ Convert base64 string to Node.js Buffer.
1405
+
1406
+ **Parameters:**
1407
+ - `base64String`: Base64 string (with or without data URI prefix)
1408
+
1409
+ **Returns:** Node.js Buffer
1410
+
1411
+ **Examples:**
1412
+
1413
+ ```javascript
1414
+ const base64 = 'SGVsbG8gV29ybGQ=';
1415
+ const buffer = master.tools.base64ToBytes(base64);
1416
+ console.log(buffer.toString('utf8')); // → 'Hello World'
1417
+
1418
+ // Handles data URIs automatically
1419
+ const dataURI = 'data:text/plain;base64,SGVsbG8gV29ybGQ=';
1420
+ const buffer2 = master.tools.base64ToBytes(dataURI);
1421
+ ```
1422
+
1423
+ ---
1424
+
1425
+ ### Streaming Large Files
1426
+
1427
+ #### `master.tools.streamFileToBase64(filePathOrFile, options)`
1428
+
1429
+ Stream large files to base64 without loading into memory (async).
1430
+
1431
+ **Parameters:**
1432
+ - `filePathOrFile`: File path string OR formidable file object
1433
+ - `options`:
1434
+ - `includeDataURI` (boolean) - Prepend data URI
1435
+ - `chunkSize` (number) - Read chunk size (default: 64KB)
1436
+ - `onProgress` (function) - Progress callback: `(bytesRead, totalBytes, percent) => {}`
1437
+
1438
+ **Returns:** Promise<base64 string>
1439
+
1440
+ **Examples:**
1441
+
1442
+ ```javascript
1443
+ // Stream large video file to base64
1444
+ class VideoController {
1445
+ async processVideo(obj) {
1446
+ const file = obj.params.formData.files.video[0];
1447
+
1448
+ // Stream with progress tracking
1449
+ const base64 = await master.tools.streamFileToBase64(file, {
1450
+ includeDataURI: true,
1451
+ chunkSize: 128 * 1024, // 128KB chunks
1452
+ onProgress: (bytesRead, total, percent) => {
1453
+ console.log(`Processing: ${percent.toFixed(1)}% (${bytesRead}/${total} bytes)`);
1454
+
1455
+ // Send progress to client via WebSocket
1456
+ master.socket.emit('upload-progress', { percent });
1457
+ }
1458
+ });
1459
+
1460
+ this.json({ success: true, videoData: base64 });
1461
+ }
1462
+ }
1463
+
1464
+ // Process 500MB file without memory issues
1465
+ const largeFile = '/path/to/500mb-video.mp4';
1466
+ const base64 = await master.tools.streamFileToBase64(largeFile, {
1467
+ onProgress: (read, total, percent) => {
1468
+ console.log(`${percent.toFixed(1)}% complete`);
1469
+ }
1470
+ });
1471
+ ```
1472
+
1473
+ ---
1474
+
1475
+ ### Common Use Cases
1476
+
1477
+ #### Use Case 1: API Response with Embedded Image
1478
+
1479
+ ```javascript
1480
+ class ProductController {
1481
+ show(obj) {
1482
+ const product = db.getProduct(obj.params.id);
1483
+ const imagePath = `./uploads/products/${product.imageFilename}`;
1484
+
1485
+ // Convert image to base64 for API
1486
+ const imageData = master.tools.fileToBase64(imagePath, {
1487
+ includeDataURI: true,
1488
+ maxSize: 2 * 1024 * 1024 // 2MB limit
1489
+ });
1490
+
1491
+ this.json({
1492
+ id: product.id,
1493
+ name: product.name,
1494
+ image: imageData // Client can use directly in <img src="">
1495
+ });
1496
+ }
1497
+ }
1498
+ ```
1499
+
1500
+ #### Use Case 2: Store File in Database
1501
+
1502
+ ```javascript
1503
+ class DocumentController {
1504
+ async upload(obj) {
1505
+ const file = obj.params.formData.files.document[0];
1506
+
1507
+ // Validate file type
1508
+ const allowedTypes = ['application/pdf', 'application/msword'];
1509
+ if (!allowedTypes.includes(file.mimetype)) {
1510
+ this.json({ error: 'Only PDF and Word documents allowed' });
1511
+ return;
1512
+ }
1513
+
1514
+ // Convert to base64 for database storage
1515
+ const base64 = master.tools.fileToBase64(file, {
1516
+ maxSize: 10 * 1024 * 1024 // 10MB
1517
+ });
1518
+
1519
+ // Store in database
1520
+ await this.db.query(
1521
+ 'INSERT INTO documents (filename, mimetype, data) VALUES (?, ?, ?)',
1522
+ [file.originalFilename, file.mimetype, base64]
1523
+ );
1524
+
1525
+ // Delete temp file
1526
+ master.request.deleteFileBuffer(file.filepath);
1527
+
1528
+ this.json({ success: true });
1529
+ }
1530
+ }
1531
+ ```
1532
+
1533
+ #### Use Case 3: Retrieve File from Database
1534
+
1535
+ ```javascript
1536
+ class DocumentController {
1537
+ async download(obj) {
1538
+ const docId = obj.params.id;
1539
+
1540
+ // Get from database
1541
+ const doc = await this.db.query(
1542
+ 'SELECT filename, mimetype, data FROM documents WHERE id = ?',
1543
+ [docId]
1544
+ );
1545
+
1546
+ if (!doc) {
1547
+ master.errorRenderer.send(obj, 404, {
1548
+ message: 'Document not found'
1549
+ });
1550
+ return;
1551
+ }
1552
+
1553
+ // Convert base64 back to file
1554
+ const tempPath = `./temp/${Date.now()}-${doc.filename}`;
1555
+ master.tools.base64ToFile(doc.data, tempPath);
1556
+
1557
+ // Send file to client
1558
+ obj.response.setHeader('Content-Type', doc.mimetype);
1559
+ obj.response.setHeader('Content-Disposition', `attachment; filename="${doc.filename}"`);
1560
+
1561
+ const fs = require('fs');
1562
+ const fileStream = fs.createReadStream(tempPath);
1563
+ fileStream.pipe(obj.response);
1564
+
1565
+ // Cleanup after sending
1566
+ fileStream.on('end', () => {
1567
+ fs.unlinkSync(tempPath);
1568
+ });
1569
+ }
1570
+ }
1571
+ ```
1572
+
1573
+ #### Use Case 4: Image Processing Pipeline
1574
+
1575
+ ```javascript
1576
+ const sharp = require('sharp');
1577
+
1578
+ class ImageController {
1579
+ async processThumbnail(obj) {
1580
+ const file = obj.params.formData.files.image[0];
1581
+
1582
+ // Read file to buffer
1583
+ const buffer = master.tools.fileToBuffer(file, {
1584
+ maxSize: 10 * 1024 * 1024
1585
+ });
1586
+
1587
+ // Process with sharp
1588
+ const thumbnail = await sharp(buffer)
1589
+ .resize(200, 200, { fit: 'cover' })
1590
+ .jpeg({ quality: 80 })
1591
+ .toBuffer();
1592
+
1593
+ // Convert thumbnail to base64
1594
+ const base64 = master.tools.bytesToBase64(thumbnail, {
1595
+ includeDataURI: true,
1596
+ mimetype: 'image/jpeg'
1597
+ });
1598
+
1599
+ // Cleanup temp file
1600
+ master.request.deleteFileBuffer(file.filepath);
1601
+
1602
+ this.json({
1603
+ success: true,
1604
+ thumbnail: base64
1605
+ });
1606
+ }
1607
+ }
1608
+ ```
1609
+
1610
+ #### Use Case 5: Email with Embedded Images
1611
+
1612
+ ```javascript
1613
+ const nodemailer = require('nodemailer');
1614
+
1615
+ class EmailController {
1616
+ async sendWithImage(obj) {
1617
+ const file = obj.params.formData.files.logo[0];
1618
+
1619
+ // Convert to base64 data URI
1620
+ const logoData = master.tools.fileToBase64(file, {
1621
+ includeDataURI: true
1622
+ });
1623
+
1624
+ // Send email with embedded image
1625
+ const transporter = nodemailer.createTransport({/* config */});
1626
+ await transporter.sendMail({
1627
+ to: 'user@example.com',
1628
+ subject: 'Welcome!',
1629
+ html: `
1630
+ <h1>Welcome to our platform!</h1>
1631
+ <img src="${logoData}" alt="Logo">
1632
+ <p>Thanks for joining.</p>
1633
+ `
1634
+ });
1635
+
1636
+ // Cleanup
1637
+ master.request.deleteFileBuffer(file.filepath);
1638
+
1639
+ this.json({ success: true });
1640
+ }
1641
+ }
1642
+ ```
1643
+
1644
+ ---
1645
+
1646
+ ### Security Best Practices
1647
+
1648
+ 1. **Always set size limits:**
1649
+ ```javascript
1650
+ const base64 = master.tools.fileToBase64(file, {
1651
+ maxSize: 5 * 1024 * 1024 // Prevent DoS
1652
+ });
1653
+ ```
1654
+
1655
+ 2. **Validate file types before conversion:**
1656
+ ```javascript
1657
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
1658
+ if (!allowedTypes.includes(file.mimetype)) {
1659
+ throw new Error('Invalid file type');
1660
+ }
1661
+ const base64 = master.tools.fileToBase64(file);
1662
+ ```
1663
+
1664
+ 3. **Delete temporary files after processing:**
1665
+ ```javascript
1666
+ try {
1667
+ const base64 = master.tools.fileToBase64(file);
1668
+ // ... process ...
1669
+ } finally {
1670
+ master.request.deleteFileBuffer(file.filepath);
1671
+ }
1672
+ ```
1673
+
1674
+ 4. **Use streaming for large files:**
1675
+ ```javascript
1676
+ // ❌ Bad: Loads entire 500MB file into memory
1677
+ const base64 = master.tools.fileToBase64(largeFile);
1678
+
1679
+ // ✅ Good: Streams in chunks
1680
+ const base64 = await master.tools.streamFileToBase64(largeFile, {
1681
+ chunkSize: 128 * 1024
1682
+ });
1683
+ ```
1684
+
1685
+ 5. **Validate base64 before decoding:**
1686
+ ```javascript
1687
+ try {
1688
+ const buffer = master.tools.base64ToBytes(untrustedBase64);
1689
+ } catch (error) {
1690
+ console.error('Invalid base64 data');
1691
+ }
1692
+ ```
1693
+
1694
+ ---
1695
+
1696
+ ### Deprecated Methods
1697
+
1698
+ #### `master.tools.base64()` - DEPRECATED
1699
+
1700
+ **⚠️ WARNING:** The original `base64()` method is **BROKEN for binary files** and should not be used. It uses `charCodeAt()` which only works correctly for text files (UTF-8). Binary files like images, PDFs, and videos will be corrupted.
1701
+
1702
+ **Do NOT use:**
1703
+ ```javascript
1704
+ // ❌ BROKEN - Corrupts binary files
1705
+ const broken = master.tools.base64(file.filepath);
1706
+ ```
1707
+
1708
+ **Use instead:**
1709
+ ```javascript
1710
+ // ✅ CORRECT - Binary-safe
1711
+ const correct = master.tools.fileToBase64(file);
1712
+ ```
1713
+
1714
+ The old method is kept for backward compatibility with text-only use cases, but all new code should use the production-grade methods documented above.
1715
+
1716
+ ---
1717
+
1718
+ ## Components
1719
+
1720
+ Components are self-contained modules with their own routes, controllers, and views.
1721
+
1722
+ ### Structure
1723
+
1724
+ ```
1725
+ components/
1726
+ user/
1727
+ config/
1728
+ initializers/
1729
+ config.js
1730
+ routes.js
1731
+ app/
1732
+ controllers/
1733
+ authController.js
1734
+ views/
1735
+ auth/
1736
+ login.html
1737
+ models/
1738
+ userContext.js
1739
+ ```
1740
+
1741
+ ### Register Component
1742
+
1743
+ ```javascript
1744
+ // In config/initializers/config.js
1745
+ master.component('components', 'user');
1746
+ master.component('components', 'mail');
1747
+ ```
1748
+
1749
+ ### Absolute Path Components
1750
+
1751
+ ```javascript
1752
+ // Load component from absolute path
1753
+ master.component('/var/www/shared-components', 'analytics');
1754
+ ```
1755
+
1756
+ Components are isolated and can be reused across projects.
1757
+
1758
+ ---
1759
+
1760
+ ## Timeout System
1761
+
1762
+ MasterController includes a production-ready timeout system with per-request tracking (Rails/Django style).
1763
+
1764
+ ### Quick Start
1765
+
1766
+ ```javascript
1767
+ // config/initializers/config.js
1768
+ const master = require('mastercontroller');
1769
+
1770
+ // Initialize timeout system
1771
+ master.timeout.init({
1772
+ globalTimeout: 120000, // 120 seconds (2 minutes) default
1773
+ enabled: true,
1774
+ onTimeout: (ctx, timeoutInfo) => {
1775
+ // Optional custom timeout handler
1776
+ console.log(`Request timeout: ${timeoutInfo.path}`);
1777
+ }
1778
+ });
1779
+
1780
+ // Register timeout middleware
1781
+ master.pipeline.use(master.timeout.middleware());
1782
+ ```
1783
+
1784
+ ### Route-Specific Timeouts
1785
+
1786
+ Configure different timeouts for different routes:
1787
+
1788
+ ```javascript
1789
+ // Short timeout for API endpoints (30 seconds)
1790
+ master.timeout.setRouteTimeout('/api/*', 30000);
1791
+
1792
+ // Long timeout for reports (5 minutes)
1793
+ master.timeout.setRouteTimeout('/admin/reports', 300000);
1794
+
1795
+ // Very long timeout for batch operations (10 minutes)
1796
+ master.timeout.setRouteTimeout('/batch/process', 600000);
1797
+
1798
+ // Critical operations (1 minute)
1799
+ master.timeout.setRouteTimeout('/checkout/*', 60000);
1800
+ ```
1801
+
1802
+ ### Environment-Specific Configuration
1803
+
1804
+ **config/environments/env.development.json:**
1805
+ ```json
1806
+ {
1807
+ "server": {
1808
+ "requestTimeout": 300000
1809
+ }
1810
+ }
1811
+ ```
1812
+
1813
+ **config/environments/env.production.json:**
1814
+ ```json
1815
+ {
1816
+ "server": {
1817
+ "requestTimeout": 120000
1818
+ }
1819
+ }
1820
+ ```
1821
+
1822
+ ### Timeout Response
1823
+
1824
+ When a request times out, the client receives:
1825
+
1826
+ ```json
1827
+ {
1828
+ "error": "Request Timeout",
1829
+ "message": "The server did not receive a complete request within the allowed time",
1830
+ "code": "MC_REQUEST_TIMEOUT",
1831
+ "timeout": 120000
1832
+ }
1833
+ ```
1834
+
1835
+ ### Monitoring Active Requests
1836
+
1837
+ ```javascript
1838
+ const stats = master.timeout.getStats();
1839
+
1840
+ console.log(stats);
1841
+ // {
1842
+ // enabled: true,
1843
+ // globalTimeout: 120000,
1844
+ // routeTimeouts: [
1845
+ // { pattern: '/api/*', timeout: 30000 },
1846
+ // { pattern: '/admin/reports', timeout: 300000 }
1847
+ // ],
1848
+ // activeRequests: 5,
1849
+ // requests: [
1850
+ // {
1851
+ // requestId: 'req_1234567890_abc123',
1852
+ // path: 'api/users',
1853
+ // method: 'get',
1854
+ // timeout: 30000,
1855
+ // elapsed: 15000,
1856
+ // remaining: 15000
1857
+ // }
1858
+ // ]
1859
+ // }
1860
+
1861
+ // Check for slow requests
1862
+ stats.requests.forEach(req => {
1863
+ if (req.elapsed > req.timeout * 0.8) {
1864
+ console.warn(`Request close to timeout: ${req.path} (${req.elapsed}ms/${req.timeout}ms)`);
1865
+ }
1866
+ });
1867
+ ```
1868
+
1869
+ ### Disable/Enable Timeouts
1870
+
1871
+ ```javascript
1872
+ // Disable for debugging
1873
+ master.timeout.disable();
1874
+
1875
+ // Re-enable
1876
+ master.timeout.enable();
1877
+
1878
+ // Check status
1879
+ console.log(master.timeout.getStats().enabled); // true/false
1880
+ ```
1881
+
1882
+ ### Complete Setup Example
1883
+
1884
+ ```javascript
1885
+ // config/initializers/config.js
1886
+ const master = require('mastercontroller');
1887
+
1888
+ // Initialize timeout system
1889
+ master.timeout.init({
1890
+ globalTimeout: master.env.server.requestTimeout || 120000,
1891
+ enabled: true,
1892
+ onTimeout: (ctx, timeoutInfo) => {
1893
+ // Log timeout
1894
+ console.error(`Request timeout: ${timeoutInfo.path} (${timeoutInfo.duration}ms)`);
1895
+
1896
+ // Send to monitoring service
1897
+ sendToMonitoring('timeout', timeoutInfo);
1898
+ }
1899
+ });
1900
+
1901
+ // Configure route-specific timeouts
1902
+ master.timeout.setRouteTimeout('/api/*', 30000); // API: 30s
1903
+ master.timeout.setRouteTimeout('/admin/reports', 300000); // Reports: 5m
1904
+ master.timeout.setRouteTimeout('/batch/*', 600000); // Batch: 10m
1905
+
1906
+ // Register middleware
1907
+ master.pipeline.use(master.timeout.middleware());
1908
+
1909
+ // Monitor timeouts periodically
1910
+ setInterval(() => {
1911
+ const stats = master.timeout.getStats();
1912
+
1913
+ if (stats.activeRequests > 100) {
1914
+ console.warn(`High number of active requests: ${stats.activeRequests}`);
1915
+ }
1916
+ }, 60000); // Every minute
1917
+ ```
1918
+
1919
+ ### Best Practices
1920
+
1921
+ 1. **Set appropriate global timeout**: 120 seconds (2 minutes) is a good default
1922
+ 2. **Use route-specific timeouts**: APIs should have shorter timeouts (30s)
1923
+ 3. **Long operations**: Use background jobs instead of long timeouts
1924
+ 4. **Disable in development**: For debugging, temporarily disable timeouts
1925
+ 5. **Monitor statistics**: Regularly check active requests and slow requests
1926
+
1927
+ ---
1928
+
1929
+ ## Error Handling
1930
+
1931
+ MasterController includes a professional error template system inspired by Rails and Django.
1932
+
1933
+ ### Quick Start
1934
+
1935
+ ```javascript
1936
+ // config/initializers/config.js
1937
+ const master = require('mastercontroller');
1938
+
1939
+ // Initialize error renderer
1940
+ master.errorRenderer.init({
1941
+ templateDir: 'public/errors', // Error templates directory
1942
+ environment: master.environmentType,
1943
+ showStackTrace: master.environmentType === 'development' // Dev only
1944
+ });
1945
+ ```
1946
+
1947
+ ### Using Error Renderer
1948
+
1949
+ **In Middleware:**
1950
+ ```javascript
1951
+ master.pipeline.use(async (ctx, next) => {
1952
+ if (!isAuthenticated(ctx)) {
1953
+ master.errorRenderer.send(ctx, 401, {
1954
+ message: 'Please log in to access this resource',
1955
+ suggestions: [
1956
+ 'Sign in with your credentials',
1957
+ 'Request a password reset if forgotten',
1958
+ 'Contact support for account issues'
1959
+ ]
1960
+ });
1961
+ return;
1962
+ }
1963
+ await next();
1964
+ });
1965
+ ```
1966
+
1967
+ **In Controllers:**
1968
+ ```javascript
1969
+ class UsersController {
1970
+ async show(obj) {
1971
+ const userId = obj.params.userId;
1972
+ const user = await this.db.query('SELECT * FROM users WHERE id = ?', [userId]);
1973
+
1974
+ if (!user) {
1975
+ master.errorRenderer.send(obj, 404, {
1976
+ message: `User #${userId} not found`,
1977
+ suggestions: [
1978
+ 'Check the user ID',
1979
+ 'Browse all users',
1980
+ 'Search for the user by name'
1981
+ ]
1982
+ });
1983
+ return;
1984
+ }
1985
+
1986
+ this.render('show', { user });
1987
+ }
1988
+
1989
+ async update(obj) {
1990
+ try {
1991
+ const userId = obj.params.id;
1992
+ const updates = obj.params.formData;
1993
+
1994
+ await this.db.query('UPDATE users SET ? WHERE id = ?', [updates, userId]);
1995
+ this.redirect(`/users/${userId}`);
1996
+ } catch (error) {
1997
+ console.error('Update failed:', error);
1998
+
1999
+ master.errorRenderer.send(obj, 500, {
2000
+ message: 'Failed to update user',
2001
+ code: 'DB_ERROR',
2002
+ stack: error.stack
2003
+ });
2004
+ }
2005
+ }
2006
+ }
2007
+ ```
2008
+
2009
+ ### Error Templates
2010
+
2011
+ Create templates in `public/errors/`:
2012
+
2013
+ ```
2014
+ public/errors/
2015
+ ├── 400.html # Bad Request
2016
+ ├── 401.html # Unauthorized
2017
+ ├── 403.html # Forbidden
2018
+ ├── 404.html # Not Found
2019
+ ├── 405.html # Method Not Allowed
2020
+ ├── 422.html # Unprocessable Entity
2021
+ ├── 429.html # Too Many Requests
2022
+ ├── 500.html # Internal Server Error
2023
+ ├── 502.html # Bad Gateway
2024
+ ├── 503.html # Service Unavailable
2025
+ └── 504.html # Gateway Timeout
2026
+ ```
2027
+
2028
+ **Template Variables:**
2029
+
2030
+ ```html
2031
+ <!DOCTYPE html>
2032
+ <html>
2033
+ <head>
2034
+ <title>{{title}} ({{statusCode}})</title>
2035
+ </head>
2036
+ <body>
2037
+ <h1>{{statusCode}} - {{title}}</h1>
2038
+ <p>{{message}}</p>
2039
+
2040
+ <!-- Conditionals (dev only) -->
2041
+ {{#if showStackTrace}}
2042
+ <pre>{{stack}}</pre>
2043
+ {{/if}}
2044
+
2045
+ <!-- Loops -->
2046
+ {{#each suggestions}}
2047
+ <li>{{this}}</li>
2048
+ {{/each}}
2049
+ </body>
2050
+ </html>
1340
2051
  ```
1341
2052
 
1342
2053
  **Available Variables:**
@@ -1348,125 +2059,1143 @@ public/errors/
1348
2059
  - `{{suggestions}}` - Array of suggestions
1349
2060
  - `{{environment}}` - Current environment
1350
2061
 
1351
- ### Custom Error Handlers
2062
+ ### Custom Error Handlers
2063
+
2064
+ Register custom error handlers for specific status codes:
2065
+
2066
+ ```javascript
2067
+ // Custom 404 handler
2068
+ master.errorRenderer.registerHandler(404, (ctx, errorData) => {
2069
+ return `
2070
+ <!DOCTYPE html>
2071
+ <html>
2072
+ <head>
2073
+ <title>Page Not Found</title>
2074
+ <style>
2075
+ body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
2076
+ .icon { font-size: 100px; }
2077
+ h1 { color: #333; }
2078
+ </style>
2079
+ </head>
2080
+ <body>
2081
+ <div class="icon">🔍</div>
2082
+ <h1>Page Not Found</h1>
2083
+ <p>${errorData.message}</p>
2084
+ <a href="/">Go Home</a>
2085
+ </body>
2086
+ </html>
2087
+ `;
2088
+ });
2089
+
2090
+ // Custom 500 handler with logging
2091
+ master.errorRenderer.registerHandler(500, (ctx, errorData) => {
2092
+ // Log to external service
2093
+ logToSentry(errorData);
2094
+
2095
+ return `
2096
+ <!DOCTYPE html>
2097
+ <html>
2098
+ <body>
2099
+ <h1>Oops! Something went wrong</h1>
2100
+ <p>Our team has been notified.</p>
2101
+ <p>Reference: ${errorData.code}</p>
2102
+ </body>
2103
+ </html>
2104
+ `;
2105
+ });
2106
+
2107
+ // Custom 503 handler (maintenance mode)
2108
+ master.errorRenderer.registerHandler(503, (ctx, errorData) => {
2109
+ return `
2110
+ <!DOCTYPE html>
2111
+ <html>
2112
+ <head>
2113
+ <title>Maintenance Mode</title>
2114
+ <style>
2115
+ body {
2116
+ font-family: Arial, sans-serif;
2117
+ text-align: center;
2118
+ padding: 50px;
2119
+ }
2120
+ .icon { font-size: 100px; }
2121
+ h1 { color: #333; }
2122
+ </style>
2123
+ </head>
2124
+ <body>
2125
+ <div class="icon">🔧</div>
2126
+ <h1>We'll be back soon!</h1>
2127
+ <p>We're performing scheduled maintenance.</p>
2128
+ <p>Expected completion: 2:00 PM EST</p>
2129
+ </body>
2130
+ </html>
2131
+ `;
2132
+ });
2133
+ ```
2134
+
2135
+ ### Content Negotiation
2136
+
2137
+ The error renderer automatically detects API requests and returns JSON:
2138
+
2139
+ ```javascript
2140
+ // Browser request → HTML
2141
+ GET /users/999
2142
+ Accept: text/html
2143
+ → Returns beautiful HTML error page
2144
+
2145
+ // API request → JSON
2146
+ GET /api/users/999
2147
+ Accept: application/json
2148
+ → Returns JSON error response
2149
+ {
2150
+ "error": "Page Not Found",
2151
+ "statusCode": 404,
2152
+ "code": "MC_HTTP_ERROR",
2153
+ "message": "The user you're looking for doesn't exist."
2154
+ }
2155
+ ```
2156
+
2157
+ ### Global Error Handler (Pipeline)
2158
+
2159
+ ```javascript
2160
+ master.pipeline.useError(async (error, ctx, next) => {
2161
+ console.error('Pipeline error:', error);
2162
+
2163
+ // Use error renderer for HTTP errors
2164
+ master.errorRenderer.send(ctx, 500, {
2165
+ message: error.message,
2166
+ code: error.code,
2167
+ stack: error.stack
2168
+ });
2169
+ });
2170
+ ```
2171
+
2172
+ ### Controller Error Handling
2173
+
2174
+ ```javascript
2175
+ class UsersController {
2176
+ async index(obj) {
2177
+ try {
2178
+ const users = await this.db.query('SELECT * FROM users');
2179
+ this.render('index', { users });
2180
+ } catch (error) {
2181
+ console.error('Database error:', error);
2182
+
2183
+ master.errorRenderer.send(obj, 500, {
2184
+ message: 'Failed to load users',
2185
+ code: 'DB_ERROR',
2186
+ stack: error.stack
2187
+ });
2188
+ }
2189
+ }
2190
+ }
2191
+ ```
2192
+
2193
+ ### Logging
2194
+
2195
+ ```javascript
2196
+ const { logger } = require('./error/MasterErrorLogger');
2197
+
2198
+ // In controllers or middleware
2199
+ logger.info({
2200
+ code: 'USER_LOGIN',
2201
+ message: 'User logged in',
2202
+ userId: user.id
2203
+ });
2204
+
2205
+ logger.warn({
2206
+ code: 'INVALID_INPUT',
2207
+ message: 'Invalid email format',
2208
+ email: input
2209
+ });
2210
+
2211
+ logger.error({
2212
+ code: 'DB_ERROR',
2213
+ message: 'Database query failed',
2214
+ error: error.message,
2215
+ stack: error.stack
2216
+ });
2217
+ ```
2218
+
2219
+ ### Common Use Cases
2220
+
2221
+ **Rate Limiting with Custom 429 Page:**
2222
+ ```javascript
2223
+ const rateLimit = new Map();
2224
+
2225
+ master.pipeline.map('/api/*', (api) => {
2226
+ api.use(async (ctx, next) => {
2227
+ const clientId = ctx.request.connection.remoteAddress;
2228
+ const requests = rateLimit.get(clientId) || [];
2229
+ const now = Date.now();
2230
+
2231
+ // Remove requests older than 1 minute
2232
+ const recent = requests.filter(time => now - time < 60000);
2233
+
2234
+ if (recent.length >= 100) {
2235
+ master.errorRenderer.send(ctx, 429, {
2236
+ message: 'Rate limit exceeded (100 requests per minute)',
2237
+ suggestions: [
2238
+ 'Wait 60 seconds and try again',
2239
+ 'Upgrade to a higher tier plan',
2240
+ 'Contact support for increased limits'
2241
+ ]
2242
+ });
2243
+ return;
2244
+ }
2245
+
2246
+ recent.push(now);
2247
+ rateLimit.set(clientId, recent);
2248
+ await next();
2249
+ });
2250
+ });
2251
+ ```
2252
+
2253
+ **Protected Admin Section:**
2254
+ ```javascript
2255
+ master.pipeline.map('/admin/*', (admin) => {
2256
+ admin.use(async (ctx, next) => {
2257
+ if (!ctx.state.user || !ctx.state.user.isAdmin) {
2258
+ master.errorRenderer.send(ctx, 403, {
2259
+ message: 'Admin access required',
2260
+ suggestions: [
2261
+ 'Sign in with an admin account',
2262
+ 'Contact an administrator for access'
2263
+ ]
2264
+ });
2265
+ return;
2266
+ }
2267
+ await next();
2268
+ });
2269
+ });
2270
+ ```
2271
+
2272
+ **Maintenance Mode:**
2273
+ ```javascript
2274
+ const maintenanceMode = process.env.MAINTENANCE === 'true';
2275
+
2276
+ if (maintenanceMode) {
2277
+ master.pipeline.use(async (ctx, next) => {
2278
+ master.errorRenderer.send(ctx, 503, {
2279
+ message: 'Service temporarily unavailable'
2280
+ });
2281
+ });
2282
+ }
2283
+ ```
2284
+
2285
+ ### Complete Setup Example
2286
+
2287
+ ```javascript
2288
+ // config/initializers/config.js
2289
+ const master = require('mastercontroller');
2290
+
2291
+ // Initialize error renderer
2292
+ master.errorRenderer.init({
2293
+ templateDir: 'public/errors',
2294
+ environment: master.environmentType,
2295
+ showStackTrace: master.environmentType === 'development'
2296
+ });
2297
+
2298
+ // Register custom handlers
2299
+ master.errorRenderer.registerHandler(404, (ctx, errorData) => {
2300
+ return `
2301
+ <!DOCTYPE html>
2302
+ <html>
2303
+ <body>
2304
+ <h1>404 - Page Not Found</h1>
2305
+ <p>${errorData.message}</p>
2306
+ <a href="/">Go Home</a>
2307
+ </body>
2308
+ </html>
2309
+ `;
2310
+ });
2311
+
2312
+ // Global error handler
2313
+ master.pipeline.useError(async (error, ctx, next) => {
2314
+ console.error('Pipeline error:', error);
2315
+
2316
+ master.errorRenderer.send(ctx, 500, {
2317
+ message: error.message,
2318
+ code: error.code,
2319
+ stack: error.stack
2320
+ });
2321
+ });
2322
+ ```
2323
+
2324
+ ### Best Practices
2325
+
2326
+ 1. **Keep error messages user-friendly**: Don't expose technical details in production
2327
+ 2. **Show stack traces in development only**: Use `showStackTrace` conditional
2328
+ 3. **Provide actionable suggestions**: Help users resolve the issue
2329
+ 4. **Consistent design**: Match your application's design
2330
+ 5. **Test all error codes**: Ensure templates render correctly
2331
+ 6. **Log errors**: Use `logger` for error tracking
2332
+ 7. **Monitor errors**: Track error rates and patterns
2333
+
2334
+ ---
2335
+
2336
+ ## HTTPS Setup
2337
+
2338
+ MasterController v1.3.2 includes **production-grade HTTPS/TLS security** with automatic secure defaults.
2339
+
2340
+ ### 🔒 Security Features (Automatic)
2341
+
2342
+ When you setup HTTPS, MasterController automatically configures:
2343
+ - ✅ **TLS 1.3** by default (2026 security standard)
2344
+ - ✅ **Secure cipher suites** (Mozilla Intermediate configuration)
2345
+ - ✅ **Path traversal protection** for static files
2346
+ - ✅ **Open redirect protection** for HTTP→HTTPS redirects
2347
+ - ✅ **SNI support** for multiple domains
2348
+ - ✅ **Certificate live reload** (zero-downtime updates)
2349
+ - ✅ **HSTS support** with preload option
2350
+
2351
+ ---
2352
+
2353
+ ## Quick Start: HTTPS in 5 Minutes
2354
+
2355
+ ### Development (Self-Signed Certificate)
2356
+
2357
+ **Step 1: Generate Self-Signed Certificate**
2358
+ ```bash
2359
+ # Create certificates directory
2360
+ mkdir -p certs
2361
+ cd certs
2362
+
2363
+ # Generate self-signed certificate (valid for 365 days)
2364
+ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
2365
+ -days 365 -nodes -subj "/CN=localhost"
2366
+
2367
+ # Combine for convenience
2368
+ cat key.pem > localhost.pem
2369
+ cat cert.pem >> localhost.pem
2370
+
2371
+ cd ..
2372
+ ```
2373
+
2374
+ **Step 2: Update `server.js`**
2375
+ ```javascript
2376
+ const fs = require('fs');
2377
+ const master = require('mastercontroller');
2378
+
2379
+ master.environmentType = process.env.NODE_ENV || 'development';
2380
+ master.root = __dirname;
2381
+
2382
+ // Setup HTTPS for development
2383
+ const server = master.setupServer('https', {
2384
+ key: fs.readFileSync('./certs/key.pem'),
2385
+ cert: fs.readFileSync('./certs/cert.pem')
2386
+ });
2387
+
2388
+ require('./config/initializers/config');
2389
+
2390
+ master.start(server);
2391
+ master.serverSettings({ httpPort: 3000 }); // Use 3000 for development
2392
+
2393
+ console.log('✅ HTTPS server running on https://localhost:3000');
2394
+ console.log('⚠️ Self-signed certificate - browser will show warning (this is normal)');
2395
+ ```
2396
+
2397
+ **Step 3: Visit `https://localhost:3000`**
2398
+ - Browser will show "Not Secure" warning
2399
+ - Click "Advanced" → "Proceed to localhost" (safe for development)
2400
+
2401
+ ---
2402
+
2403
+ ### Production (Let's Encrypt - FREE)
2404
+
2405
+ **Step 1: Install Certbot**
2406
+ ```bash
2407
+ # Ubuntu/Debian
2408
+ sudo apt update
2409
+ sudo apt install certbot
2410
+
2411
+ # CentOS/RHEL
2412
+ sudo yum install certbot
2413
+
2414
+ # macOS
2415
+ brew install certbot
2416
+ ```
2417
+
2418
+ **Step 2: Get FREE SSL Certificate**
2419
+ ```bash
2420
+ # Stop any web server on port 80
2421
+ sudo systemctl stop nginx
2422
+
2423
+ # Get certificate (replace with your domain)
2424
+ sudo certbot certonly --standalone -d yourapp.com -d www.yourapp.com
2425
+
2426
+ # Certificates will be saved to:
2427
+ # /etc/letsencrypt/live/yourapp.com/privkey.pem
2428
+ # /etc/letsencrypt/live/yourapp.com/fullchain.pem
2429
+ ```
2430
+
2431
+ **Step 3: Update `server.js` for Production**
2432
+ ```javascript
2433
+ const fs = require('fs');
2434
+ const master = require('mastercontroller');
2435
+
2436
+ master.environmentType = process.env.NODE_ENV || 'production';
2437
+ master.root = __dirname;
2438
+
2439
+ // Setup HTTPS with Let's Encrypt certificates
2440
+ const server = master.setupServer('https', {
2441
+ key: fs.readFileSync('/etc/letsencrypt/live/yourapp.com/privkey.pem'),
2442
+ cert: fs.readFileSync('/etc/letsencrypt/live/yourapp.com/fullchain.pem')
2443
+ });
2444
+
2445
+ // Enable HSTS (strongly recommended)
2446
+ master.enableHSTS({
2447
+ maxAge: 31536000, // 1 year
2448
+ includeSubDomains: true,
2449
+ preload: true
2450
+ });
2451
+
2452
+ require('./config/initializers/config');
2453
+
2454
+ // Start HTTPS on port 443
2455
+ master.start(server);
2456
+ master.serverSettings({ httpPort: 443 });
2457
+
2458
+ // Redirect HTTP to HTTPS (port 80 → 443)
2459
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
2460
+ 'yourapp.com',
2461
+ 'www.yourapp.com'
2462
+ ]);
2463
+
2464
+ console.log('========================================');
2465
+ console.log('🚀 Production Server Started');
2466
+ console.log('========================================');
2467
+ console.log('✅ HTTPS: https://yourapp.com (port 443)');
2468
+ console.log('✅ HTTP redirect: http://yourapp.com → https://yourapp.com');
2469
+ console.log('✅ TLS 1.3 enabled');
2470
+ console.log('✅ HSTS enabled (1 year)');
2471
+ console.log('✅ Secure ciphers configured');
2472
+ console.log('========================================');
2473
+ ```
2474
+
2475
+ **Step 4: Set Permissions (if needed)**
2476
+ ```bash
2477
+ # Option 1: Allow Node.js to bind to ports 80/443 (Linux)
2478
+ sudo setcap 'cap_net_bind_service=+ep' $(which node)
2479
+
2480
+ # Option 2: Run with sudo (not recommended)
2481
+ sudo node server.js
2482
+
2483
+ # Option 3: Use reverse proxy (recommended - see below)
2484
+ ```
2485
+
2486
+ **Step 5: Auto-Renew Certificates**
2487
+ ```bash
2488
+ # Certbot automatically renews certificates
2489
+ # Test renewal process:
2490
+ sudo certbot renew --dry-run
2491
+
2492
+ # Add to crontab for auto-renewal (runs daily)
2493
+ sudo crontab -e
2494
+ # Add this line:
2495
+ 0 0 * * * certbot renew --quiet --post-hook "systemctl restart myapp"
2496
+ ```
2497
+
2498
+ ---
2499
+
2500
+ ### Production (Custom Certificate)
2501
+
2502
+ If you have a certificate from a commercial CA (GoDaddy, Namecheap, etc.):
2503
+
2504
+ ```javascript
2505
+ const fs = require('fs');
2506
+ const master = require('mastercontroller');
2507
+
2508
+ master.environmentType = 'production';
2509
+ master.root = __dirname;
2510
+
2511
+ const server = master.setupServer('https', {
2512
+ key: fs.readFileSync('/path/to/your-domain.key'),
2513
+ cert: fs.readFileSync('/path/to/your-domain.crt'),
2514
+ ca: fs.readFileSync('/path/to/ca-bundle.crt') // Intermediate certificates
2515
+ });
2516
+
2517
+ master.enableHSTS({
2518
+ maxAge: 31536000,
2519
+ includeSubDomains: true,
2520
+ preload: true
2521
+ });
2522
+
2523
+ require('./config/initializers/config');
2524
+ master.start(server);
2525
+ master.serverSettings({ httpPort: 443 });
2526
+
2527
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
2528
+ 'yourapp.com',
2529
+ 'www.yourapp.com'
2530
+ ]);
2531
+ ```
2532
+
2533
+ ---
2534
+
2535
+ ---
2536
+
2537
+ ## Production Deployment Options
2538
+
2539
+ ### Option 1: Direct HTTPS (Simple, Good for Small Apps)
2540
+
2541
+ Run MasterController directly on ports 80/443:
2542
+
2543
+ ```javascript
2544
+ const fs = require('fs');
2545
+ const master = require('mastercontroller');
2546
+
2547
+ master.environmentType = 'production';
2548
+ master.root = __dirname;
2549
+
2550
+ const server = master.setupServer('https', {
2551
+ key: fs.readFileSync('/etc/letsencrypt/live/yourapp.com/privkey.pem'),
2552
+ cert: fs.readFileSync('/etc/letsencrypt/live/yourapp.com/fullchain.pem')
2553
+ });
2554
+
2555
+ master.enableHSTS({
2556
+ maxAge: 31536000,
2557
+ includeSubDomains: true,
2558
+ preload: true
2559
+ });
2560
+
2561
+ require('./config/initializers/config');
2562
+ master.start(server);
2563
+ master.serverSettings({ httpPort: 443 });
2564
+
2565
+ // HTTP redirect
2566
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
2567
+ 'yourapp.com',
2568
+ 'www.yourapp.com'
2569
+ ]);
2570
+ ```
2571
+
2572
+ **Pros:**
2573
+ - ✅ Simple setup
2574
+ - ✅ No extra software needed
2575
+ - ✅ Full control over TLS
2576
+
2577
+ **Cons:**
2578
+ - ❌ Requires root/sudo for ports 80/443
2579
+ - ❌ No load balancing
2580
+ - ❌ No static file caching
2581
+
2582
+ ---
2583
+
2584
+ ### Option 2: Nginx Reverse Proxy (Recommended for Production)
2585
+
2586
+ Run MasterController on high port (3000) behind Nginx on ports 80/443:
2587
+
2588
+ **Step 1: Install Nginx**
2589
+ ```bash
2590
+ # Ubuntu/Debian
2591
+ sudo apt update
2592
+ sudo apt install nginx
2593
+
2594
+ # CentOS/RHEL
2595
+ sudo yum install nginx
2596
+
2597
+ # macOS
2598
+ brew install nginx
2599
+ ```
2600
+
2601
+ **Step 2: Configure MasterController (High Port)**
2602
+ ```javascript
2603
+ // server.js - Run on port 3000
2604
+ const master = require('mastercontroller');
2605
+
2606
+ master.environmentType = 'production';
2607
+ master.root = __dirname;
2608
+
2609
+ // HTTP only (Nginx handles HTTPS)
2610
+ const server = master.setupServer('http');
2611
+
2612
+ require('./config/initializers/config');
2613
+ master.start(server);
2614
+ master.serverSettings({
2615
+ httpPort: 3000,
2616
+ hostname: '127.0.0.1' // Only accept local connections
2617
+ });
2618
+
2619
+ console.log('✅ Server running on http://127.0.0.1:3000');
2620
+ console.log('⚠️ Behind Nginx reverse proxy');
2621
+ ```
2622
+
2623
+ **Step 3: Configure Nginx**
2624
+ ```nginx
2625
+ # /etc/nginx/sites-available/yourapp.com
2626
+
2627
+ # HTTP redirect to HTTPS
2628
+ server {
2629
+ listen 80;
2630
+ listen [::]:80;
2631
+ server_name yourapp.com www.yourapp.com;
2632
+
2633
+ return 301 https://$server_name$request_uri;
2634
+ }
2635
+
2636
+ # HTTPS server
2637
+ server {
2638
+ listen 443 ssl http2;
2639
+ listen [::]:443 ssl http2;
2640
+ server_name yourapp.com www.yourapp.com;
2641
+
2642
+ # SSL Configuration (Let's Encrypt)
2643
+ ssl_certificate /etc/letsencrypt/live/yourapp.com/fullchain.pem;
2644
+ ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;
2645
+ ssl_trusted_certificate /etc/letsencrypt/live/yourapp.com/chain.pem;
2646
+
2647
+ # Modern TLS configuration (matches MasterController defaults)
2648
+ ssl_protocols TLSv1.3 TLSv1.2;
2649
+ ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES256-GCM-SHA384';
2650
+ ssl_prefer_server_ciphers on;
2651
+
2652
+ # HSTS
2653
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
2654
+
2655
+ # Security headers
2656
+ add_header X-Frame-Options "SAMEORIGIN" always;
2657
+ add_header X-Content-Type-Options "nosniff" always;
2658
+ add_header X-XSS-Protection "1; mode=block" always;
2659
+
2660
+ # Proxy to MasterController
2661
+ location / {
2662
+ proxy_pass http://127.0.0.1:3000;
2663
+ proxy_http_version 1.1;
2664
+
2665
+ # WebSocket support
2666
+ proxy_set_header Upgrade $http_upgrade;
2667
+ proxy_set_header Connection 'upgrade';
2668
+
2669
+ # Forward real client IP
2670
+ proxy_set_header Host $host;
2671
+ proxy_set_header X-Real-IP $remote_addr;
2672
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
2673
+ proxy_set_header X-Forwarded-Proto $scheme;
2674
+
2675
+ # Timeouts
2676
+ proxy_connect_timeout 60s;
2677
+ proxy_send_timeout 60s;
2678
+ proxy_read_timeout 60s;
2679
+
2680
+ proxy_cache_bypass $http_upgrade;
2681
+ }
2682
+
2683
+ # Static file caching (optional)
2684
+ location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
2685
+ proxy_pass http://127.0.0.1:3000;
2686
+ expires 1y;
2687
+ add_header Cache-Control "public, immutable";
2688
+ }
2689
+
2690
+ # Gzip compression
2691
+ gzip on;
2692
+ gzip_vary on;
2693
+ gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
2694
+ }
2695
+ ```
2696
+
2697
+ **Step 4: Enable Nginx Configuration**
2698
+ ```bash
2699
+ # Create symlink to enable site
2700
+ sudo ln -s /etc/nginx/sites-available/yourapp.com /etc/nginx/sites-enabled/
2701
+
2702
+ # Test configuration
2703
+ sudo nginx -t
2704
+
2705
+ # Reload Nginx
2706
+ sudo systemctl reload nginx
2707
+
2708
+ # Enable Nginx on boot
2709
+ sudo systemctl enable nginx
2710
+ ```
2711
+
2712
+ **Step 5: Configure MasterController to Trust Proxy**
2713
+ ```javascript
2714
+ // config/initializers/config.js
2715
+ const master = require('mastercontroller');
2716
+
2717
+ // Trust X-Forwarded-* headers from Nginx
2718
+ master.pipeline.use(async (ctx, next) => {
2719
+ // Get real client IP from X-Forwarded-For
2720
+ const forwardedFor = ctx.request.headers['x-forwarded-for'];
2721
+ if (forwardedFor) {
2722
+ ctx.request.clientIp = forwardedFor.split(',')[0].trim();
2723
+ }
2724
+
2725
+ // Trust X-Forwarded-Proto for HTTPS detection
2726
+ if (ctx.request.headers['x-forwarded-proto'] === 'https') {
2727
+ ctx.request.isHttps = true;
2728
+ }
2729
+
2730
+ await next();
2731
+ });
2732
+
2733
+ // ... rest of config
2734
+ ```
2735
+
2736
+ **Pros:**
2737
+ - ✅ No root/sudo needed for Node.js
2738
+ - ✅ Static file caching
2739
+ - ✅ Load balancing support
2740
+ - ✅ Better performance
2741
+ - ✅ Easier certificate management
2742
+ - ✅ Industry standard
2743
+
2744
+ **Cons:**
2745
+ - ❌ Extra complexity
2746
+ - ❌ Another service to maintain
2747
+
2748
+ ---
2749
+
2750
+ ### Option 3: PM2 with Nginx (Best for Production)
2751
+
2752
+ Combine PM2 process manager with Nginx:
2753
+
2754
+ **Step 1: Install PM2**
2755
+ ```bash
2756
+ npm install -g pm2
2757
+ ```
2758
+
2759
+ **Step 2: Create PM2 Ecosystem File**
2760
+ ```javascript
2761
+ // ecosystem.config.js
2762
+ module.exports = {
2763
+ apps: [{
2764
+ name: 'myapp',
2765
+ script: './server.js',
2766
+ instances: 'max', // Use all CPU cores
2767
+ exec_mode: 'cluster',
2768
+ env: {
2769
+ NODE_ENV: 'production'
2770
+ },
2771
+ error_file: './logs/err.log',
2772
+ out_file: './logs/out.log',
2773
+ log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
2774
+ merge_logs: true
2775
+ }]
2776
+ };
2777
+ ```
2778
+
2779
+ **Step 3: Start with PM2**
2780
+ ```bash
2781
+ # Start application
2782
+ pm2 start ecosystem.config.js
2783
+
2784
+ # Save PM2 configuration
2785
+ pm2 save
2786
+
2787
+ # Setup PM2 to start on system boot
2788
+ pm2 startup
2789
+
2790
+ # Monitor application
2791
+ pm2 monit
2792
+
2793
+ # View logs
2794
+ pm2 logs
2795
+
2796
+ # Restart application
2797
+ pm2 restart myapp
2798
+
2799
+ # Reload with zero downtime
2800
+ pm2 reload myapp
2801
+ ```
2802
+
2803
+ **Step 4: Configure Nginx** (same as Option 2)
2804
+
2805
+ **Pros:**
2806
+ - ✅ Auto-restart on crash
2807
+ - ✅ Zero-downtime deployments
2808
+ - ✅ Cluster mode (use all CPU cores)
2809
+ - ✅ Log management
2810
+ - ✅ Monitoring
2811
+ - ✅ Auto-start on boot
2812
+
2813
+ ---
2814
+
2815
+ ---
2816
+
2817
+ ## Advanced HTTPS Configuration
2818
+
2819
+ ### Multiple Domains (SNI - Server Name Indication)
2820
+
2821
+ MasterController supports serving multiple domains with different certificates:
2822
+
2823
+ **Method 1: Using Environment Configuration**
2824
+ ```json
2825
+ // config/environments/env.production.json
2826
+ {
2827
+ "server": {
2828
+ "httpPort": 443,
2829
+ "tls": {
2830
+ "default": {
2831
+ "keyPath": "/etc/letsencrypt/live/example.com/privkey.pem",
2832
+ "certPath": "/etc/letsencrypt/live/example.com/fullchain.pem"
2833
+ },
2834
+ "sni": {
2835
+ "api.example.com": {
2836
+ "keyPath": "/etc/letsencrypt/live/api.example.com/privkey.pem",
2837
+ "certPath": "/etc/letsencrypt/live/api.example.com/fullchain.pem"
2838
+ },
2839
+ "admin.example.com": {
2840
+ "keyPath": "/etc/letsencrypt/live/admin.example.com/privkey.pem",
2841
+ "certPath": "/etc/letsencrypt/live/admin.example.com/fullchain.pem"
2842
+ }
2843
+ },
2844
+ "hsts": true,
2845
+ "hstsMaxAge": 31536000
2846
+ }
2847
+ }
2848
+ }
2849
+ ```
2850
+
2851
+ ```javascript
2852
+ // server.js
2853
+ const master = require('mastercontroller');
2854
+
2855
+ master.environmentType = 'production';
2856
+ master.root = __dirname;
2857
+
2858
+ // Loads TLS config from environment file (including SNI)
2859
+ const server = master.setupServer('https');
2860
+
2861
+ require('./config/initializers/config');
2862
+ master.start(server);
2863
+ master.serverSettings(master.env.server);
2864
+
2865
+ console.log('✅ HTTPS with SNI enabled');
2866
+ console.log(' • example.com');
2867
+ console.log(' • api.example.com');
2868
+ console.log(' • admin.example.com');
2869
+ ```
1352
2870
 
2871
+ **Method 2: Programmatic SNI**
1353
2872
  ```javascript
1354
- // Register custom handler for specific status code
1355
- master.errorRenderer.registerHandler(503, (ctx, errorData) => {
1356
- return `
1357
- <!DOCTYPE html>
1358
- <html>
1359
- <body>
1360
- <h1>Maintenance Mode</h1>
1361
- <p>We'll be back soon! Expected completion: 2:00 PM EST</p>
1362
- </body>
1363
- </html>
1364
- `;
2873
+ const fs = require('fs');
2874
+ const tls = require('tls');
2875
+ const master = require('mastercontroller');
2876
+
2877
+ master.environmentType = 'production';
2878
+ master.root = __dirname;
2879
+
2880
+ // Default certificate
2881
+ const server = master.setupServer('https', {
2882
+ key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
2883
+ cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem'),
2884
+
2885
+ // SNI callback for different domains
2886
+ SNICallback: (servername, cb) => {
2887
+ let ctx;
2888
+
2889
+ switch(servername) {
2890
+ case 'api.example.com':
2891
+ ctx = tls.createSecureContext({
2892
+ key: fs.readFileSync('/etc/letsencrypt/live/api.example.com/privkey.pem'),
2893
+ cert: fs.readFileSync('/etc/letsencrypt/live/api.example.com/fullchain.pem')
2894
+ });
2895
+ break;
2896
+
2897
+ case 'admin.example.com':
2898
+ ctx = tls.createSecureContext({
2899
+ key: fs.readFileSync('/etc/letsencrypt/live/admin.example.com/privkey.pem'),
2900
+ cert: fs.readFileSync('/etc/letsencrypt/live/admin.example.com/fullchain.pem')
2901
+ });
2902
+ break;
2903
+
2904
+ default:
2905
+ // Use default certificate
2906
+ ctx = null;
2907
+ }
2908
+
2909
+ cb(null, ctx);
2910
+ }
1365
2911
  });
2912
+
2913
+ require('./config/initializers/config');
2914
+ master.start(server);
2915
+ master.serverSettings({ httpPort: 443 });
1366
2916
  ```
1367
2917
 
1368
- ### Content Negotiation
2918
+ ---
1369
2919
 
1370
- The error renderer automatically detects API requests and returns JSON:
2920
+ ### HTTP to HTTPS Redirect (Secure)
2921
+
2922
+ **⚠️ SECURITY:** Always specify allowed hosts to prevent open redirect attacks!
1371
2923
 
1372
2924
  ```javascript
1373
- // Browser request HTML
1374
- GET /users/999
1375
- Accept: text/html
1376
- → Returns beautiful HTML error page
2925
+ // SECURE: Validate host header against whitelist
2926
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
2927
+ 'example.com',
2928
+ 'www.example.com',
2929
+ 'api.example.com'
2930
+ ]);
1377
2931
 
1378
- // API request JSON
1379
- GET /api/users/999
1380
- Accept: application/json
1381
- → Returns JSON error response
1382
- {
1383
- "error": "Page Not Found",
1384
- "statusCode": 404,
1385
- "code": "MC_HTTP_ERROR",
1386
- "message": "The user you're looking for doesn't exist."
1387
- }
2932
+ console.log('✅ HTTP redirect server running on port 80');
1388
2933
  ```
1389
2934
 
1390
- ### Global Error Handler (Pipeline)
2935
+ **Why host validation?** Without it, attackers can redirect users to malicious domains:
2936
+ ```bash
2937
+ # Attack without validation:
2938
+ curl -H "Host: evil.com" http://example.com
2939
+ # Redirects to: https://evil.com (phishing!)
2940
+
2941
+ # With validation: Returns 400 Bad Request ✅
2942
+ ```
1391
2943
 
2944
+ **For Multiple Domains:**
1392
2945
  ```javascript
1393
- master.pipeline.useError(async (error, ctx, next) => {
1394
- console.error('Pipeline error:', error);
2946
+ // Redirect all domains
2947
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
2948
+ 'example.com',
2949
+ 'www.example.com',
2950
+ 'api.example.com',
2951
+ 'admin.example.com',
2952
+ 'blog.example.com'
2953
+ ]);
2954
+ ```
1395
2955
 
1396
- // Use error renderer for HTTP errors
1397
- master.errorRenderer.send(ctx, 500, {
1398
- message: error.message,
1399
- code: error.code,
1400
- stack: error.stack
1401
- });
1402
- });
2956
+ ---
2957
+
2958
+ ### Certificate Renewal (Let's Encrypt)
2959
+
2960
+ **Automatic Renewal (Recommended)**
2961
+
2962
+ Let's Encrypt certificates expire after 90 days. Setup automatic renewal:
2963
+
2964
+ ```bash
2965
+ # Method 1: Systemd timer (Ubuntu/Debian - already configured)
2966
+ sudo systemctl status certbot.timer
2967
+
2968
+ # Method 2: Crontab (manual setup)
2969
+ sudo crontab -e
2970
+ # Add this line (runs twice daily):
2971
+ 0 0,12 * * * certbot renew --quiet --post-hook "systemctl restart myapp"
2972
+
2973
+ # Method 3: PM2 with reload hook
2974
+ sudo crontab -e
2975
+ # Add this line:
2976
+ 0 0 * * * certbot renew --quiet --post-hook "pm2 reload myapp"
1403
2977
  ```
1404
2978
 
1405
- ### Controller Error Handling
2979
+ **Manual Renewal**
2980
+ ```bash
2981
+ # Test renewal (dry run)
2982
+ sudo certbot renew --dry-run
1406
2983
 
1407
- ```javascript
1408
- class UsersController {
1409
- async index(obj) {
1410
- try {
1411
- const users = await this.db.query('SELECT * FROM users');
1412
- this.render('index', { users });
1413
- } catch (error) {
1414
- console.error('Database error:', error);
2984
+ # Actually renew certificates
2985
+ sudo certbot renew
1415
2986
 
1416
- master.errorRenderer.send(obj, 500, {
1417
- message: 'Failed to load users',
1418
- code: 'DB_ERROR',
1419
- stack: error.stack
1420
- });
1421
- }
1422
- }
1423
- }
2987
+ # Restart your application
2988
+ pm2 restart myapp
2989
+ # or
2990
+ sudo systemctl restart myapp
1424
2991
  ```
1425
2992
 
1426
- ### Logging
2993
+ **Certificate Live Reload (Zero Downtime)**
2994
+
2995
+ MasterController supports certificate live reload with `fs.watchFile()`:
1427
2996
 
1428
2997
  ```javascript
1429
- const { logger } = require('./error/MasterErrorLogger');
2998
+ const fs = require('fs');
2999
+ const master = require('mastercontroller');
1430
3000
 
1431
- // In controllers or middleware
1432
- logger.info({
1433
- code: 'USER_LOGIN',
1434
- message: 'User logged in',
1435
- userId: user.id
1436
- });
3001
+ const certPath = '/etc/letsencrypt/live/example.com/fullchain.pem';
3002
+ const keyPath = '/etc/letsencrypt/live/example.com/privkey.pem';
1437
3003
 
1438
- logger.warn({
1439
- code: 'INVALID_INPUT',
1440
- message: 'Invalid email format',
1441
- email: input
3004
+ let server = master.setupServer('https', {
3005
+ key: fs.readFileSync(keyPath),
3006
+ cert: fs.readFileSync(certPath)
1442
3007
  });
1443
3008
 
1444
- logger.error({
1445
- code: 'DB_ERROR',
1446
- message: 'Database query failed',
1447
- error: error.message,
1448
- stack: error.stack
3009
+ // Watch for certificate changes
3010
+ fs.watchFile(certPath, (curr, prev) => {
3011
+ console.log('📝 Certificate changed, reloading...');
3012
+
3013
+ try {
3014
+ // Reload certificates without restarting server
3015
+ const newCert = fs.readFileSync(certPath);
3016
+ const newKey = fs.readFileSync(keyPath);
3017
+
3018
+ // Update server context
3019
+ server.setSecureContext({
3020
+ key: newKey,
3021
+ cert: newCert
3022
+ });
3023
+
3024
+ console.log('✅ Certificate reloaded successfully (zero downtime)');
3025
+ } catch (error) {
3026
+ console.error('❌ Failed to reload certificate:', error);
3027
+ }
1449
3028
  });
3029
+
3030
+ require('./config/initializers/config');
3031
+ master.start(server);
3032
+ master.serverSettings({ httpPort: 443 });
1450
3033
  ```
1451
3034
 
1452
3035
  ---
1453
3036
 
1454
- ## HTTPS Setup
3037
+ ### Common Errors and Solutions
3038
+
3039
+ #### Error: "EACCES: permission denied, bind 80" or "bind 443"
3040
+
3041
+ **Problem:** Node.js doesn't have permission to bind to ports 80/443.
3042
+
3043
+ **Solutions:**
3044
+
3045
+ **Option 1: Use setcap (Linux - Recommended)**
3046
+ ```bash
3047
+ # Give Node.js permission to bind to privileged ports
3048
+ sudo setcap 'cap_net_bind_service=+ep' $(which node)
3049
+
3050
+ # Verify
3051
+ getcap $(which node)
3052
+ # Output: /usr/bin/node = cap_net_bind_service+ep
3053
+ ```
3054
+
3055
+ **Option 2: Run as root (Not Recommended)**
3056
+ ```bash
3057
+ sudo node server.js
3058
+ ```
3059
+
3060
+ **Option 3: Use reverse proxy (Best)**
3061
+ ```bash
3062
+ # Run Node.js on port 3000 (no permissions needed)
3063
+ # Use Nginx on ports 80/443
3064
+ ```
3065
+
3066
+ **Option 4: Use authbind (Linux)**
3067
+ ```bash
3068
+ sudo apt install authbind
3069
+ sudo touch /etc/authbind/byport/80
3070
+ sudo touch /etc/authbind/byport/443
3071
+ sudo chmod 500 /etc/authbind/byport/80
3072
+ sudo chmod 500 /etc/authbind/byport/443
3073
+ sudo chown $USER /etc/authbind/byport/80
3074
+ sudo chown $USER /etc/authbind/byport/443
3075
+
3076
+ # Run with authbind
3077
+ authbind --deep node server.js
3078
+ ```
3079
+
3080
+ ---
3081
+
3082
+ #### Error: "ENOENT: no such file or directory, open '/path/to/cert.pem'"
3083
+
3084
+ **Problem:** Certificate files don't exist or path is wrong.
3085
+
3086
+ **Solutions:**
3087
+
3088
+ ```bash
3089
+ # Check if files exist
3090
+ ls -l /etc/letsencrypt/live/yourapp.com/
3091
+
3092
+ # Check permissions
3093
+ sudo ls -l /etc/letsencrypt/live/yourapp.com/
3094
+ # If you see permission denied, you need to run as root or copy certs
1455
3095
 
1456
- ### Basic HTTPS
3096
+ # Copy certificates to accessible location (if needed)
3097
+ sudo cp /etc/letsencrypt/live/yourapp.com/privkey.pem ~/certs/
3098
+ sudo cp /etc/letsencrypt/live/yourapp.com/fullchain.pem ~/certs/
3099
+ sudo chown $USER:$USER ~/certs/*.pem
3100
+ ```
3101
+
3102
+ ---
3103
+
3104
+ #### Error: "unable to verify the first certificate"
3105
+
3106
+ **Problem:** Missing intermediate certificates (chain).
3107
+
3108
+ **Solution:**
1457
3109
 
1458
3110
  ```javascript
1459
- const fs = require('fs');
3111
+ // Use fullchain.pem instead of cert.pem
3112
+ const server = master.setupServer('https', {
3113
+ key: fs.readFileSync('/path/to/privkey.pem'),
3114
+ cert: fs.readFileSync('/path/to/fullchain.pem'), // NOT cert.pem!
3115
+ ca: fs.readFileSync('/path/to/chain.pem') // Optional: explicit chain
3116
+ });
3117
+ ```
3118
+
3119
+ ---
3120
+
3121
+ #### Error: "cert has expired"
3122
+
3123
+ **Problem:** SSL certificate has expired.
3124
+
3125
+ **Solutions:**
3126
+
3127
+ ```bash
3128
+ # Check expiration date
3129
+ openssl x509 -in /etc/letsencrypt/live/yourapp.com/fullchain.pem -noout -dates
1460
3130
 
1461
- const credentials = {
1462
- key: fs.readFileSync('path/to/key.pem'),
1463
- cert: fs.readFileSync('path/to/cert.pem')
3131
+ # Renew certificate
3132
+ sudo certbot renew
3133
+
3134
+ # Restart application
3135
+ pm2 restart myapp
3136
+ ```
3137
+
3138
+ ---
3139
+
3140
+ #### Error: Browser shows "Not Secure" or "NET::ERR_CERT_AUTHORITY_INVALID"
3141
+
3142
+ **Problem:** Self-signed certificate (development) or certificate not trusted.
3143
+
3144
+ **Solutions:**
3145
+
3146
+ **For Development (Self-Signed):**
3147
+ 1. Click "Advanced" in browser
3148
+ 2. Click "Proceed to localhost" (safe for development)
3149
+ 3. Or add certificate to system trust store
3150
+
3151
+ **For Production:**
3152
+ 1. Use Let's Encrypt or commercial CA certificate
3153
+ 2. Ensure fullchain.pem includes intermediate certificates
3154
+ 3. Check certificate matches domain name
3155
+
3156
+ ---
3157
+
3158
+ #### Browser keeps redirecting between HTTP and HTTPS (Loop)
3159
+
3160
+ **Problem:** Redirect loop, usually caused by incorrect proxy configuration.
3161
+
3162
+ **Solution:**
3163
+
3164
+ ```javascript
3165
+ // config/initializers/config.js
3166
+ // Configure hostname in environment file
3167
+ master.env = {
3168
+ server: {
3169
+ hostname: 'yourapp.com', // Set this!
3170
+ httpsPort: 443
3171
+ }
1464
3172
  };
1465
3173
 
1466
- const server = master.setupServer('https', credentials);
3174
+ // For Nginx proxy, make sure X-Forwarded-Proto is set correctly
3175
+ ```
3176
+
3177
+ ---
3178
+
3179
+ ### HSTS (HTTP Strict Transport Security)
3180
+
3181
+ HSTS tells browsers to always use HTTPS for your domain (prevents downgrade attacks).
3182
+
3183
+ ```javascript
3184
+ // Basic usage (1 year, includeSubDomains)
3185
+ master.enableHSTS();
3186
+
3187
+ // Custom configuration
3188
+ master.enableHSTS({
3189
+ maxAge: 15552000, // 180 days
3190
+ includeSubDomains: true, // Cover *.example.com
3191
+ preload: false // Don't submit to preload list yet
3192
+ });
1467
3193
  ```
1468
3194
 
1469
- ### Environment-based TLS
3195
+ **HSTS Preload List:**
3196
+ After running HSTS for 30+ days, submit to [hstspreload.org](https://hstspreload.org/) for browser built-in enforcement.
3197
+
3198
+ ### Environment-based TLS Configuration
1470
3199
 
1471
3200
  Configure TLS in `config/environments/env.production.json`:
1472
3201
 
@@ -1488,33 +3217,206 @@ Configure TLS in `config/environments/env.production.json`:
1488
3217
  "keyPath": "/path/to/app.key",
1489
3218
  "certPath": "/path/to/app.crt"
1490
3219
  }
1491
- }
3220
+ },
3221
+ "hsts": true,
3222
+ "hstsMaxAge": 31536000
1492
3223
  }
1493
3224
  }
1494
3225
  }
1495
3226
  ```
1496
3227
 
1497
3228
  ```javascript
3229
+ // Loads TLS config from environment file
1498
3230
  const server = master.setupServer('https');
1499
3231
  master.serverSettings(master.env.server);
1500
3232
  ```
1501
3233
 
1502
- ### HTTP to HTTPS Redirect
3234
+ **Features:**
3235
+ - ✅ **SNI Support** - Different certificates for different domains
3236
+ - ✅ **Live Reload** - Update certificates without restarting server
3237
+ - ✅ **HSTS Configuration** - Automatic HSTS from config
3238
+
3239
+ ### Advanced TLS Configuration
3240
+
3241
+ #### Custom TLS Version
3242
+ ```javascript
3243
+ const server = master.setupServer('https', {
3244
+ key: fs.readFileSync('/path/to/key.pem'),
3245
+ cert: fs.readFileSync('/path/to/cert.pem'),
3246
+ minVersion: 'TLSv1.2', // Override default TLS 1.3 (for compatibility)
3247
+ maxVersion: 'TLSv1.3'
3248
+ });
3249
+ ```
3250
+
3251
+ #### Custom Cipher Suites
3252
+ ```javascript
3253
+ const server = master.setupServer('https', {
3254
+ key: fs.readFileSync('/path/to/key.pem'),
3255
+ cert: fs.readFileSync('/path/to/cert.pem'),
3256
+ ciphers: [
3257
+ 'TLS_AES_256_GCM_SHA384',
3258
+ 'TLS_CHACHA20_POLY1305_SHA256',
3259
+ 'ECDHE-RSA-AES256-GCM-SHA384'
3260
+ ].join(':')
3261
+ });
3262
+ ```
3263
+
3264
+ **Note:** MasterController uses secure defaults. Only customize if you have specific requirements.
3265
+
3266
+ ### Let's Encrypt Example
1503
3267
 
1504
3268
  ```javascript
1505
- // Start HTTPS server on 443
1506
- const httpsServer = master.setupServer('https');
1507
- master.start(httpsServer);
3269
+ // Let's Encrypt certificates (auto-renewed by certbot)
3270
+ const server = master.setupServer('https', {
3271
+ key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
3272
+ cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem')
3273
+ });
3274
+
3275
+ master.enableHSTS({
3276
+ maxAge: 31536000,
3277
+ includeSubDomains: true,
3278
+ preload: true
3279
+ });
3280
+
3281
+ master.start(server);
1508
3282
  master.serverSettings({ httpPort: 443 });
1509
3283
 
1510
- // Start redirect server on 80
1511
- const redirectServer = master.startHttpToHttpsRedirect(80);
3284
+ // Secure HTTP redirect
3285
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
3286
+ 'example.com',
3287
+ 'www.example.com'
3288
+ ]);
1512
3289
  ```
1513
3290
 
1514
- ### HSTS (HTTP Strict Transport Security)
3291
+ ### Testing Your HTTPS Setup
3292
+
3293
+ #### 1. SSL Labs Test
3294
+ ```bash
3295
+ # Test your HTTPS configuration (should get A or A+)
3296
+ https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com
3297
+ ```
3298
+
3299
+ #### 2. Local Testing
3300
+ ```bash
3301
+ # Test TLS 1.3
3302
+ curl -v --tlsv1.3 https://localhost
3303
+
3304
+ # Test HSTS header
3305
+ curl -I https://localhost | grep Strict-Transport-Security
3306
+
3307
+ # Test HTTP redirect
3308
+ curl -I http://localhost
3309
+
3310
+ # Test path traversal protection (should return 403)
3311
+ curl http://localhost/../../../etc/passwd
3312
+ ```
3313
+
3314
+ #### 3. Cipher Suite Testing
3315
+ ```bash
3316
+ # Use testssl.sh for comprehensive testing
3317
+ ./testssl.sh --full https://yourdomain.com
3318
+
3319
+ # Or nmap
3320
+ nmap --script ssl-enum-ciphers -p 443 yourdomain.com
3321
+ ```
3322
+
3323
+ ### Security Comparison
3324
+
3325
+ MasterController's HTTPS implementation **exceeds industry standards**:
3326
+
3327
+ | Feature | MasterController v1.3.1 | Express | ASP.NET Core | Rails |
3328
+ |---------|-------------------------|---------|--------------|-------|
3329
+ | **TLS 1.3 Default** | ✅ | ❌ | ❌ | ❌ |
3330
+ | **Secure Ciphers** | ✅ Auto | ❌ Manual | ⚠️ Partial | ❌ Manual |
3331
+ | **Path Traversal Protection** | ✅ | ✅ | ✅ | ✅ |
3332
+ | **Open Redirect Protection** | ✅ | ✅ | ✅ | ✅ |
3333
+ | **SNI Support** | ✅ Built-in | ❌ Manual | ✅ | ❌ Manual |
3334
+ | **Certificate Live Reload** | ✅ **Unique!** | ❌ | ❌ | ❌ |
3335
+ | **HSTS Built-in** | ✅ | Via helmet | ✅ | ✅ |
3336
+
3337
+ ### Complete Production Example
1515
3338
 
1516
3339
  ```javascript
1517
- master.enableHSTS(); // In production HTTPS
3340
+ // server.js - Production HTTPS setup
3341
+ const master = require('mastercontroller');
3342
+ const fs = require('fs');
3343
+
3344
+ // Set environment
3345
+ master.environmentType = process.env.NODE_ENV || 'production';
3346
+ master.root = __dirname;
3347
+
3348
+ // Setup HTTPS with Let's Encrypt certificates
3349
+ const server = master.setupServer('https', {
3350
+ key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
3351
+ cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem')
3352
+ });
3353
+
3354
+ // Enable HSTS with preload
3355
+ master.enableHSTS({
3356
+ maxAge: 31536000, // 1 year
3357
+ includeSubDomains: true,
3358
+ preload: true // Submit to hstspreload.org after 30 days
3359
+ });
3360
+
3361
+ // Load application configuration
3362
+ require('./config/initializers/config');
3363
+
3364
+ // Start HTTPS server on port 443
3365
+ master.start(server);
3366
+ master.serverSettings({ httpPort: 443 });
3367
+
3368
+ // Start HTTP to HTTPS redirect with host validation
3369
+ const redirectServer = master.startHttpToHttpsRedirect(80, '0.0.0.0', [
3370
+ 'example.com',
3371
+ 'www.example.com',
3372
+ 'api.example.com',
3373
+ 'admin.example.com'
3374
+ ]);
3375
+
3376
+ console.log('========================================');
3377
+ console.log('🚀 MasterController Production Server');
3378
+ console.log('========================================');
3379
+ console.log('✅ HTTPS on port 443');
3380
+ console.log('✅ HTTP redirect on port 80');
3381
+ console.log('✅ TLS 1.3 enabled');
3382
+ console.log('✅ Secure cipher suites');
3383
+ console.log('✅ HSTS enabled (max-age: 1 year)');
3384
+ console.log('✅ Path traversal protection');
3385
+ console.log('✅ Open redirect protection');
3386
+ console.log('========================================');
3387
+ ```
3388
+
3389
+ ### Troubleshooting
3390
+
3391
+ **Certificate Errors:**
3392
+ ```bash
3393
+ # Check certificate expiration
3394
+ openssl x509 -in /path/to/cert.pem -noout -dates
3395
+
3396
+ # Verify certificate chain
3397
+ openssl verify -CAfile /path/to/ca.pem /path/to/cert.pem
3398
+ ```
3399
+
3400
+ **Port Permission Errors (ports 80/443):**
3401
+ ```bash
3402
+ # Option 1: Use setcap (Linux)
3403
+ sudo setcap 'cap_net_bind_service=+ep' $(which node)
3404
+
3405
+ # Option 2: Run as root (not recommended)
3406
+ sudo node server.js
3407
+
3408
+ # Option 3: Use reverse proxy (recommended)
3409
+ # Run Node.js on high port (3000) behind nginx/Apache
3410
+ ```
3411
+
3412
+ **HSTS Testing:**
3413
+ ```bash
3414
+ # Check if HSTS header is present
3415
+ curl -I https://yourdomain.com | grep -i strict
3416
+
3417
+ # Check HSTS status in browser
3418
+ # Chrome: chrome://net-internals/#hsts
3419
+ # Firefox: about:networking#hsts
1518
3420
  ```
1519
3421
 
1520
3422
  ---
@@ -1580,16 +3482,15 @@ master.enableHSTS(); // In production HTTPS
1580
3482
 
1581
3483
  ### Sessions
1582
3484
 
1583
- - `master.sessions.init(options)` - Initialize sessions
1584
- - `master.sessions.set(name, data, response, secret, options)` - Create session
1585
- - `master.sessions.get(name, request, secret)` - Get session data
1586
- - `master.sessions.delete(name, response)` - Delete session
1587
- - `master.sessions.reset()` - Clear all sessions
1588
- - `master.sessions.setCookie(name, value, response, options)` - Set cookie
1589
- - `master.sessions.getCookie(name, request, secret)` - Get cookie
1590
- - `master.sessions.deleteCookie(name, response, options)` - Delete cookie
1591
- - `master.sessions.createSessionID()` - Generate random session ID
1592
- - `master.sessions.middleware()` - Get pipeline middleware
3485
+ - `master.session.init(options)` - Initialize secure sessions
3486
+ - `master.session.destroy(req, res)` - Destroy session completely
3487
+ - `master.session.touch(sessionId)` - Extend session expiry
3488
+ - `master.session.getSessionCount()` - Get active session count
3489
+ - `master.session.clearAllSessions()` - Clear all sessions (testing only)
3490
+ - `master.session.getBestPractices(environment)` - Get recommended settings
3491
+ - `master.session.middleware()` - Get pipeline middleware
3492
+
3493
+ **Session Data Access:** Use `obj.request.session` object directly (Rails/Express style)
1593
3494
 
1594
3495
  ### Request
1595
3496
 
@@ -1602,11 +3503,26 @@ master.enableHSTS(); // In production HTTPS
1602
3503
 
1603
3504
  ### Tools
1604
3505
 
1605
- - `master.tools.encrypt(data, secret)` - Encrypt data
1606
- - `master.tools.decrypt(data, secret)` - Decrypt data
3506
+ **Encryption:**
3507
+ - `master.tools.encrypt(data, secret)` - Encrypt data with AES-256-CBC
3508
+ - `master.tools.decrypt(data, secret)` - Decrypt data with AES-256-CBC
3509
+
3510
+ **File Conversion (NEW in v1.3.1):**
3511
+ - `master.tools.fileToBase64(filePathOrFile, options)` - Convert file to base64 (binary-safe)
3512
+ - `master.tools.base64ToFile(base64String, outputPath, options)` - Convert base64 to file
3513
+ - `master.tools.fileToBuffer(filePathOrFile, options)` - Convert file to Node.js Buffer
3514
+ - `master.tools.fileToBytes(filePathOrFile, options)` - Convert file to Uint8Array
3515
+ - `master.tools.bytesToBase64(bufferOrBytes, options)` - Convert Buffer/Uint8Array to base64
3516
+ - `master.tools.base64ToBytes(base64String)` - Convert base64 to Buffer
3517
+ - `master.tools.streamFileToBase64(filePathOrFile, options)` - Stream large files to base64 (async)
3518
+
3519
+ **Utilities:**
1607
3520
  - `master.tools.combineObjects(target, source)` - Merge objects
1608
3521
  - `master.tools.makeWordId(length)` - Generate random ID
1609
3522
 
3523
+ **Deprecated:**
3524
+ - `master.tools.base64(path)` - ⚠️ DEPRECATED - Broken for binary files, use `fileToBase64()` instead
3525
+
1610
3526
  ---
1611
3527
 
1612
3528
  ## Production Tips
@@ -1626,14 +3542,17 @@ master.enableHSTS(); // In production HTTPS
1626
3542
 
1627
3543
  ## Documentation
1628
3544
 
1629
- Detailed guides:
3545
+ ### Security Documentation
3546
+
3547
+ - [Security Fixes v1.3.2](SECURITY-FIXES-v1.3.2.md) - All security fixes and migration guide
3548
+ - [Security Quick Start](docs/SECURITY-QUICKSTART.md) - 5-minute security setup guide
3549
+ - [Security Audit - Action System](docs/SECURITY-AUDIT-ACTION-SYSTEM.md) - Complete security audit of controllers and filters
3550
+ - [Security Audit - HTTPS](docs/SECURITY-AUDIT-HTTPS.md) - HTTPS/TLS security audit
3551
+
3552
+ ### Feature Documentation
1630
3553
 
1631
- - [HTTP Server Setup](docs/server-setup-http.md)
1632
- - [HTTPS with Credentials](docs/server-setup-https-credentials.md)
1633
- - [HTTPS with Environment TLS & SNI](docs/server-setup-https-env-tls-sni.md)
1634
- - [Hostname Binding](docs/server-setup-hostname-binding.md)
1635
- - [Nginx Reverse Proxy](docs/server-setup-nginx-reverse-proxy.md)
1636
- - [Environment TLS Reference](docs/environment-tls-reference.md)
3554
+ - [Timeout and Error Handling](docs/timeout-and-error-handling.md) - Professional timeout tracking and error rendering
3555
+ - [Environment TLS Reference](docs/environment-tls-reference.md) - TLS/SNI configuration reference
1637
3556
 
1638
3557
  ---
1639
3558