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 +69 -13
- package/README.md +44 -0
- package/examples/FileServingExample.js +88 -0
- package/package.json +1 -1
package/MasterAction.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
// version 0.0.
|
|
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