From 6c9cdf1af8ba299c4d998563d8f136b23f5ff0e9 Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Sat, 17 Oct 2026 14:17:23 +0000 Subject: [PATCH 1/3] Side findings of spec 010: local uploads, and the site server's proxy package - local.conf sets client_max_body_size 64m, as every deployment does: it set nothing, so nginx's 1 MB default refused local uploads that the deployments accept. limits.spec.ts now holds every configuration, local included, to at least Java's 50 MiB upload cap (shown failing on local.conf first). - serve-prod.js, the production site server, requires http-proxy-middleware, which was installed only as a dev package of another tool. It is now a declared dependency, at the 3.0.7 already resolved. The lockfile changes nothing but the dev flag on it and the four packages it needs at run time: no name or version moved at any depth (diffed). Co-Authored-By: Claude Opus 5.5 --- deploy/nginx/limits.spec.ts | 20 ++++++++++++++++++++ deploy/nginx/local.conf | 4 ++++ package-lock.json | 8 +++----- package.json | 1 + 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/deploy/nginx/limits.spec.ts b/deploy/nginx/limits.spec.ts index dc43b336..8b23a032 100644 --- a/deploy/nginx/limits.spec.ts +++ b/deploy/nginx/limits.spec.ts @@ -28,3 +28,23 @@ describe('per-client request limits', () => { }); } }); + +/** + * Uploads must reach the services: Java's own cap is 50 MiB, and a result for + * many samples is several megabytes. local.conf once set nothing, so nginx's + * 1 MB default refused local uploads that every deployment accepted. + */ +const JAVA_UPLOAD_CAP_MB = 50; + +describe('request body limits', () => { + for (const file of ['dev.conf', 'release.conf', 'production.conf', 'local.conf']) { + it(`${file} lets an upload of Java's size through`, () => { + const conf = readFileSync(join(__dirname, file), 'utf8'); + const sizes = [...conf.matchAll(/^\s*client_max_body_size\s+(\d+)m;/gm)].map((m) => + Number(m[1]) + ); + expect(sizes.length, 'no client_max_body_size').toBeGreaterThan(0); + for (const size of sizes) expect(size).toBeGreaterThanOrEqual(JAVA_UPLOAD_CAP_MB); + }); + } +}); diff --git a/deploy/nginx/local.conf b/deploy/nginx/local.conf index 4b508f07..445f9913 100644 --- a/deploy/nginx/local.conf +++ b/deploy/nginx/local.conf @@ -64,6 +64,10 @@ server { listen 80; server_name localhost; + # As every deployment: a result for many samples is several megabytes, and + # nginx's 1 MB default refused local uploads the deployments accept. + client_max_body_size 64m; + # The Tina admin is reachable here, and deliberately so: editing content is # the point of running locally. Every deployed environment denies it. location ~ ^/admin(/|$) { diff --git a/package-lock.json b/package-lock.json index 7fde382c..40d49d68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "express": "4.22.3", "fflate": "0.8.3", "gifenc": "1.0.3", + "http-proxy-middleware": "3.0.7", "immer": "^10.2.0", "lodash": "^4.18.1", "marked": "^17.0.6", @@ -11922,7 +11923,6 @@ }, "node_modules/@types/http-proxy": { "version": "1.17.17", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -17453,7 +17453,6 @@ }, "node_modules/follow-redirects": { "version": "1.15.11", - "dev": true, "funding": [ { "type": "individual", @@ -18191,7 +18190,6 @@ }, "node_modules/http-proxy": { "version": "1.18.1", - "dev": true, "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", @@ -18216,7 +18214,8 @@ }, "node_modules/http-proxy-middleware": { "version": "3.0.7", - "dev": true, + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.7.tgz", + "integrity": "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==", "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.15", @@ -25287,7 +25286,6 @@ }, "node_modules/requires-port": { "version": "1.0.0", - "dev": true, "license": "MIT" }, "node_modules/resize-observer-polyfill": { diff --git a/package.json b/package.json index 497c49b8..be95cb4a 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "express": "4.22.3", "fflate": "0.8.3", "gifenc": "1.0.3", + "http-proxy-middleware": "3.0.7", "immer": "^10.2.0", "lodash": "^4.18.1", "marked": "^17.0.6", From d11ab1f3ef51b8bdf10bbce294e9aa7890a67dfb Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Sat, 17 Oct 2026 14:26:16 +0000 Subject: [PATCH 2/3] Review of #508: the body-limit test reads every unit It matched only values written as "64m", so a smaller limit written as 512k (or a bare byte count) passed unseen. Every value is now converted from its unit; one it cannot read fails. Shown: 512k and a garbage value fail, 64M and 1g pass. Co-Authored-By: Claude Opus 5.5 --- deploy/nginx/limits.spec.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/deploy/nginx/limits.spec.ts b/deploy/nginx/limits.spec.ts index 8b23a032..e836311a 100644 --- a/deploy/nginx/limits.spec.ts +++ b/deploy/nginx/limits.spec.ts @@ -40,11 +40,16 @@ describe('request body limits', () => { for (const file of ['dev.conf', 'release.conf', 'production.conf', 'local.conf']) { it(`${file} lets an upload of Java's size through`, () => { const conf = readFileSync(join(__dirname, file), 'utf8'); - const sizes = [...conf.matchAll(/^\s*client_max_body_size\s+(\d+)m;/gm)].map((m) => - Number(m[1]) - ); - expect(sizes.length, 'no client_max_body_size').toBeGreaterThan(0); - for (const size of sizes) expect(size).toBeGreaterThanOrEqual(JAVA_UPLOAD_CAP_MB); + // Every value, in any unit nginx takes: matching only "64m" let a smaller + // limit written as "512k" pass unseen. A value it cannot read fails. + const values = [...conf.matchAll(/^\s*client_max_body_size\s+(\S+);/gm)].map((m) => m[1]); + expect(values.length, 'no client_max_body_size').toBeGreaterThan(0); + const unit: Record = { '': 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3 }; + for (const value of values) { + const parsed = /^(\d+)([kKmMgG]?)$/.exec(value); + const bytes = parsed ? Number(parsed[1]) * unit[parsed[2].toLowerCase()] : NaN; + expect([value, bytes >= JAVA_UPLOAD_CAP_MB * 1024 ** 2]).toEqual([value, true]); + } }); } }); From 040c2aed5ce256216ea18df8ad0c3d351b000b2c Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Sat, 17 Oct 2026 14:33:09 +0000 Subject: [PATCH 3/3] Tasks 010: T054 and T055 done in #508 Co-Authored-By: Claude Opus 5.5 --- specs/010-java-services/tasks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/010-java-services/tasks.md b/specs/010-java-services/tasks.md index 3c7c2cae..fdf9320c 100644 --- a/specs/010-java-services/tasks.md +++ b/specs/010-java-services/tasks.md @@ -205,8 +205,8 @@ Each cutover is one PR. It adds the group's patterns to node's table, one nginx ## Phase 8: Polish and side findings -- [ ] T054 [P] Set `client_max_body_size` in `deploy/nginx/local.conf` to match the other deployments (64 MB), so local uploads over 1 MB are not refused. Test it with `deploy/nginx/test-config.sh`. -- [ ] T055 [P] Declare the proxy package that `projects/website-angular/src/scripts/serve-prod.js` uses in `package.json` (it is only present as a dev-only transitive dependency), and check the lockfile changes only that entry +- [x] T054 [P] Set `client_max_body_size` in `deploy/nginx/local.conf` to match the other deployments (64 MB), so local uploads over 1 MB are not refused. Test it with `deploy/nginx/test-config.sh`. — #508 +- [x] T055 [P] Declare the proxy package that `projects/website-angular/src/scripts/serve-prod.js` uses in `package.json` (it is only present as a dev-only transitive dependency), and check the lockfile changes only that entry — #508 - [ ] T056 Update `specs/006-content-service-to-node/tasks.md` Phase 9 rows with the PR that moved each Java-homed operation, as each moves - [ ] T057 Update the curator-facing documents if any reader-visible behaviour is touched by a cutover (constitution: Development Workflow) - [ ] T058 [EXT] Decide the analysis report's species overview figure. The PDF report embeds the retired Fireworks image, drawn by Java's fireworks-exporter; the site's overview is now the Reacfoam (Voronoi) map. Showing that in the report needs analysis-report changed upstream (its owners) or the figure supplied to it.