Reaching 100% in the upload progress bar only means the browser finished SENDING the bytes to nginx - it does not mean the server has finished receiving/saving the file. For multi-GB files this gap was invisible, making a genuinely-still-working upload look frozen at 100 percent, leading to a premature reload (confirmed via nginx access log: status 499, client closed request).
nginx-panel.conf: added fastcgi_request_buffering off on the admin panels /api/ location so nginx streams the body straight to PHP-FPM instead of fully buffering it to a temp file first and reading it back (a full extra disk I/O pass for large files); also added client_body_timeout/fastcgi_send_timeout/send_timeout at 1800s alongside the existing fastcgi_read_timeout, since those are separate timeout phases that previously still defaulted to 60s.
admin.js: added an xhr.upload load-complete handler that switches the dialog to a clear "Finishing up on the server..." state once the send phase hits 100%, so large uploads no longer look stuck during the save phase.
nginx-panel.conf: added client_max_body_size 20g at the http level (this nginx instance only serves the NovaCPX panel, isolated from the system nginx), and scoped generous PHP upload limits (upload_max_filesize/post_max_size 20G, execution/input time 1800s) to just the admin panels /api/ location via fastcgi_param PHP_VALUE - this avoids touching the shared php8.3-fpm www pool config used by other sites.
admin.js: rewrote fmSubmitUpload from fetch() (no progress support, and would throw uncaught on a non-JSON error body like an nginx 413 page) to XMLHttpRequest with a real progress bar (percent + bytes transferred) that stays visible for the whole transfer instead of the dialog closing immediately on click.