fix(uploads): apply configured max file size to guest uploads (#613 follow-up)
The admin's Settings → General → "Max File Size (MB)" value (general_max_file_size_mb) never applied to guest gallery uploads — the guest route hardcoded multer's per-file cap at 50MB (gallery.js) and the guest UI hardcoded the same 50MB client-side guard and "max 50MB" hint text. So a guest could not upload a large video even when the admin raised the limit (reported by mat1990dj on #613). Same class as the file-count miss fixed in #614, for size. - uploadSettings.js: new getMaxFileSizeMb()/getMaxFileSizeBytes() reading general_max_file_size_mb (default 50MB, cached 60s, clamped to a 10GB ceiling), mirroring getMaxFilesPerUpload. - gallery.js (guest upload): multer limits.fileSize now resolves from the setting; a LIMIT_FILE_SIZE error returns an actionable "max N MB" message. - publicSettings.js: exposes general_max_file_size_mb (default 50) so the gallery UI can render the real limit and guard client-side before an oversized POST. - UserPhotoUpload.tsx: reads the limit, uses it for the client-side size guard, and passes it to the requirements hint. The "max 50MB" literal in upload.fileRequirements is now interpolated ({{sizeLimit}}) across all 8 locales; adds upload.fileTooLarge (en/de; others fall back to en). Scope: guest path only (the reported gap). The admin path keeps its generous 10GB cap — admins are trusted and default 50MB would otherwise regress large admin video uploads. Format and batch-size limits already work correctly and are untouched. Adds SQLite-backed unit tests for the new getter. Verified end-to-end on a booted instance: admin sets 500MB → persisted → public settings exposes 500 → guest multer sources its cap from it.
This commit is contained in:
@@ -43,6 +43,14 @@ export const UserPhotoUpload: React.FC<UserPhotoUploadProps> = ({
|
||||
? Number(publicSettings?.general_max_files_per_upload)
|
||||
: 500;
|
||||
|
||||
// Per-file size limit (MB). Was hardcoded to 50MB below, so the admin's
|
||||
// "Max File Size" setting never applied to guests (#613 follow-up). Surfaced
|
||||
// via publicSettings (default 50); the backend enforces the same value.
|
||||
const maxFileSizeMb = Number.isFinite(Number(publicSettings?.general_max_file_size_mb))
|
||||
? Number(publicSettings?.general_max_file_size_mb)
|
||||
: 50;
|
||||
const maxFileSizeBytes = maxFileSizeMb * 1024 * 1024;
|
||||
|
||||
const allowedMimeTypes = useMemo(
|
||||
() => extensionsToMimeTypes(publicSettings?.allowed_file_types),
|
||||
[publicSettings?.allowed_file_types]
|
||||
@@ -60,9 +68,9 @@ export const UserPhotoUpload: React.FC<UserPhotoUploadProps> = ({
|
||||
toast.error(`Invalid file type: ${file.name}`);
|
||||
return false;
|
||||
}
|
||||
// Check file size (50MB max)
|
||||
if (file.size > 50 * 1024 * 1024) {
|
||||
toast.error(`File too large: ${file.name}`);
|
||||
// Check file size against the configured per-file limit.
|
||||
if (file.size > maxFileSizeBytes) {
|
||||
toast.error(t('upload.fileTooLarge', { name: file.name, limit: maxFileSizeMb }));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -228,7 +236,7 @@ export const UserPhotoUpload: React.FC<UserPhotoUploadProps> = ({
|
||||
{/* #613 — pass { limit } so `{{limit}}` interpolates
|
||||
with the real number from settings instead of
|
||||
rendering literally. */}
|
||||
{t('upload.fileRequirements', { limit: maxFilesPerUpload })}
|
||||
{t('upload.fileRequirements', { limit: maxFilesPerUpload, sizeLimit: maxFileSizeMb })}
|
||||
</p>
|
||||
<input
|
||||
type="file"
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
"noCategory": "Keine Kategorie",
|
||||
"eventSpecific": "(Veranstaltungsspezifisch)",
|
||||
"clickToUpload": "Klicken zum Hochladen oder per Drag & Drop",
|
||||
"fileRequirements": "JPEG, PNG oder WebP (max. 50MB pro Datei, {{limit}} Dateien pro Upload)",
|
||||
"fileRequirements": "JPEG, PNG oder WebP (max. {{sizeLimit}}MB pro Datei, {{limit}} Dateien pro Upload)",
|
||||
"selectedFiles": "Ausgewählte Dateien",
|
||||
"uploading": "Wird hochgeladen...",
|
||||
"transferring": "Übertragung",
|
||||
@@ -192,7 +192,8 @@
|
||||
"processingFailed_one": "{{count}} Foto konnte nicht verarbeitet werden",
|
||||
"processingFailed_other": "{{count}} Fotos konnten nicht verarbeitet werden",
|
||||
"uploadingChunks_one": "{{count}} Teil wird hochgeladen",
|
||||
"uploadingChunks_other": "{{count}} Teile werden hochgeladen"
|
||||
"uploadingChunks_other": "{{count}} Teile werden hochgeladen",
|
||||
"fileTooLarge": "Datei zu groß: {{name}} (max. {{limit}}MB)"
|
||||
},
|
||||
"navigation": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
"noCategory": "No category",
|
||||
"eventSpecific": "(Event specific)",
|
||||
"clickToUpload": "Click to upload or drag and drop",
|
||||
"fileRequirements": "JPEG, PNG or WebP (max 50MB per file, {{limit}} files per upload)",
|
||||
"fileRequirements": "JPEG, PNG or WebP (max {{sizeLimit}}MB per file, {{limit}} files per upload)",
|
||||
"selectedFiles": "Selected files",
|
||||
"uploading": "Uploading...",
|
||||
"transferring": "Transferring",
|
||||
@@ -192,7 +192,8 @@
|
||||
"processingFailed_one": "{{count}} photo failed to process",
|
||||
"processingFailed_other": "{{count}} photos failed to process",
|
||||
"uploadingChunks_one": "{{count}} chunk uploading",
|
||||
"uploadingChunks_other": "{{count}} chunks uploading"
|
||||
"uploadingChunks_other": "{{count}} chunks uploading",
|
||||
"fileTooLarge": "File too large: {{name}} (max {{limit}}MB)"
|
||||
},
|
||||
"navigation": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"noCategory": "Sin categoría",
|
||||
"eventSpecific": "(Específico del evento)",
|
||||
"clickToUpload": "Haz clic para subir o arrastra y suelta",
|
||||
"fileRequirements": "JPEG, PNG o WebP (máx. 50MB por archivo, {{limit}} archivos por subida)",
|
||||
"fileRequirements": "JPEG, PNG o WebP (máx. {{sizeLimit}}MB por archivo, {{limit}} archivos por subida)",
|
||||
"fileRequirementsMedia": "Imágenes JPEG, PNG o WebP y videos MP4/MOV/WEBM (máx. 50MB por archivo, {{limit}} archivos por subida)",
|
||||
"unsupportedFiles": "Algunos archivos se omitieron porque el formato no es compatible (usa JPEG/PNG/WebP/MP4/MOV/WEBM).",
|
||||
"selectedFiles": "Archivos seleccionados",
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"noCategory": "Aucune catégorie",
|
||||
"eventSpecific": "(Spécifique à l'événement)",
|
||||
"clickToUpload": "Cliquez pour téléverser ou glissez-déposez",
|
||||
"fileRequirements": "JPEG, PNG ou WebP (max 50 Mo par fichier, {{limit}} fichiers par téléversement)",
|
||||
"fileRequirements": "JPEG, PNG ou WebP (max {{sizeLimit}} Mo par fichier, {{limit}} fichiers par téléversement)",
|
||||
"selectedFiles": "Fichiers sélectionnés",
|
||||
"uploading": "Téléversement en cours...",
|
||||
"transferring": "Transfert en cours",
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"noCategory": "Geen categorie",
|
||||
"eventSpecific": "(Evenement-specifiek)",
|
||||
"clickToUpload": "Klik om te uploaden of sleep bestanden hierheen",
|
||||
"fileRequirements": "JPEG, PNG of WebP (max. 50 MB per bestand, {{limit}} bestanden per upload)",
|
||||
"fileRequirements": "JPEG, PNG of WebP (max. {{sizeLimit}} MB per bestand, {{limit}} bestanden per upload)",
|
||||
"selectedFiles": "Geselecteerde bestanden",
|
||||
"uploading": "Uploaden...",
|
||||
"uploadComplete": "Upload voltooid!",
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"noCategory": "Sem categoria",
|
||||
"eventSpecific": "(Específico do evento)",
|
||||
"clickToUpload": "Clique para enviar ou arraste e solte",
|
||||
"fileRequirements": "JPEG, PNG ou WebP (máx. 50MB por arquivo, {{limit}} arquivos por envio)",
|
||||
"fileRequirements": "JPEG, PNG ou WebP (máx. {{sizeLimit}}MB por arquivo, {{limit}} arquivos por envio)",
|
||||
"selectedFiles": "Arquivos selecionados",
|
||||
"uploading": "Enviando...",
|
||||
"uploadComplete": "Envio concluído!",
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"noCategory": "Без категории",
|
||||
"eventSpecific": "(Для конкретного события)",
|
||||
"clickToUpload": "Нажмите для загрузки или перетащите файлы",
|
||||
"fileRequirements": "JPEG, PNG или WebP (макс. 50 МБ на файл, {{limit}} файлов за загрузку)",
|
||||
"fileRequirements": "JPEG, PNG или WebP (макс. {{sizeLimit}} МБ на файл, {{limit}} файлов за загрузку)",
|
||||
"selectedFiles": "Выбранные файлы",
|
||||
"uploading": "Загрузка...",
|
||||
"uploadComplete": "Загрузка завершена!",
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
"noCategory": "Brez kategorije",
|
||||
"eventSpecific": "(specifično za dogodek)",
|
||||
"clickToUpload": "Kliknite za nalaganje ali povlecite in spustite",
|
||||
"fileRequirements": "JPEG, PNG ali WebP (največ 50 MB na datoteko, {{limit}} datotek na nalaganje)",
|
||||
"fileRequirements": "JPEG, PNG ali WebP (največ {{sizeLimit}} MB na datoteko, {{limit}} datotek na nalaganje)",
|
||||
"selectedFiles": "Izbrane datoteke",
|
||||
"uploading": "Nalaganje...",
|
||||
"transferring": "Prenašanje",
|
||||
|
||||
Reference in New Issue
Block a user