ep_media_upload 0.2.1 → 0.2.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 +4 -2
- package/index.js +43 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,9 @@ Etherpad plugin for secure file uploads via S3 presigned URLs.
|
|
|
27
27
|
"expires": 900,
|
|
28
28
|
"downloadExpires": 300
|
|
29
29
|
},
|
|
30
|
-
"fileTypes": ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip"],
|
|
31
|
-
"maxFileSize": 52428800
|
|
30
|
+
"fileTypes": ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip", "mp3", "mp4", "wav", "mov"],
|
|
31
|
+
"maxFileSize": 52428800,
|
|
32
|
+
"inlineExtensions": ["mp3", "mp4", "wav", "mov", "webm", "ogg"]
|
|
32
33
|
}
|
|
33
34
|
```
|
|
34
35
|
|
|
@@ -49,6 +50,7 @@ Etherpad plugin for secure file uploads via S3 presigned URLs.
|
|
|
49
50
|
|--------|----------|---------|-------------|
|
|
50
51
|
| `fileTypes` | No | all | Array of allowed extensions (without dots) |
|
|
51
52
|
| `maxFileSize` | No | unlimited | Max file size in bytes |
|
|
53
|
+
| `inlineExtensions` | No | `[]` | Extensions to open inline in browser (streaming). Files not in this list will download. |
|
|
52
54
|
|
|
53
55
|
### Environment Variables
|
|
54
56
|
|
package/index.js
CHANGED
|
@@ -133,7 +133,7 @@ const EXTENSION_MIME_MAP = {
|
|
|
133
133
|
|
|
134
134
|
// Audio
|
|
135
135
|
mp3: ['audio/mpeg', 'audio/mp3'],
|
|
136
|
-
wav: ['audio/wav', 'audio/wave', 'audio/x-wav'],
|
|
136
|
+
wav: ['audio/wav', 'audio/wave', 'audio/x-wav', 'audio/vnd.wave'],
|
|
137
137
|
ogg: ['audio/ogg'],
|
|
138
138
|
m4a: ['audio/mp4', 'audio/x-m4a'],
|
|
139
139
|
flac: ['audio/flac'],
|
|
@@ -467,12 +467,50 @@ exports.expressCreateServer = (hookName, context) => {
|
|
|
467
467
|
const prefix = keyPrefix || '';
|
|
468
468
|
const key = `${prefix}${padId}/${fileId}`;
|
|
469
469
|
|
|
470
|
+
// Extract file extension to determine inline vs attachment disposition
|
|
471
|
+
const fileExtension = getValidExtension(fileId);
|
|
472
|
+
|
|
473
|
+
// Get inlineExtensions from config (extensions that should open in browser)
|
|
474
|
+
// Default behavior is download (attachment) for all files
|
|
475
|
+
const inlineExtensions = settings.ep_media_upload?.inlineExtensions || [];
|
|
476
|
+
const shouldOpenInline = fileExtension &&
|
|
477
|
+
Array.isArray(inlineExtensions) &&
|
|
478
|
+
inlineExtensions.map(e => e.toLowerCase()).includes(fileExtension.toLowerCase());
|
|
479
|
+
|
|
480
|
+
// Determine Content-Disposition based on extension config
|
|
481
|
+
// Extract filename for Content-Disposition header (UUID.ext -> use as filename)
|
|
482
|
+
const filename = fileId.replace(/[^\w\-_.]/g, '_'); // Sanitize for header
|
|
483
|
+
const disposition = shouldOpenInline
|
|
484
|
+
? `inline; filename="${filename}"`
|
|
485
|
+
: `attachment; filename="${filename}"`;
|
|
486
|
+
|
|
487
|
+
// Map extensions to canonical MIME types for consistent browser playback
|
|
488
|
+
const EXTENSION_CONTENT_TYPE = {
|
|
489
|
+
mp3: 'audio/mpeg',
|
|
490
|
+
wav: 'audio/wav',
|
|
491
|
+
mp4: 'video/mp4',
|
|
492
|
+
mov: 'video/quicktime',
|
|
493
|
+
webm: 'video/webm',
|
|
494
|
+
ogg: 'audio/ogg',
|
|
495
|
+
m4a: 'audio/mp4',
|
|
496
|
+
pdf: 'application/pdf',
|
|
497
|
+
};
|
|
498
|
+
|
|
470
499
|
// Generate presigned GET URL with short expiry
|
|
500
|
+
// Use ResponseContentDisposition and ResponseContentType to override stored headers
|
|
471
501
|
const s3Client = new S3Client({ region });
|
|
472
|
-
const
|
|
502
|
+
const commandParams = {
|
|
473
503
|
Bucket: bucket,
|
|
474
504
|
Key: key,
|
|
475
|
-
|
|
505
|
+
ResponseContentDisposition: disposition,
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// Set canonical Content-Type for inline extensions to ensure browser compatibility
|
|
509
|
+
if (shouldOpenInline && fileExtension && EXTENSION_CONTENT_TYPE[fileExtension.toLowerCase()]) {
|
|
510
|
+
commandParams.ResponseContentType = EXTENSION_CONTENT_TYPE[fileExtension.toLowerCase()];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const getCommand = new GetObjectCommand(commandParams);
|
|
476
514
|
|
|
477
515
|
// Use downloadExpires from config, default to 300 seconds (5 minutes)
|
|
478
516
|
const expiresIn = downloadExpires || 300;
|
|
@@ -480,7 +518,8 @@ exports.expressCreateServer = (hookName, context) => {
|
|
|
480
518
|
|
|
481
519
|
// Log download request for audit trail
|
|
482
520
|
const username = req.session?.user?.username || 'anonymous';
|
|
483
|
-
|
|
521
|
+
const dispositionType = shouldOpenInline ? 'inline' : 'attachment';
|
|
522
|
+
logger.info(`[ep_media_upload] DOWNLOAD: author="${authorId}" user="${username}" ip="${clientIp}" pad="${padId}" file="${fileId}" disposition="${dispositionType}"`);
|
|
484
523
|
|
|
485
524
|
// Redirect to the presigned URL
|
|
486
525
|
return res.redirect(302, presignedGetUrl);
|
package/package.json
CHANGED