uploads: support up to 1 GB attachments, streamed to disk (was 25 MB, buffered in memory) (batch145)

- Server streams the upload body straight to /data/uploads (a .part temp file, atomic rename on
  success), backpressure-aware, so a 1 GB file never buffers in RAM. MAX_UPLOAD_MB env (default
  1024 = 1 GB) controls the cap; error message reflects it.
- Client size guard raised 25 MB -> 1 GB.
- docker-compose documents MAX_UPLOAD_MB and the required Nginx Proxy Manager client_max_body_size.
NOTE: the actual bottleneck for the user's 9.7 MB reject is almost certainly NPM's client_max_body_size
(nginx default 1 MB) — that must be raised in the NPM admin; the app change alone can't lift it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 16:06:19 +05:30
parent 90d49d29d0
commit 5960efb612
3 changed files with 36 additions and 14 deletions
+2 -2
View File
@@ -1158,7 +1158,7 @@
</head>
<body>
<script src="/icons.js?v=6"></script>
<script>window.__BUILD='2026-07-20-batch144';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
<script>window.__BUILD='2026-07-20-batch145';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
//
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
@@ -2178,7 +2178,7 @@ function fmtSize(b){ b=+b||0; if(b<1024) return b+' B'; if(b<1048576) return Mat
// (and a %), making it obvious when it's safe to hit Send.
function uploadFile(file){
if(!file) return;
if(file.size>25*1024*1024){ toast('“'+file.name+'” is too large (max 25 MB)'); return; }
if(file.size>1024*1024*1024){ toast('“'+file.name+'” is too large (max 1 GB)'); return; }
const ph={ id:'up-'+Math.random().toString(36).slice(2), name:file.name, mime:file.type||'', uploading:true, pct:0, size:file.size };
pendingAttachs.push(ph); renderAttachBar();
const fail=(msg)=>{ const i=pendingAttachs.indexOf(ph); if(i>=0) pendingAttachs.splice(i,1); renderAttachBar(); toast(msg||'Upload failed'); };