Files
edr-platform/docs/uploads.md

87 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Upload size limits
A document upload passes through four independent ceilings. The **smallest one
wins**, so raising a limit in application code changes nothing while a lower
limit sits in front of it.
| # | Layer | Limit | Where it lives |
| - | ----- | ----- | -------------- |
| 1 | Reverse proxy (`client_max_body_size`) | **50m** | nginx config on the API host — **not in this repo** for deployed environments |
| 2 | Express JSON/urlencoded body | `JSON_BODY_LIMIT` = 80mb | `apps/edr-freight-api/src/main.ts` |
| 3 | Multer multipart (`fileSize`) | `DOCUMENT_UPLOAD_MAX_BYTES` = 50MB | `apps/edr-freight-api/src/common/document-upload.options.ts` |
| 4 | Per-field portal gate (`max_size_mb`) | 50 | `freight.file_upload_fields`, seeded by `file-upload-settings.seeder.ts` |
Layer 4 is the only one the customer sees before uploading — `SmartFileInput`
blocks the file client-side with "File size exceeds the limit of NMB". Layers
13 produce a failed request after the fact.
## The nginx layer is the one that bites
nginx defaults `client_max_body_size` to **1m** and answers anything larger with
its own HTML error page:
```
HTTP/1.1 413 Request Entity Too Large
Server: nginx
Content-Type: text/html
```
Two tells that a 413 came from the proxy rather than the API:
- the body is nginx's HTML page, not the API's JSON envelope, and
- `curl -w '%{size_upload}'` reports **0** — nginx rejects on the `Content-Length`
header, so the body is never transmitted.
This is also why the failure looks like a CORS error in the browser: nginx's
error response carries no `Access-Control-Allow-Origin` header.
### Applying it
Add to the `server` (or `location`) block fronting the API and reload:
```nginx
server {
server_name edrfreightapi-staging.edrsc.com;
client_max_body_size 50m;
location / {
proxy_pass http://freight-api:3001;
# Large uploads stream for a while; the default 60s read timeout can
# cut off a slow client mid-body.
proxy_read_timeout 300s;
proxy_request_buffering off;
}
}
```
```bash
nginx -t && nginx -s reload # -t first: a bad config that reloads takes the site down
```
On Kubernetes ingress-nginx this is an annotation on the Ingress instead:
```yaml
nginx.ingress.kubernetes.io/proxy-body-size: 50m
```
Note that `client_max_body_size 0` disables the check entirely — do not use it.
An unbounded body is a denial-of-service vector, and layer 3 buffers uploads in
memory.
## Verifying end to end
512KB should pass the proxy and reach the API; 50MB should too. A `401` here is
a *success* for this purpose — it means the request got past nginx to the API's
auth layer.
```bash
head -c 52428800 /dev/urandom > big.bin
curl -s -o /dev/null -w 'HTTP %{http_code} uploaded %{size_upload}\n' \
-X POST "https://edrfreightapi-staging.edrsc.com/api/companies/<id>/documents" \
-F "test=@big.bin"
```
- `413` with `uploaded 0` → the proxy is still capped; layer 1 was not applied.
- `401`/`200` with the full byte count → the body made it through.