mastercontroller 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +4 -1
- package/MasterAction.js +137 -23
- package/MasterActionFilters.js +197 -92
- package/MasterControl.js +265 -44
- package/MasterHtml.js +226 -143
- package/MasterPipeline.js +1 -1
- package/MasterRequest.js +202 -24
- package/MasterSocket.js +6 -1
- package/MasterTools.js +428 -13
- package/README.md +2364 -309
- package/SECURITY-FIXES-v1.3.2.md +614 -0
- package/docs/SECURITY-AUDIT-ACTION-SYSTEM.md +1374 -0
- package/docs/SECURITY-AUDIT-HTTPS.md +1056 -0
- package/docs/SECURITY-QUICKSTART.md +375 -0
- package/docs/timeout-and-error-handling.md +8 -6
- package/package.json +1 -1
- package/security/SecurityEnforcement.js +241 -0
- package/security/SessionSecurity.js +100 -2
- package/test/security/filters.test.js +276 -0
- package/test/security/https.test.js +214 -0
- package/test/security/path-traversal.test.js +222 -0
- package/test/security/xss.test.js +190 -0
- package/MasterSession.js +0 -208
- package/docs/server-setup-hostname-binding.md +0 -24
- package/docs/server-setup-http.md +0 -32
- package/docs/server-setup-https-credentials.md +0 -32
- package/docs/server-setup-https-env-tls-sni.md +0 -62
- package/docs/server-setup-nginx-reverse-proxy.md +0 -46
package/MasterTools.js
CHANGED
|
@@ -51,21 +51,48 @@ class MasterTools{
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
encrypt(payload, secret){
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
// Generate random IV (16 bytes for AES)
|
|
55
|
+
const iv = crypto.randomBytes(16);
|
|
56
|
+
|
|
57
|
+
// Create 256-bit key from secret
|
|
58
|
+
const key = crypto.createHash('sha256').update(String(secret)).digest();
|
|
59
|
+
|
|
60
|
+
// Create cipher with AES-256-CBC
|
|
61
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
62
|
+
|
|
63
|
+
// Encrypt payload
|
|
64
|
+
let encrypted = cipher.update(String(payload), 'utf8', 'hex');
|
|
65
|
+
encrypted += cipher.final('hex');
|
|
66
|
+
|
|
67
|
+
// Prepend IV to encrypted data (IV is not secret, needed for decryption)
|
|
68
|
+
return iv.toString('hex') + ':' + encrypted;
|
|
61
69
|
}
|
|
62
|
-
|
|
70
|
+
|
|
63
71
|
decrypt(encryption, secret){
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
try {
|
|
73
|
+
// Split IV and encrypted data
|
|
74
|
+
const parts = encryption.split(':');
|
|
75
|
+
if (parts.length !== 2) {
|
|
76
|
+
throw new Error('Invalid encrypted data format');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
80
|
+
const encryptedData = parts[1];
|
|
81
|
+
|
|
82
|
+
// Create 256-bit key from secret
|
|
83
|
+
const key = crypto.createHash('sha256').update(String(secret)).digest();
|
|
84
|
+
|
|
85
|
+
// Create decipher
|
|
86
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
87
|
+
|
|
88
|
+
// Decrypt
|
|
89
|
+
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
|
|
90
|
+
decrypted += decipher.final('utf8');
|
|
91
|
+
|
|
92
|
+
return decrypted;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new Error('Decryption failed: ' + error.message);
|
|
95
|
+
}
|
|
69
96
|
}
|
|
70
97
|
|
|
71
98
|
generateRandomKey(hash){
|
|
@@ -74,7 +101,24 @@ class MasterTools{
|
|
|
74
101
|
return sha.digest('hex');
|
|
75
102
|
}
|
|
76
103
|
|
|
104
|
+
/**
|
|
105
|
+
* @deprecated This custom base64 implementation ONLY works for TEXT strings, NOT binary files.
|
|
106
|
+
* For binary files (images, PDFs, videos), use Node.js Buffer API or the new file conversion methods below.
|
|
107
|
+
* This method will be removed in v2.0.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* // ❌ WRONG - Corrupts binary files
|
|
111
|
+
* const base64 = tools.base64().encode(binaryData);
|
|
112
|
+
*
|
|
113
|
+
* // ✅ CORRECT - Use Node.js Buffer
|
|
114
|
+
* const base64 = Buffer.from(binaryData).toString('base64');
|
|
115
|
+
*
|
|
116
|
+
* // ✅ CORRECT - Use new helper methods
|
|
117
|
+
* const base64 = tools.fileToBase64('/path/to/file.jpg');
|
|
118
|
+
*/
|
|
77
119
|
base64(){
|
|
120
|
+
console.warn('[DEPRECATED] MasterTools.base64() only works for TEXT strings, not binary files. Use Buffer.toString("base64") or tools.fileToBase64() instead. This method will be removed in v2.0.');
|
|
121
|
+
|
|
78
122
|
var $that = this;
|
|
79
123
|
return {
|
|
80
124
|
encode: function(string){
|
|
@@ -132,6 +176,377 @@ class MasterTools{
|
|
|
132
176
|
}
|
|
133
177
|
};
|
|
134
178
|
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// FILE CONVERSION UTILITIES (Production-Grade)
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Convert file to base64 string (binary-safe)
|
|
185
|
+
*
|
|
186
|
+
* @param {String|Object} filePathOrFile - File path or formidable file object
|
|
187
|
+
* @param {Object} options - Conversion options
|
|
188
|
+
* @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
|
|
189
|
+
* @param {Boolean} options.includeDataURI - Include data URI prefix (default: false)
|
|
190
|
+
* @returns {String} Base64 encoded string
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // Convert uploaded file to base64
|
|
194
|
+
* const file = obj.params.formData.files.image[0];
|
|
195
|
+
* const base64 = master.tools.fileToBase64(file);
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* // Convert file by path with data URI
|
|
199
|
+
* const base64 = master.tools.fileToBase64('/path/to/image.jpg', {
|
|
200
|
+
* includeDataURI: true,
|
|
201
|
+
* maxSize: 5 * 1024 * 1024 // 5MB limit
|
|
202
|
+
* });
|
|
203
|
+
* // Returns: "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
|
|
204
|
+
*/
|
|
205
|
+
fileToBase64(filePathOrFile, options = {}) {
|
|
206
|
+
const fs = require('fs');
|
|
207
|
+
const path = require('path');
|
|
208
|
+
|
|
209
|
+
// Extract file path from formidable file object or use as-is
|
|
210
|
+
const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
|
|
211
|
+
? filePathOrFile.filepath
|
|
212
|
+
: filePathOrFile;
|
|
213
|
+
|
|
214
|
+
// Validate file path
|
|
215
|
+
if (!filepath || typeof filepath !== 'string') {
|
|
216
|
+
throw new Error('Invalid file path provided');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!fs.existsSync(filepath)) {
|
|
220
|
+
throw new Error(`File not found: ${filepath}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Get file stats
|
|
224
|
+
const stats = fs.statSync(filepath);
|
|
225
|
+
|
|
226
|
+
// Check file size
|
|
227
|
+
const maxSize = options.maxSize || 10 * 1024 * 1024; // 10MB default
|
|
228
|
+
if (stats.size > maxSize) {
|
|
229
|
+
throw new Error(`File size (${stats.size} bytes) exceeds maximum (${maxSize} bytes). Use streaming for large files.`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Security: Check if it's actually a file
|
|
233
|
+
if (!stats.isFile()) {
|
|
234
|
+
throw new Error('Path is not a file');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
// Read file as binary buffer
|
|
239
|
+
const buffer = fs.readFileSync(filepath);
|
|
240
|
+
|
|
241
|
+
// Convert to base64
|
|
242
|
+
const base64 = buffer.toString('base64');
|
|
243
|
+
|
|
244
|
+
// Include data URI if requested
|
|
245
|
+
if (options.includeDataURI) {
|
|
246
|
+
const mimetype = filePathOrFile.mimetype || this._getMimeTypeFromPath(filepath);
|
|
247
|
+
return `data:${mimetype};base64,${base64}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return base64;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
throw new Error(`Failed to convert file to base64: ${error.message}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Convert base64 string to file (binary-safe)
|
|
258
|
+
*
|
|
259
|
+
* @param {String} base64String - Base64 encoded string (with or without data URI)
|
|
260
|
+
* @param {String} outputPath - Output file path
|
|
261
|
+
* @param {Object} options - Conversion options
|
|
262
|
+
* @param {Boolean} options.overwrite - Overwrite existing file (default: false)
|
|
263
|
+
* @returns {Object} File information {path, size, mimetype}
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* const fileInfo = master.tools.base64ToFile(
|
|
267
|
+
* 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
|
|
268
|
+
* '/path/to/output.jpg',
|
|
269
|
+
* { overwrite: true }
|
|
270
|
+
* );
|
|
271
|
+
* console.log(fileInfo);
|
|
272
|
+
* // { path: '/path/to/output.jpg', size: 51234, mimetype: 'image/jpeg' }
|
|
273
|
+
*/
|
|
274
|
+
base64ToFile(base64String, outputPath, options = {}) {
|
|
275
|
+
const fs = require('fs');
|
|
276
|
+
const path = require('path');
|
|
277
|
+
|
|
278
|
+
// Validate inputs
|
|
279
|
+
if (!base64String || typeof base64String !== 'string') {
|
|
280
|
+
throw new Error('Invalid base64 string provided');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!outputPath || typeof outputPath !== 'string') {
|
|
284
|
+
throw new Error('Invalid output path provided');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check if file exists
|
|
288
|
+
if (fs.existsSync(outputPath) && !options.overwrite) {
|
|
289
|
+
throw new Error(`File already exists: ${outputPath}. Set overwrite: true to replace.`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Extract mimetype and base64 data from data URI if present
|
|
293
|
+
let mimetype = null;
|
|
294
|
+
let base64Data = base64String;
|
|
295
|
+
|
|
296
|
+
const dataURIMatch = base64String.match(/^data:([^;]+);base64,(.+)$/);
|
|
297
|
+
if (dataURIMatch) {
|
|
298
|
+
mimetype = dataURIMatch[1];
|
|
299
|
+
base64Data = dataURIMatch[2];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Validate base64 format
|
|
303
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
|
|
304
|
+
throw new Error('Invalid base64 string format');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
// Convert base64 to buffer
|
|
309
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
310
|
+
|
|
311
|
+
// Ensure output directory exists
|
|
312
|
+
const dir = path.dirname(outputPath);
|
|
313
|
+
if (!fs.existsSync(dir)) {
|
|
314
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Write buffer to file
|
|
318
|
+
fs.writeFileSync(outputPath, buffer);
|
|
319
|
+
|
|
320
|
+
// Get file stats
|
|
321
|
+
const stats = fs.statSync(outputPath);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
path: outputPath,
|
|
325
|
+
size: stats.size,
|
|
326
|
+
mimetype: mimetype || this._getMimeTypeFromPath(outputPath)
|
|
327
|
+
};
|
|
328
|
+
} catch (error) {
|
|
329
|
+
throw new Error(`Failed to convert base64 to file: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Convert file to Node.js Buffer (binary-safe)
|
|
335
|
+
*
|
|
336
|
+
* @param {String|Object} filePathOrFile - File path or formidable file object
|
|
337
|
+
* @param {Object} options - Conversion options
|
|
338
|
+
* @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
|
|
339
|
+
* @returns {Buffer} Node.js Buffer containing file data
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* const file = obj.params.formData.files.document[0];
|
|
343
|
+
* const buffer = master.tools.fileToBuffer(file);
|
|
344
|
+
* console.log(buffer.length); // File size in bytes
|
|
345
|
+
*/
|
|
346
|
+
fileToBuffer(filePathOrFile, options = {}) {
|
|
347
|
+
const fs = require('fs');
|
|
348
|
+
|
|
349
|
+
const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
|
|
350
|
+
? filePathOrFile.filepath
|
|
351
|
+
: filePathOrFile;
|
|
352
|
+
|
|
353
|
+
if (!filepath || typeof filepath !== 'string') {
|
|
354
|
+
throw new Error('Invalid file path provided');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!fs.existsSync(filepath)) {
|
|
358
|
+
throw new Error(`File not found: ${filepath}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const stats = fs.statSync(filepath);
|
|
362
|
+
const maxSize = options.maxSize || 10 * 1024 * 1024; // 10MB default
|
|
363
|
+
|
|
364
|
+
if (stats.size > maxSize) {
|
|
365
|
+
throw new Error(`File size (${stats.size} bytes) exceeds maximum (${maxSize} bytes)`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!stats.isFile()) {
|
|
369
|
+
throw new Error('Path is not a file');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
return fs.readFileSync(filepath);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
throw new Error(`Failed to read file to buffer: ${error.message}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Convert file to Uint8Array byte array (binary-safe)
|
|
381
|
+
*
|
|
382
|
+
* @param {String|Object} filePathOrFile - File path or formidable file object
|
|
383
|
+
* @param {Object} options - Conversion options
|
|
384
|
+
* @param {Number} options.maxSize - Maximum file size in bytes (default: 10MB)
|
|
385
|
+
* @returns {Uint8Array} Byte array
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* const file = obj.params.formData.files.data[0];
|
|
389
|
+
* const bytes = master.tools.fileToBytes(file);
|
|
390
|
+
* console.log(bytes[0]); // First byte
|
|
391
|
+
* console.log(bytes.length); // Total bytes
|
|
392
|
+
*/
|
|
393
|
+
fileToBytes(filePathOrFile, options = {}) {
|
|
394
|
+
const buffer = this.fileToBuffer(filePathOrFile, options);
|
|
395
|
+
return new Uint8Array(buffer);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Convert Buffer or Uint8Array to base64 string
|
|
400
|
+
*
|
|
401
|
+
* @param {Buffer|Uint8Array} bytes - Binary data
|
|
402
|
+
* @returns {String} Base64 encoded string
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* const buffer = fs.readFileSync('/path/to/file.pdf');
|
|
406
|
+
* const base64 = master.tools.bytesToBase64(buffer);
|
|
407
|
+
*/
|
|
408
|
+
bytesToBase64(bytes) {
|
|
409
|
+
if (!bytes) {
|
|
410
|
+
throw new Error('Invalid bytes provided');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
// Convert Uint8Array to Buffer if needed
|
|
415
|
+
const buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
|
|
416
|
+
return buffer.toString('base64');
|
|
417
|
+
} catch (error) {
|
|
418
|
+
throw new Error(`Failed to convert bytes to base64: ${error.message}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Convert base64 string to Buffer
|
|
424
|
+
*
|
|
425
|
+
* @param {String} base64String - Base64 encoded string (with or without data URI)
|
|
426
|
+
* @returns {Buffer} Node.js Buffer
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* const base64 = 'SGVsbG8gV29ybGQ=';
|
|
430
|
+
* const buffer = master.tools.base64ToBytes(base64);
|
|
431
|
+
* console.log(buffer.toString('utf8')); // "Hello World"
|
|
432
|
+
*/
|
|
433
|
+
base64ToBytes(base64String) {
|
|
434
|
+
if (!base64String || typeof base64String !== 'string') {
|
|
435
|
+
throw new Error('Invalid base64 string provided');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Strip data URI prefix if present
|
|
439
|
+
let base64Data = base64String;
|
|
440
|
+
const dataURIMatch = base64String.match(/^data:[^;]+;base64,(.+)$/);
|
|
441
|
+
if (dataURIMatch) {
|
|
442
|
+
base64Data = dataURIMatch[1];
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Validate base64 format
|
|
446
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
|
|
447
|
+
throw new Error('Invalid base64 string format');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
return Buffer.from(base64Data, 'base64');
|
|
452
|
+
} catch (error) {
|
|
453
|
+
throw new Error(`Failed to convert base64 to bytes: ${error.message}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Stream large file to base64 (for files > 10MB)
|
|
459
|
+
* Returns a Promise that resolves with base64 string
|
|
460
|
+
*
|
|
461
|
+
* @param {String|Object} filePathOrFile - File path or formidable file object
|
|
462
|
+
* @param {Object} options - Streaming options
|
|
463
|
+
* @param {Function} options.onProgress - Progress callback (percent: number) => void
|
|
464
|
+
* @returns {Promise<String>} Base64 encoded string
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* // Stream 500MB video file
|
|
468
|
+
* const base64 = await master.tools.streamFileToBase64('/path/to/video.mp4', {
|
|
469
|
+
* onProgress: (percent) => console.log(`${percent}% complete`)
|
|
470
|
+
* });
|
|
471
|
+
*/
|
|
472
|
+
async streamFileToBase64(filePathOrFile, options = {}) {
|
|
473
|
+
const fs = require('fs');
|
|
474
|
+
const { Transform } = require('stream');
|
|
475
|
+
|
|
476
|
+
const filepath = typeof filePathOrFile === 'object' && filePathOrFile.filepath
|
|
477
|
+
? filePathOrFile.filepath
|
|
478
|
+
: filePathOrFile;
|
|
479
|
+
|
|
480
|
+
if (!filepath || !fs.existsSync(filepath)) {
|
|
481
|
+
throw new Error(`File not found: ${filepath}`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const stats = fs.statSync(filepath);
|
|
485
|
+
if (!stats.isFile()) {
|
|
486
|
+
throw new Error('Path is not a file');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return new Promise((resolve, reject) => {
|
|
490
|
+
const chunks = [];
|
|
491
|
+
let bytesRead = 0;
|
|
492
|
+
|
|
493
|
+
const readStream = fs.createReadStream(filepath);
|
|
494
|
+
|
|
495
|
+
readStream.on('data', (chunk) => {
|
|
496
|
+
chunks.push(chunk);
|
|
497
|
+
bytesRead += chunk.length;
|
|
498
|
+
|
|
499
|
+
// Progress callback
|
|
500
|
+
if (options.onProgress && typeof options.onProgress === 'function') {
|
|
501
|
+
const percent = Math.round((bytesRead / stats.size) * 100);
|
|
502
|
+
options.onProgress(percent);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
readStream.on('end', () => {
|
|
507
|
+
try {
|
|
508
|
+
const buffer = Buffer.concat(chunks);
|
|
509
|
+
const base64 = buffer.toString('base64');
|
|
510
|
+
resolve(base64);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
reject(new Error(`Failed to convert stream to base64: ${error.message}`));
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
readStream.on('error', (error) => {
|
|
517
|
+
reject(new Error(`Stream error: ${error.message}`));
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Get MIME type from file path
|
|
524
|
+
* @private
|
|
525
|
+
*/
|
|
526
|
+
_getMimeTypeFromPath(filepath) {
|
|
527
|
+
const path = require('path');
|
|
528
|
+
const ext = path.extname(filepath).toLowerCase();
|
|
529
|
+
|
|
530
|
+
const mimeTypes = {
|
|
531
|
+
'.jpg': 'image/jpeg',
|
|
532
|
+
'.jpeg': 'image/jpeg',
|
|
533
|
+
'.png': 'image/png',
|
|
534
|
+
'.gif': 'image/gif',
|
|
535
|
+
'.webp': 'image/webp',
|
|
536
|
+
'.svg': 'image/svg+xml',
|
|
537
|
+
'.pdf': 'application/pdf',
|
|
538
|
+
'.txt': 'text/plain',
|
|
539
|
+
'.json': 'application/json',
|
|
540
|
+
'.xml': 'application/xml',
|
|
541
|
+
'.zip': 'application/zip',
|
|
542
|
+
'.mp4': 'video/mp4',
|
|
543
|
+
'.mp3': 'audio/mpeg',
|
|
544
|
+
'.wav': 'audio/wav'
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
548
|
+
}
|
|
549
|
+
|
|
135
550
|
combineObjandArray(data, objParams){
|
|
136
551
|
|
|
137
552
|
if(Array.isArray(data) === false){
|