diff --git a/CHANGELOG.md b/CHANGELOG.md index 8868f10..f84da80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 2.5.13 - 2026-08-05 + +### Fixed + +- Slide and template save forms now allow larger multipart text fields, so rich region content no longer trips Multer's default field-value limit. + ## 2.5.12 - 2026-08-05 ### Fixed diff --git a/package.json b/package.json index b1be75f..06a426d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.5.12", + "version": "2.5.13", "private": false, "description": "Pulse Signage application with MySQL and media storage", "repository": { diff --git a/src/web/lib/media/upload-sync.js b/src/web/lib/media/upload-sync.js index 2ba27fc..d11773f 100644 --- a/src/web/lib/media/upload-sync.js +++ b/src/web/lib/media/upload-sync.js @@ -19,6 +19,7 @@ function createUploadSyncService(options) { const notifyPlayerScreens = options && options.notifyPlayerScreens; const backgroundTaskQueue = options && options.backgroundTaskQueue; const MAX_UPLOAD_BYTES = 1024 * 1024 * 1024; + const MAX_FIELD_BYTES = 10 * 1024 * 1024; const pendingPlayerUploadSyncs = new Map(); let pendingPlayerUploadSyncFlushTimer = null; let pendingPlayerUploadSyncFlushInFlight = null; @@ -84,7 +85,8 @@ function createUploadSyncService(options) { return multer({ storage: storage, limits: { - fileSize: MAX_UPLOAD_BYTES + fileSize: MAX_UPLOAD_BYTES, + fieldSize: MAX_FIELD_BYTES } }); } diff --git a/test/upload-sync.test.js b/test/upload-sync.test.js new file mode 100644 index 0000000..de5e063 --- /dev/null +++ b/test/upload-sync.test.js @@ -0,0 +1,48 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); +const express = require('express'); + +require('../src/common'); + +const { createUploadSyncService } = require('../src/web/lib/media'); + +test('multipart uploads accept large text fields used by slide and template forms', async () => { + const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-')); + const uploadSyncService = createUploadSyncService({ + common: {}, + playerSnapshotCache: new Map(), + notifyPlayerScreens: async () => {} + }); + const upload = uploadSyncService.createUploadMiddleware(uploadDir); + const app = express(); + + app.post('/upload', upload.any(), function (_req, res) { + res.sendStatus(204); + }); + + app.use(function (error, _req, res, _next) { + res.status(error.statusCode || 500).send(String(error && error.message ? error.message : error)); + }); + + const server = app.listen(0); + + try { + const address = server.address(); + const largeValue = 'x'.repeat(1_100_000); + const formData = new FormData(); + formData.set('region_text_6', largeValue); + + const response = await fetch(`http://127.0.0.1:${address.port}/upload`, { + method: 'POST', + body: formData + }); + + assert.equal(response.status, 204); + } finally { + await new Promise((resolve) => server.close(resolve)); + fs.rmSync(uploadDir, { recursive: true, force: true }); + } +}); \ No newline at end of file