From ea7bb64f0224fa3479d77f23a1e50b22f46846e5 Mon Sep 17 00:00:00 2001 From: zuiqiangxijiejin <166717129+zuiqiangxijiejin@users.noreply.github.com> Date: Tue, 10 Nov 2026 09:47:37 +0800 Subject: [PATCH] fix(nginx): use checkout-local temp paths for local development (#8075) * fix(nginx): use checkout-local temp paths for local development * docs(nginx): clarify local request buffering comment * docs: keep nginx guidance within the inherited byte budget --------- Co-authored-by: Willem Jiang --- AGENTS.md | 31 ++++++++++---------- README.md | 2 ++ backend/tests/test_nginx_local_temp_paths.py | 18 ++++++++++++ docker/nginx/nginx.local.conf | 14 ++++++--- 4 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 backend/tests/test_nginx_local_temp_paths.py diff --git a/AGENTS.md b/AGENTS.md index 211a859b8d..71ca5ec177 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,24 +25,23 @@ A single `make dev` / Docker stack runs four cooperating services: | **Frontend** | `3000` | Next.js web interface | | **Provisioner** | `8002` | Optional — only when sandbox is configured for provisioner/K8s mode | -Nginx is the single public entry: it proxies `/api/*` to the Gateway, rewriting -`/api/langgraph/*` onto the Gateway's native routes, and serves the frontend — see -[backend/AGENTS.md](backend/AGENTS.md) for the runtime and router detail. It compresses -HTML and configured textual assets, deliberately leaving SSE, fonts, images, audio, and -video uncompressed at the proxy layer. +Nginx serves the frontend and proxies `/api/*` to Gateway, rewriting +`/api/langgraph/*` to native routes. See [backend/AGENTS.md](backend/AGENTS.md). +It compresses HTML and configured text assets. SSE, fonts, images, audio, and +video stay uncompressed. -Keep the dedicated `/api/threads/{thread_id}/browser/stream` WebSocket regex before -the generic threads regex in Docker, local and Helm nginx configs. Upgrade headers -and the 600s read timeout are pinned by `backend/tests/test_nginx_browser_websocket.py`. -Helm uses nginx's default connect/send timeouts for the in-cluster Gateway. +Put the `/api/threads/{thread_id}/browser/stream` WebSocket regex before the +generic threads regex in Docker, local, and Helm configs. +`backend/tests/test_nginx_browser_websocket.py` pins upgrade headers and the +600s read timeout. Helm uses nginx defaults for Gateway connect/send timeouts. +Keep local temp paths checkout-relative and aligned with `serve.sh` and `nginx.sh`. -Both Compose files publish nginx at `"${BIND_HOST:-127.0.0.1}:${PORT:-2026}:2026"` -(README's **loopback default**); bare `"${PORT}:2026"` binds `0.0.0.0`. Root `PORT` -controls Docker ingress only; local Next.js stays on `3000` so `.env` cannot -redirect `make dev`'s port wait. Nginx's IPv4+IPv6 `default_server` and Gateway's -`0.0.0.0:8001` bind are container-internal; only published nginx is external. -Require explicit bind addresses for new published ports; -`backend/tests/test_compose_default_bind_host.py` pins every service in both files. +Both Compose files publish nginx at `"${BIND_HOST:-127.0.0.1}:${PORT:-2026}:2026"`. +Bare `"${PORT}:2026"` binds `0.0.0.0`. Require explicit bind addresses for new +published ports. `backend/tests/test_compose_default_bind_host.py` pins both files. +Root `PORT` controls Docker ingress only. Local Next.js stays on `3000` so +`.env` cannot redirect the dev port wait. Nginx's IPv4/IPv6 `default_server` +and Gateway's `0.0.0.0:8001` stay container-internal. Only published nginx is external. Provisioner PVC mode declares `USERDATA_PVC_NAME` once as `user-data`; extra and Lark broker credential mounts reuse it with their existing subPaths and read-only diff --git a/README.md b/README.md index 808f9f252b..87a0b67c9d 100644 --- a/README.md +++ b/README.md @@ -941,6 +941,8 @@ such a checkout, use `bash ./scripts/.sh ...`. make dev ``` + Local nginx writes temporary files under the checkout's `temp/` directory. + 5. **Access**: http://localhost:2026 6. **(Optional) Load sample memory data for local review**: open `Settings > Memory`, click **Import memory**, and select `backend/docs/memory-settings-sample.json`. The browser imports into the signed-in user's memory. diff --git a/backend/tests/test_nginx_local_temp_paths.py b/backend/tests/test_nginx_local_temp_paths.py new file mode 100644 index 0000000000..47f4d7a9df --- /dev/null +++ b/backend/tests/test_nginx_local_temp_paths.py @@ -0,0 +1,18 @@ +"""Local nginx must use the writable directories prepared by its launchers.""" + +import re + +import pytest +from support.nginx_conf import REPO_ROOT, read_config + + +@pytest.mark.parametrize("kind", ["client_body", "proxy", "fastcgi", "uwsgi", "scgi"]) +def test_local_nginx_temp_path_matches_launchers(kind: str) -> None: + directory = f"temp/{kind}_temp" + config = read_config("docker/nginx/nginx.local.conf") + paths = re.findall(rf"^[ \t]*{kind}_temp_path[ \t]+([^;\n]+);", config, re.M) + assert paths == [directory] + + for launcher in ("scripts/serve.sh", "scripts/nginx.sh"): + lines = (REPO_ROOT / launcher).read_text(encoding="utf-8").splitlines() + assert any(line.startswith("mkdir -p ") and directory in line.split()[2:] for line in lines), f"{launcher} must create {directory} before starting nginx" diff --git a/docker/nginx/nginx.local.conf b/docker/nginx/nginx.local.conf index 4a40cd6979..417902d4ab 100644 --- a/docker/nginx/nginx.local.conf +++ b/docker/nginx/nginx.local.conf @@ -7,6 +7,12 @@ events { } pid logs/nginx.pid; http { + client_body_temp_path temp/client_body_temp; + proxy_temp_path temp/proxy_temp; + fastcgi_temp_path temp/fastcgi_temp; + uwsgi_temp_path temp/uwsgi_temp; + scgi_temp_path temp/scgi_temp; + # Basic settings sendfile on; tcp_nopush on; @@ -114,8 +120,8 @@ http { # Long chat/text-prompt support (issue #3952): a sufficiently long # pasted prompt otherwise exceeds nginx's default 1m # client_max_body_size, or gets spooled to a temp file via - # proxy_request_buffering before reaching Gateway -- on a non-root - # local run that temp directory may not be writable, producing a + # proxy_request_buffering before reaching Gateway. Previously, an + # unwritable system temp directory on non-root runs could cause a # raw nginx 500 instead of a graceful application error. Mirrors # the uploads location's fix below (same mechanism, sized for text # prompts rather than binary file uploads). @@ -397,8 +403,8 @@ http { # Disable response buffering for the frontend. Without this, # nginx tries to spool large upstream responses (e.g. Next.js - # static chunks) into ``proxy_temp_path``, which defaults to - # the system-owned ``/var/lib/nginx/proxy`` and fails with + # static chunks) into ``proxy_temp_path``. System-owned defaults + # such as ``/var/lib/nginx/proxy`` can fail with # ``[crit] open() ... failed (13: Permission denied)`` when # nginx is launched as a non-root user (every dev machine # except production root containers). The symptom on the