diff --git a/backend/src/routes/gallery.js b/backend/src/routes/gallery.js
index 444712fc..31bd918b 100644
--- a/backend/src/routes/gallery.js
+++ b/backend/src/routes/gallery.js
@@ -1627,7 +1627,7 @@ router.post('/:eventId/upload', verifyGalleryAccess, async (req, res) => {
// Import multer and photo processing
const multer = require('multer');
- const { getAllowedMimeTypes } = require('../services/uploadSettings');
+ const { getAllowedMimeTypes, getMaxFilesPerUpload } = require('../services/uploadSettings');
const { validateFileType } = require('../utils/fileSecurityUtils');
// Resolve allowed MIME types from settings
@@ -1638,11 +1638,26 @@ router.post('/:eventId/upload', verifyGalleryAccess, async (req, res) => {
allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp'];
}
+ // #613 — per-batch file count was hardcoded to 10 here, so the admin's
+ // Settings → General → "Max Files per Upload" value silently didn't
+ // apply to guest uploads (only admin uploads honoured it via
+ // adminPhotos.js:131). Zszywany reported uploading 16 files succeeded
+ // even with the limit set to 10. Mirror the admin path: resolve from
+ // settings (cached for 60s in the service) and feed multer both
+ // `limits.files` and the `.array(...)` cap. Fall back to the service's
+ // default if the read fails.
+ let maxFilesPerUpload;
+ try {
+ maxFilesPerUpload = await getMaxFilesPerUpload();
+ } catch {
+ maxFilesPerUpload = 500;
+ }
+
const upload = multer({
dest: tempUploadDir,
limits: {
- fileSize: 50 * 1024 * 1024, // 50MB
- files: 10 // Max 10 files at once
+ fileSize: 50 * 1024 * 1024, // 50MB per file (separate concern from #613)
+ files: maxFilesPerUpload
},
fileFilter: (req, file, cb) => {
if (validateFileType(file.originalname, file.mimetype, allowedMimeTypes)) {
@@ -1651,7 +1666,7 @@ router.post('/:eventId/upload', verifyGalleryAccess, async (req, res) => {
cb(new Error('Invalid file type'));
}
}
- }).array('photos', 10);
+ }).array('photos', maxFilesPerUpload);
// Handle upload
upload(req, res, async (err) => {
diff --git a/backend/src/routes/publicSettings.js b/backend/src/routes/publicSettings.js
index 050adc9e..9137807e 100644
--- a/backend/src/routes/publicSettings.js
+++ b/backend/src/routes/publicSettings.js
@@ -18,7 +18,15 @@ router.get('/', async (req, res) => {
'event_default_require_password',
'event_default_feedback_enabled',
'gallery_show_filter_bar',
- 'event_phone_field_enabled'
+ 'event_phone_field_enabled',
+ // #613 — guest upload UI needs to know the per-batch file
+ // count limit so it can render a real number in the
+ // "fileRequirements" hint (`{{limit}}` was showing literal)
+ // and short-circuit before posting too many files. The
+ // backend route also enforces it via getMaxFilesPerUpload,
+ // but a client-side guard saves a 4MB+ round-trip when the
+ // limit is small.
+ 'general_max_files_per_upload'
]);
})
.select('setting_key', 'setting_value');
@@ -145,6 +153,17 @@ router.get('/', async (req, res) => {
gallery_show_filter_bar: settingsObject.gallery_show_filter_bar !== false,
// Upload settings (safe to expose - needed for client-side validation)
allowed_file_types: settingsObject.general_allowed_file_types || 'jpg,jpeg,png,webp',
+ // #613 — per-batch file count limit. The guest UserPhotoUpload modal
+ // reads this both to render the "{{limit}} files per upload" hint
+ // (showed `{{limit}}` literal before this) and to refuse a batch
+ // before posting it. Backend route enforces the same value via
+ // getMaxFilesPerUpload(), so the client-side check is purely a UX
+ // optimisation. Default mirrors uploadSettings.js
+ // DEFAULT_MAX_FILES_PER_UPLOAD so the UI shows a sensible number
+ // even on installs that have never explicitly set the value.
+ general_max_files_per_upload: Number.isFinite(Number(settingsObject.general_max_files_per_upload))
+ ? Number(settingsObject.general_max_files_per_upload)
+ : 500,
// SEO meta tag flags (safe to expose - these are intended for crawlers)
seo_meta_noindex: settingsObject.seo_meta_noindex === true,
seo_meta_nofollow: settingsObject.seo_meta_nofollow === true,
diff --git a/frontend/src/components/gallery/UserPhotoUpload.tsx b/frontend/src/components/gallery/UserPhotoUpload.tsx
index 791aa624..955f3b18 100644
--- a/frontend/src/components/gallery/UserPhotoUpload.tsx
+++ b/frontend/src/components/gallery/UserPhotoUpload.tsx
@@ -32,6 +32,17 @@ export const UserPhotoUpload: React.FC
- {t('upload.fileRequirements')} + {/* #613 — pass { limit } so `{{limit}}` interpolates + with the real number from settings instead of + rendering literally. */} + {t('upload.fileRequirements', { limit: maxFilesPerUpload })}