From ada22773c11980a045bd4d9c2f34fbafd2ce2d23 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Thu, 9 Jul 2026 22:21:18 +0800 Subject: [PATCH] Add remote development support with Docker Nginx configuration - Introduce a new `nginx-remote-dev` service in `docker-compose.yaml` for remote development environments, allowing Nginx to proxy requests to the host machine's frontend and backend services. - Create a new Nginx configuration file `ai-video.remote-dev.conf` tailored for remote setups, ensuring proper handling of WebSocket and API requests. - Update `README.md` to include detailed instructions for setting up remote development with Nginx, including certificate generation and service initialization. - Enhance the `setup-certs.sh` script to support additional hostnames for certificate signing, improving flexibility for remote access. - Revise documentation to clarify the development paths and requirements for local and remote environments. --- deploy/README.md | 141 +++++++++++++++++++++++++- deploy/nginx/ai-video.remote-dev.conf | 83 +++++++++++++++ deploy/setup-certs.sh | 16 ++- docker-compose.yaml | 16 +++ 4 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 deploy/nginx/ai-video.remote-dev.conf diff --git a/deploy/README.md b/deploy/README.md index 1e813e5..7d9bc46 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,8 +1,8 @@ -# 本地 / 局域网 HTTPS 调试 +# 开发 HTTPS 调试部署 语音预览要求麦克风可用,浏览器只在 **安全上下文**(localhost 或 https)下放行 -`getUserMedia`。本机用 localhost 就够;**要在局域网用 IP 给别的设备测,就得上 https**。 -这里用 mkcert 签本地受信任证书 + nginx 反代统一 TLS。 +`getUserMedia`。本机用 localhost 就够;**局域网或远程机器用 IP/域名给别的设备测, +就要走 https**。开发环境推荐 nginx 统一做 TLS 入口,前端页面、HTTP API 和 WS 信令都同源。 ## 结构 @@ -16,7 +16,9 @@ 前端页面和信令 ws 同源(同 host 同端口),没有混合内容 / 跨源问题。 -## 步骤 +## 路径 A:本机 / 局域网,nginx 直接跑在宿主机 + +适合 Mac 本机调试,或同一局域网里手机访问电脑 IP。 ```bash # 1) 装 mkcert(只需一次) @@ -37,6 +39,118 @@ nginx -c "$(pwd)/deploy/nginx/ai-video.dev.conf" -g 'daemon off;' # 局域网:https://<本机IP> (脚本结尾会打印) ``` +`deploy/nginx/ai-video.dev.conf` 里的证书路径是本机绝对路径。换目录或换机器时, +把 `ssl_certificate` 和 `ssl_certificate_key` 改到当前仓库的 `deploy/certs/`。 + +## 路径 B:远程开发机,Docker nginx + 宿主机 npm/uvicorn + +适合在云主机/远程 Linux 上开发:数据库可以用 Docker,但前端和后端单独跑, +便于热更新和看日志。nginx 跑在 Docker 容器里,通过 `host.docker.internal` +反代到宿主机上的 `3000/8000`。 + +### 1. 准备证书 + +有域名时推荐使用正式证书,把证书文件放到: + +```text +deploy/certs/ai-video.pem +deploy/certs/ai-video-key.pem +``` + +只做开发调试也可以用 mkcert。远程机器上把访问用的 IP/域名传给脚本: + +```bash +./deploy/setup-certs.sh <远程机器公网IP或内网IP> <可选域名> +``` + +mkcert 是本地 CA。浏览器所在设备要免警告,需要信任这台远程机器生成的 +`rootCA.pem`;否则可以临时接受浏览器的自签证书警告。 + +### 2. 启动数据库并初始化 + +```bash +docker compose up -d postgres +make db-seed +``` + +`make db-seed` 会建表、同步接口定义、写入模型资源和助手示例。模型密钥不放 +`.env`,启动后到前端「组件 / 模型」里把 `replace-me` 换成真实密钥。 + +### 3. 单独启动后端 + +后端必须监听 `0.0.0.0`,否则 nginx 容器连不到宿主机端口: + +```bash +cd backend +cp .env.example .env + +# 如果 postgres 用 docker compose,宿主机连库可用 localhost:5432 +# backend/.env: +# DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/postgres + +uv run --with-requirements requirements.txt \ + uvicorn app:app --host 0.0.0.0 --port 8000 --reload +``` + +### 4. 单独启动前端 + +前端也监听 `0.0.0.0`。`NEXT_PUBLIC_API_BASE_URL` 要写浏览器最终访问 nginx 的 +https 地址,不要写 `:8000`: + +```bash +cd frontend +npm install +NEXT_PUBLIC_API_BASE_URL=https://<远程机器IP或域名> \ + npm run dev -- --hostname 0.0.0.0 +``` + +### 5. 启动 Docker nginx + +推荐用 compose profile: + +```bash +docker compose --profile remote-dev up -d nginx-remote-dev +``` + +也可以直接 `docker run`: + +```bash +docker run --rm --name ai-video-nginx \ + --add-host=host.docker.internal:host-gateway \ + -p 80:80 -p 443:443 \ + -v "$PWD/deploy/nginx/ai-video.remote-dev.conf:/etc/nginx/nginx.conf:ro" \ + -v "$PWD/deploy/certs:/etc/nginx/certs:ro" \ + nginx:alpine +``` + +访问: + +```text +https://<远程机器IP或域名> +``` + +### 6. 远程语音 / WebRTC 注意事项 + +- 云安全组至少放行 `80/tcp`、`443/tcp`。 +- 如果浏览器和后端不在同一内网,WebRTC 媒体链路通常需要 TURN。 +- 启动 coturn: + +```bash +# 项目根 .env: +# PUBLIC_IP=<远程机器公网IP> +# TURN_SECRET=<和 backend/.env 一致的密钥> + +docker compose --profile remote up -d coturn +``` + +- 云安全组放行 `3478/tcp`、`3478/udp`、`49152-49200/udp`。 +- `backend/.env` 中配置: + +```text +TURN_URLS=turn:<远程机器公网IP>:3478?transport=udp,turn:<远程机器公网IP>:3478?transport=tcp +TURN_SECRET=<同上> +``` + ## 前端怎么连后端 前端读环境变量 `NEXT_PUBLIC_API_BASE_URL`(compose 里已设)。走 nginx 后, @@ -50,7 +164,7 @@ wsUrl = NEXT_PUBLIC_API_BASE_URL.replace(/^http/, 'ws') + '/ws/voice' > 没有反代、直连后端时则是 `https://:8000`,但那样要给后端单独配证书、 > 还有跨源,不推荐。统一走 nginx 最干净。 -## 给别的设备(手机等)免警告 +## 给别的设备免警告 证书是 mkcert 本地 CA 签的,只有装了该 CA 的设备才信任: @@ -64,3 +178,20 @@ en0/en1 IP 加进 SAN;换网络换了 IP,重跑脚本即可。 ## 证书不入库 `deploy/.gitignore` 已忽略 `certs/`。私钥不要提交。 + +## 常见问题 + +### nginx 容器里为什么不用 127.0.0.1? + +Docker 容器里的 `127.0.0.1` 是容器自己,不是宿主机。远程开发路径使用 +`host.docker.internal`,并通过 `host-gateway` 映射到宿主机。 + +### 页面能打开,但 HMR 或语音 WS 断开? + +检查 nginx 配置里是否保留了 `Upgrade`、`Connection` 头,以及 `/ws/` 的 +`proxy_read_timeout 3600s`。本仓库的两份开发 nginx 配置都已经包含。 + +### API 请求跨域报错? + +走 nginx 时前端和后端同源,通常不需要跨源。若你绕过 nginx 直接访问后端, +需要在 `backend/.env` 的 `CORS_ORIGINS` 加上前端实际 origin。 diff --git a/deploy/nginx/ai-video.remote-dev.conf b/deploy/nginx/ai-video.remote-dev.conf new file mode 100644 index 0000000..7962103 --- /dev/null +++ b/deploy/nginx/ai-video.remote-dev.conf @@ -0,0 +1,83 @@ +# AI Video Assistant —— 远程开发机用 nginx 反代 +# +# 适用场景: +# nginx 跑在 Docker 容器里; +# Next dev 和 uvicorn 跑在 Docker 宿主机上。 +# +# 容器内不能用 127.0.0.1 访问宿主机进程,所以这里使用 host.docker.internal。 +# 启动容器时需要加: +# --add-host=host.docker.internal:host-gateway +# 或使用 docker-compose.yaml 里的 nginx-remote-dev profile。 + +worker_processes 1; +events { worker_connections 256; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + sendfile on; + + upstream ui_upstream { + server host.docker.internal:3000; + } + + upstream api_upstream { + server host.docker.internal:8000; + } + + server { + listen 80; + server_name _; + return 301 https://$host$request_uri; + } + + server { + listen 443 ssl; + server_name _; + + ssl_certificate /etc/nginx/certs/ai-video.pem; + ssl_certificate_key /etc/nginx/certs/ai-video-key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + location /ws/ { + proxy_pass http://api_upstream; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_buffering off; + } + + location /api/ { + proxy_pass http://api_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + client_max_body_size 50M; + } + + location /health { + proxy_pass http://api_upstream; + proxy_set_header Host $host; + } + + location / { + proxy_pass http://ui_upstream; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + } + } +} diff --git a/deploy/setup-certs.sh b/deploy/setup-certs.sh index e53f622..f945842 100755 --- a/deploy/setup-certs.sh +++ b/deploy/setup-certs.sh @@ -1,11 +1,13 @@ #!/usr/bin/env bash -# 用 mkcert 生成本地受信任 TLS 证书,供 deploy/nginx/ai-video.dev.conf 使用。 +# 用 mkcert 生成本地受信任 TLS 证书,供开发 nginx 配置使用。 # # mkcert 会建一个"本地 CA"并装进系统/浏览器信任库,之后它签的证书在本机零警告。 # 局域网里其它设备(手机/别的电脑)要免警告,需把这个 CA 根证书也装到那台设备上 # (见末尾提示)。 # -# 用法: ./deploy/setup-certs.sh +# 用法: +# ./deploy/setup-certs.sh +# ./deploy/setup-certs.sh 192.168.1.20 dev.example.com set -euo pipefail CERT_DIR="$(cd "$(dirname "$0")" && pwd)/certs" @@ -23,7 +25,12 @@ echo "▶ 安装/确认本地 CA(mkcert -install)…" mkcert -install # 3) 探测本机局域网 IP(其它设备靠这个 IP 访问) -LAN_IP="$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || true)" +LAN_IP="$( + ipconfig getifaddr en0 2>/dev/null \ + || ipconfig getifaddr en1 2>/dev/null \ + || hostname -I 2>/dev/null | awk '{print $1}' \ + || true +)" if [ -z "$LAN_IP" ]; then echo "⚠ 没探测到局域网 IP(en0/en1),证书将只覆盖 localhost。" echo " 如需 LAN 访问,手动重跑:mkcert ... <你的IP>" @@ -32,6 +39,9 @@ fi # 4) 签证书:覆盖 localhost / 回环 / 局域网 IP / 一个好记的本地域名 HOSTS=(localhost 127.0.0.1 ::1 ai-video.local) [ -n "$LAN_IP" ] && HOSTS+=("$LAN_IP") +for extra_host in "$@"; do + [ -n "$extra_host" ] && HOSTS+=("$extra_host") +done echo "▶ 为以下名字签发证书:${HOSTS[*]}" mkcert -cert-file "$CERT_DIR/ai-video.pem" \ diff --git a/docker-compose.yaml b/docker-compose.yaml index 2435472..58c4e1f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -153,6 +153,22 @@ services: - ui networks: [app-network] + # ---- 可选(profile: remote-dev):远程开发机 Docker nginx + 宿主机 npm/uvicorn ---- + # 宿主机单独启动: + # frontend: NEXT_PUBLIC_API_BASE_URL=https:// npm run dev -- --hostname 0.0.0.0 + # backend: uvicorn app:app --host 0.0.0.0 --port 8000 --reload + nginx-remote-dev: + image: nginx:alpine + profiles: ["remote-dev"] + ports: + - "80:80" + - "443:443" + volumes: + - ./deploy/nginx/ai-video.remote-dev.conf:/etc/nginx/nginx.conf:ro + - ./deploy/certs:/etc/nginx/certs:ro + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: postgres_data: redis_data: