mastercontroller 1.2.9 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MasterAction.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- // version 0.0.21
2
+ // version 0.0.22
3
3
 
4
4
  var master = require('./MasterControl');
5
5
  var fileserver = require('fs');
@@ -133,18 +133,6 @@ class MasterAction{
133
133
  }
134
134
  }
135
135
 
136
- returnReact(data, location){
137
-
138
- var masterView = null;
139
- data = data === undefined ? {} : data;
140
- this.params = this.params === undefined ? {} : this.params;
141
- this.params = tools.combineObjects(data, this.params);
142
- var func = master.viewList;
143
- this.params = tools.combineObjects(this.params, func);
144
- var html = master.reactView.compile(this.__currentRoute.toController, this.__currentRoute.toAction, this.__currentRoute.root);
145
-
146
- }
147
-
148
136
  returnView(data, location){
149
137
 
150
138
  var masterView = null;
@@ -202,6 +190,74 @@ class MasterAction{
202
190
  return false;
203
191
  }
204
192
 
193
+ // Serves binary files with proper headers and MIME types
194
+ // options: {
195
+ // contentType: string (auto-detected if not provided),
196
+ // disposition: 'inline' | 'attachment' (default: 'attachment'),
197
+ // filename: string (defaults to original filename)
198
+ // }
199
+ returnFile(filePath, options){
200
+ options = options || {};
201
+
202
+ // Default options
203
+ var disposition = options.disposition || 'attachment';
204
+ var filename = options.filename || filePath.split('/').pop();
205
+ var contentType = options.contentType;
206
+
207
+ // Auto-detect content type if not provided
208
+ if (!contentType) {
209
+ var ext = filePath.split('.').pop().toLowerCase();
210
+ var mimeTypes = {
211
+ 'pdf': 'application/pdf',
212
+ 'jpg': 'image/jpeg',
213
+ 'jpeg': 'image/jpeg',
214
+ 'png': 'image/png',
215
+ 'gif': 'image/gif',
216
+ 'svg': 'image/svg+xml',
217
+ 'zip': 'application/zip',
218
+ 'csv': 'text/csv',
219
+ 'txt': 'text/plain',
220
+ 'xml': 'application/xml',
221
+ 'json': 'application/json',
222
+ 'mp4': 'video/mp4',
223
+ 'mp3': 'audio/mpeg',
224
+ 'wav': 'audio/wav',
225
+ 'doc': 'application/msword',
226
+ 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
227
+ 'xls': 'application/vnd.ms-excel',
228
+ 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
229
+ 'ppt': 'application/vnd.ms-powerpoint',
230
+ 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
231
+ };
232
+ contentType = mimeTypes[ext] || 'application/octet-stream';
233
+ }
234
+
235
+ try {
236
+ // Read file as binary
237
+ var fileBuffer = fileserver.readFileSync(filePath);
238
+
239
+ // Build headers
240
+ var headers = {
241
+ 'Content-Type': contentType,
242
+ 'Content-Length': fileBuffer.length,
243
+ 'Content-Disposition': disposition + '; filename="' + filename + '"'
244
+ };
245
+
246
+ // Send response
247
+ if (!this.__response._headerSent) {
248
+ this.__response.writeHead(200, headers);
249
+ this.__response.end(fileBuffer);
250
+ }
251
+ } catch(error) {
252
+ // Handle file not found or read errors
253
+ if (!this.__response._headerSent) {
254
+ this.__response.writeHead(404, {'Content-Type': 'text/plain'});
255
+ this.__response.end('File not found: ' + filePath);
256
+ }
257
+ console.error('Error serving file:', error);
258
+ }
259
+ }
260
+
205
261
 
206
262
  }
207
263
 
package/README.md CHANGED
@@ -49,6 +49,50 @@ Use `setupServer('https', credentials)` or configure via environment TLS; see do
49
49
  - `docs/server-setup-nginx-reverse-proxy.md`
50
50
  - `docs/environment-tls-reference.md`
51
51
 
52
+ ### How File Uploads Work
53
+
54
+ MasterController handles file uploads through the `formidable` library (v3.5.4+) integrated into the request parsing pipeline in `MasterRequest.js`.
55
+
56
+ **Processing Flow:**
57
+
58
+ 1. **Content-Type Detection** - When a request arrives, the framework parses the `Content-Type` header to determine how to handle the request body (`MasterRequest.js:34-36`)
59
+
60
+ 2. **Multipart Form Data** - For `multipart/form-data` requests (file uploads), the framework uses formidable's `IncomingForm` to parse the request (`MasterRequest.js:43-78`)
61
+
62
+ 3. **Event-Based Parsing** - Formidable emits events during parsing:
63
+ - `field` event: Captures regular form fields and adds them to `parsedURL.formData.fields`
64
+ - `file` event: Captures uploaded files and stores them in `parsedURL.formData.files` as arrays (supporting multiple file uploads per field)
65
+ - `end` event: Signals completion and resolves the promise with parsed data
66
+
67
+ 4. **File Metadata** - Each uploaded file object includes:
68
+ - `name` or `originalFilename`: The original filename
69
+ - `extension`: Extracted file extension (e.g., `.jpg`, `.pdf`)
70
+ - `filepath`: Temporary location where formidable stored the file
71
+ - Other formidable metadata (size, mimetype, etc.)
72
+
73
+ 5. **Accessing Uploads in Controllers** - In your controller actions, access uploaded files via:
74
+ ```js
75
+ this.params.formData.files['fieldName'][0] // First file for 'fieldName'
76
+ this.params.formData.fields['textField'] // Regular form fields
77
+ ```
78
+
79
+ 6. **Multiple Files** - Files are always stored as arrays in `parsedURL.formData.files[field]`, allowing multiple files to be uploaded with the same field name (`MasterRequest.js:59-65`)
80
+
81
+ 7. **Cleanup** - Use `this.request.deleteFileBuffer(filePath)` to remove temporary files after processing (`MasterRequest.js:162-169`)
82
+
83
+ **Configuration Options:**
84
+
85
+ You can configure file upload behavior via `master.request.init()`:
86
+ - `disableFormidableMultipartFormData`: Set to `true` to skip file upload parsing
87
+ - `formidable`: Pass options directly to formidable (upload directory, max file size, etc.)
88
+
89
+ **Supported Content Types:**
90
+ - `multipart/form-data` - File uploads
91
+ - `application/x-www-form-urlencoded` - Standard forms
92
+ - `application/json` - JSON payloads
93
+ - `text/plain` - Plain text (1MB limit)
94
+ - `text/html` - HTML content
95
+
52
96
  ### Production tips
53
97
  - Prefer a reverse proxy for TLS and serve Node on a high port.
54
98
  - If keeping TLS in Node, harden TLS and manage cert rotation.
@@ -0,0 +1,88 @@
1
+ // Example: Binary File Serving with MasterController
2
+ //
3
+ // This example demonstrates how to use the returnFile() method
4
+ // to serve binary files in your MasterController application.
5
+
6
+ class ExampleController extends MasterAction {
7
+
8
+ // Example 1: Serve a PDF file for download
9
+ downloadPdf(){
10
+ var filePath = master.root + '/storage/documents/report.pdf';
11
+ this.returnFile(filePath);
12
+ // Downloads as 'report.pdf' with auto-detected content-type
13
+ }
14
+
15
+ // Example 2: Display an image inline
16
+ showImage(){
17
+ var filePath = master.root + '/storage/images/photo.jpg';
18
+ this.returnFile(filePath, {
19
+ disposition: 'inline' // Display in browser instead of download
20
+ });
21
+ }
22
+
23
+ // Example 3: Serve file with custom filename
24
+ downloadReport(){
25
+ var filePath = master.root + '/storage/reports/monthly-2024-01.pdf';
26
+ this.returnFile(filePath, {
27
+ filename: 'January_Report.pdf' // Custom filename for download
28
+ });
29
+ }
30
+
31
+ // Example 4: Serve file with explicit content type
32
+ downloadCsv(){
33
+ var filePath = master.root + '/storage/exports/data.csv';
34
+ this.returnFile(filePath, {
35
+ contentType: 'text/csv',
36
+ filename: 'export.csv'
37
+ });
38
+ }
39
+
40
+ // Example 5: Serve dynamic file based on user request
41
+ downloadUserFile(){
42
+ var fileId = this.params.id;
43
+ var filePath = master.root + '/storage/user-files/' + fileId;
44
+
45
+ // You might want to add authentication/authorization checks here
46
+ // if (this.session.userId !== file.ownerId) { ... }
47
+
48
+ this.returnFile(filePath, {
49
+ filename: 'user-document.pdf'
50
+ });
51
+ }
52
+
53
+ // Example 6: Serve images inline (for img src tags)
54
+ serveUserAvatar(){
55
+ var userId = this.params.id;
56
+ var avatarPath = master.root + '/storage/avatars/' + userId + '.png';
57
+ this.returnFile(avatarPath, {
58
+ disposition: 'inline',
59
+ contentType: 'image/png'
60
+ });
61
+ }
62
+
63
+ }
64
+
65
+ module.exports = ExampleController;
66
+
67
+
68
+ // Supported MIME types (auto-detected by file extension):
69
+ //
70
+ // Images: jpg, jpeg, png, gif, svg
71
+ // Documents: pdf, doc, docx, xls, xlsx, ppt, pptx
72
+ // Data: csv, json, xml, txt
73
+ // Archives: zip
74
+ // Media: mp3, mp4, wav
75
+ //
76
+ // For other file types, use contentType option or files will be served as 'application/octet-stream'
77
+
78
+
79
+ // Route configuration example:
80
+ // In your routes.js:
81
+ //
82
+ // {
83
+ // "url" : "/download/pdf",
84
+ // "namespace" : "example",
85
+ // "controller" : "ExampleController",
86
+ // "action" : "downloadPdf",
87
+ // "type" : "GET"
88
+ // }
package/package.json CHANGED
@@ -18,5 +18,5 @@
18
18
  "scripts": {
19
19
  "test": "echo \"Error: no test specified\" && exit 1"
20
20
  },
21
- "version": "1.2.9"
21
+ "version": "1.2.11"
22
22
  }