# 开发 HTTPS 调试部署 语音预览要求麦克风可用,浏览器只在 **安全上下文**(localhost 或 https)下放行 `getUserMedia`。本机用 localhost 就够;**局域网或远程机器用 IP/域名给别的设备测, 就要走 https**。开发环境推荐 nginx 统一做 TLS 入口,前端页面、HTTP API 和 WS 信令都同源。 ## 结构 ``` 浏览器 ──https/wss──> nginx :443 (唯一 TLS 入口, mkcert 证书) ├── /ws/ → 后端 :8000 (/ws/voice 信令、/ws/stream 裸流) ├── /api/ → 后端 :8000 (assistants/model-resources/...) ├── /health → 后端 :8000 └── / → 前端 :3000 (Next dev + HMR) ``` 前端页面和信令 ws 同源(同 host 同端口),没有混合内容 / 跨源问题。 ## 路径 A:本机 / 局域网,nginx 直接跑在宿主机 适合 Mac 本机调试,或同一局域网里手机访问电脑 IP。 ```bash # 1) 装 mkcert(只需一次) brew install mkcert nss # 2) 生成证书(本机 CA + localhost/LAN IP/ai-video.local 的证书) ./deploy/setup-certs.sh # 3) 起前后端(任选其一) docker compose up -d # api:8000 + ui:3030 都发布到 localhost # 或本地分别 npm run dev / uvicorn app:app --port 8000 # 4) 起 nginx(装一下:brew install nginx) nginx -c "$(pwd)/deploy/nginx/ai-video.dev.conf" -g 'daemon off;' # 5) 访问 # 本机: https://localhost 或 https://ai-video.local # 局域网: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 的同源地址**。如果地址栏带端口,这里也必须带端口; 如果地址栏不带端口,这里也不带端口。不要写后端直连端口 `:8000`。 ```bash cd frontend npm install # nginx 使用默认 443 时: 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 ``` 如果远程机器希望用非标准 HTTPS 端口,例如 `18189`,把宿主机端口映射到 容器的 `443` 即可: ```bash NGINX_HTTPS_PORT=18189 docker compose --profile remote-dev up -d nginx-remote-dev ``` 此时前端也要使用带端口的同源地址: ```bash NEXT_PUBLIC_API_BASE_URL=https://<远程机器IP或域名>:18189 \ npm run dev -- --hostname 0.0.0.0 ``` 浏览器访问: ```text https://<远程机器IP或域名>:18189 ``` 也可以直接 `docker run`: ```bash docker run --rm --name ai-video-nginx \ --add-host=host.docker.internal:host-gateway \ -p 80:80 -p 18189: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或域名>:18189 ``` ### 6. 远程语音 / WebRTC 注意事项 - 云安全组至少放行 `80/tcp`、`443/tcp`。 - 如果 HTTPS 映射到 `18189`,云安全组要放行 `18189/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`。走 nginx 后,让它等于浏览器访问 页面的 **origin** 即可。origin 包含协议、host 和可选端口: ``` 默认 443: NEXT_PUBLIC_API_BASE_URL=https://<访问用的host> 自定义端口: NEXT_PUBLIC_API_BASE_URL=https://<访问用的host>:18189 wsUrl = NEXT_PUBLIC_API_BASE_URL.replace(/^http/, 'ws') + '/ws/voice' ``` > 没有反代、直连后端时则是 `https://:8000`,但那样要给后端单独配证书、 > 还有跨源,不推荐。统一走 nginx 最干净。 ## 给别的设备免警告 证书是 mkcert 本地 CA 签的,只有装了该 CA 的设备才信任: ```bash mkcert -CAROOT # 打印 CA 目录,里面的 rootCA.pem 拷到设备并信任 ``` LAN IP 不在证书 SAN 里会报名称不匹配——`setup-certs.sh` 已自动把探测到的 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。