fix(downloads): apply original-filename toggle to individual downloads too (#507)
Follow-up to #498. The toggle reached zip downloads but single-photo downloads still landed on disk with the renamed `event_individual_NNN.jpg` even when the admin had flipped the setting on. Two reasons, fixed in lockstep: - Frontend overrode the server's Content-Disposition with a hardcoded `<a download="X">` attribute (`gallery.service.ts`, `photos.service.ts`) where X was the sanitized `photo.filename` known to the client. So the backend's correctly-formed `Content-Disposition` never reached the disk write. Added `parseContentDispositionFilename` (RFC 5987 + plain `filename=` fallback) and let the server name win when present. - `secureImages.js` (enhanced/maximum protection's secure-download route) was missed in #498 and still emitted a hardcoded `filename="${photo.filename}"` regardless of the toggle. Wired it through `getUseOriginalFilenames` + `buildContentDisposition` so it matches the regular gallery download path. Also exposed `Content-Disposition` via CORS so split (cross-origin) frontend deployments can still read it from JavaScript. Same-origin Docker deploys already had access; this is a defensive addition for the split case.
This commit is contained in:
+6
-1
@@ -161,7 +161,12 @@ const corsOptions = {
|
||||
callback(null, false);
|
||||
}
|
||||
},
|
||||
credentials: true
|
||||
credentials: true,
|
||||
// Expose Content-Disposition so split (cross-origin) frontend
|
||||
// deployments can read the server's chosen download filename. Used
|
||||
// by the gallery/admin download flows to honour the #493 "original
|
||||
// camera filename" toggle on individual photo downloads (#507).
|
||||
exposedHeaders: ['Content-Disposition'],
|
||||
};
|
||||
|
||||
// Only attach CORS to API endpoints, not static assets
|
||||
|
||||
@@ -8,6 +8,11 @@ const { formatBoolean } = require('../utils/dbCompat');
|
||||
const { resolvePhotoFilePath, resolvePhotoStorageKey } = require('../services/photoResolver');
|
||||
const { withLocalCopy } = require('../services/imageProcessor');
|
||||
const { getStorage } = require('../services/storage');
|
||||
const {
|
||||
getUseOriginalFilenames,
|
||||
pickRawDownloadName,
|
||||
} = require('../services/downloadFilenameService');
|
||||
const { buildContentDisposition } = require('../utils/filenameSanitizer');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -339,9 +344,16 @@ router.get('/:slug/secure-download/:photoId/:token',
|
||||
'download'
|
||||
);
|
||||
|
||||
// #493/#507: respect the original-filename toggle here too. The
|
||||
// regular `/gallery/:slug/download/:photoId` route already does
|
||||
// this — secure-images was missed in the original PR and ran
|
||||
// even when the admin had opted into original camera filenames.
|
||||
const useOriginal = await getUseOriginalFilenames();
|
||||
const downloadName = pickRawDownloadName(photo, useOriginal);
|
||||
|
||||
res.set({
|
||||
'Content-Type': photo.mime_type || 'image/jpeg',
|
||||
'Content-Disposition': `attachment; filename="${photo.filename}"`,
|
||||
'Content-Disposition': buildContentDisposition(downloadName),
|
||||
'Content-Length': fileBuffer.length,
|
||||
'X-Download-Protected': 'true'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user