Add HTTPS demo entry for microphone access
- Add a self-signed HTTPS Nginx entrypoint on Docker port 4443 so browser microphone APIs can run in demo mode. - Keep the existing HTTP port 4002 unchanged while exposing container port 443 and generating the demo certificate during image build. - Update CORS defaults and Compose environment for the HTTPS frontend origin. - Clarify the report editor microphone message with localhost, HTTPS, and browser trusted-origin demo options. - Document the browser HTTP microphone limitation, HTTPS demo URL, and Chrome/Edge insecure-origin workaround in README and docs.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Backend API development defaults.
|
||||
API_PORT=3100
|
||||
API_BODY_LIMIT="100mb"
|
||||
CORS_ORIGIN="http://localhost:3001,http://localhost:4002"
|
||||
CORS_ORIGIN="http://localhost:3001,http://localhost:4002,https://localhost:4443"
|
||||
DATABASE_URL="postgresql://surclaw:surclaw_dev_password@localhost:5433/surclaw?schema=public"
|
||||
SESSION_SECRET="change-me-in-production"
|
||||
SESSION_COOKIE_SECURE="false"
|
||||
|
||||
@@ -309,6 +309,8 @@ npm run test:e2e
|
||||
|
||||
API 默认 JSON/urlencoded 请求体上限为 `100mb`,由 `API_BODY_LIMIT` 控制;Nginx 同步配置了 `client_max_body_size 100m`,用于承载迁移期报告 HTML、图片/关键帧和通用文件 Data URL 上传。
|
||||
|
||||
Docker 前端默认暴露 `http://localhost:4002`,并额外暴露自签名 HTTPS 演示入口 `https://localhost:4443`。浏览器麦克风 API 不能在普通局域网 HTTP 页面中由应用代码强行开启;语音听写演示优先使用 HTTPS 入口,或用 Chrome/Edge 的 `--unsafely-treat-insecure-origin-as-secure` 临时标记 HTTP 来源。
|
||||
|
||||
### `server/prisma/schema.prisma`
|
||||
|
||||
PostgreSQL 数据模型。当前覆盖 `Tenant`、`Department`、`User`、`UserSession`、`AppSession`、`Report`、`ReportMedia`、`ReportHistory`、`Template`、模板部门授权、`FileResource`、`SystemSetting` 和 `AuditLog`。Prisma 7 连接配置在根目录 `prisma.config.ts` 中。
|
||||
|
||||
@@ -8,7 +8,15 @@ RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
RUN apk add --no-cache openssl \
|
||||
&& mkdir -p /etc/nginx/certs \
|
||||
&& openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||
-keyout /etc/nginx/certs/surclaw-demo.key \
|
||||
-out /etc/nginx/certs/surclaw-demo.crt \
|
||||
-subj "/CN=localhost" \
|
||||
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -223,6 +223,7 @@ docker-compose up -d --build
|
||||
|
||||
```text
|
||||
前端: http://localhost:4002
|
||||
语音 HTTPS 演示入口: https://localhost:4443
|
||||
API: http://localhost:3002/api/health
|
||||
数据库: localhost:5433
|
||||
```
|
||||
@@ -236,6 +237,7 @@ docker-compose down
|
||||
部署说明:
|
||||
|
||||
- `web` 服务使用 Nginx 托管前端 `dist/`。
|
||||
- Docker 前端同时暴露 `http://localhost:4002` 和自签名证书的 `https://localhost:4443`;麦克风听写建议使用 HTTPS 演示入口。
|
||||
- `api` 服务运行 NestJS 后端,并把上传文件目录挂载到 `uploads_data` volume。
|
||||
- `db` 服务运行 PostgreSQL 16。
|
||||
- `nginx.conf` 已配置 SPA 路由回退、`/api` 反向代理和 `100m` 请求体上限。
|
||||
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
environment:
|
||||
API_PORT: 3100
|
||||
API_BODY_LIMIT: 100mb
|
||||
CORS_ORIGIN: http://localhost:4002,http://localhost:3001
|
||||
CORS_ORIGIN: http://localhost:4002,https://localhost:4443,http://localhost:3001
|
||||
DATABASE_URL: postgresql://surclaw:surclaw_dev_password@db:5432/surclaw?schema=public
|
||||
SESSION_SECRET: change-me-in-production
|
||||
SESSION_COOKIE_SECURE: "false"
|
||||
@@ -41,6 +41,7 @@ services:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4002:80"
|
||||
- "4443:443"
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
|
||||
@@ -74,11 +74,11 @@ AI 和语音密钥由后端 Settings API 保存并由代理使用,前端不再
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
默认通过 Nginx 暴露 `http://localhost:4002`。
|
||||
默认通过 Nginx 暴露 `http://localhost:4002`,并额外暴露自签名证书的语音演示入口 `https://localhost:4443`。
|
||||
|
||||
当前 Compose 服务:
|
||||
|
||||
- `web`:前端静态站点,暴露 `http://localhost:4002`。
|
||||
- `web`:前端静态站点,暴露 `http://localhost:4002` 和 `https://localhost:4443`。
|
||||
- `api`:NestJS API,暴露 `http://localhost:3002`。
|
||||
- `db`:PostgreSQL 16,暴露 `localhost:5433`。
|
||||
- `uploads_data`:后端文件持久化 volume。
|
||||
@@ -88,7 +88,23 @@ docker-compose up -d --build
|
||||
- `Dockerfile` 使用 Node 构建 `dist/`。
|
||||
- 运行阶段使用 `nginx:alpine` 托管静态文件。
|
||||
- `Dockerfile.server` 构建并运行 NestJS API。
|
||||
- `nginx.conf` 已配置 SPA 路由回退、`/api` 反向代理和 `100m` 请求体上限。
|
||||
- `nginx.conf` 已配置 SPA 路由回退、`/api` 反向代理、`100m` 请求体上限和自签名 HTTPS 演示入口。
|
||||
|
||||
## 麦克风访问
|
||||
|
||||
浏览器不允许普通局域网 HTTP 页面调用麦克风,代码无法绕过这个限制。Docker 演示环境建议使用:
|
||||
|
||||
```text
|
||||
https://localhost:4443
|
||||
```
|
||||
|
||||
首次打开会看到自签名证书提示,接受后即可让浏览器开放麦克风权限。如果必须用 `http://局域网IP:4002` 演示,只能在 Chrome/Edge 启动时加临时参数,把该 HTTP 来源标记为可信:
|
||||
|
||||
```bash
|
||||
google-chrome --unsafely-treat-insecure-origin-as-secure=http://局域网IP:4002 --user-data-dir=/tmp/surclaw-chrome-demo
|
||||
```
|
||||
|
||||
这是浏览器演示开关,不是系统安全方案;正式环境仍建议使用 HTTPS。
|
||||
|
||||
## 部署边界
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
| 关键帧插入 | 真实集成 | 关键帧可点击插入或拖入图片占位符;上传成功后编辑器会把插入图片从 Data URL 替换为受控文件 URL。 |
|
||||
| AI 辅助撰写 | 真实集成 | 前端调用 `/api/ai/chat`,后端使用全局共用 Provider Key 代理 OpenAI 兼容 `/chat/completions`;需要有效 Provider 配置、模型和网络。 |
|
||||
| AI 差异确认 | 真实可用 | 使用 `diff` 生成左右差异,确认后写入 AI 区域。 |
|
||||
| 讯飞语音听写 | 真实集成 | 前端使用麦克风采集音频并连接 `/api/speech/iat`;后端读取讯飞配置、生成鉴权 URL、补齐首帧 APPID/业务参数并转发 IAT 结果。需要浏览器权限、安全上下文(`localhost` 或 HTTPS)、有效配置和网络。 |
|
||||
| 讯飞语音听写 | 真实集成 | 前端使用麦克风采集音频并连接 `/api/speech/iat`;后端读取讯飞配置、生成鉴权 URL、补齐首帧 APPID/业务参数并转发 IAT 结果。需要浏览器权限、安全上下文(`localhost` 或 HTTPS)、有效配置和网络;Docker 提供 `https://localhost:4443` 演示入口。 |
|
||||
| AI/语音密钥管理 | 真实集成 | AI Key 和讯飞 APIKey/APISecret 均由后端代理读取和使用;普通用户读取设置时不返回真实密钥。 |
|
||||
| 系统设置 | 真实集成 | `SystemSettings` 优先调用 `/api/settings/system` 读取和保存抽帧、默认模板、AI Provider、语音配置;“恢复演示出厂设置”会二次确认后调用后端 demo reset,清空报告/审计并恢复默认用户、模板和演示配置。只有开发/显式回退模式下 API 不可用才回退本地缓存。 |
|
||||
| 审计日志查看 | 真实集成 | 超级管理员和管理员可进入审计日志页,调用 `GET /api/audit-logs` 查看登录、报告、模板、用户、部门、设置和文件等操作;管理员只看本部门或自己相关日志。 |
|
||||
|
||||
@@ -28,6 +28,7 @@ docker compose version
|
||||
| NestJS API 本地服务 | `3100` | `npm run server:dev` 默认监听 |
|
||||
| Docker API 暴露端口 | `3002` | 宿主机访问 Compose API |
|
||||
| Docker 前端入口 | `4002` | 推荐的整套系统访问入口 |
|
||||
| Docker 前端 HTTPS 演示入口 | `4443` | 自签名证书入口,推荐用于麦克风听写 |
|
||||
| Docker PostgreSQL | `5433` | 宿主机访问 Compose 数据库 |
|
||||
|
||||
如果这些端口被占用,优先修改 `.env`、`docker-compose.yaml` 和对应文档,避免回退到 `3000` 等默认端口。
|
||||
@@ -45,6 +46,7 @@ docker compose up -d --build
|
||||
|
||||
```text
|
||||
http://localhost:4002
|
||||
https://localhost:4443
|
||||
```
|
||||
|
||||
健康检查:
|
||||
@@ -56,7 +58,7 @@ curl http://localhost:3002/api/health
|
||||
|
||||
当前 Compose 会启动:
|
||||
|
||||
- `tuwen_web`:Nginx 托管前端,宿主机端口 `4002`。
|
||||
- `tuwen_web`:Nginx 托管前端,宿主机端口 `4002`;同时提供自签名 HTTPS 演示入口 `4443`。
|
||||
- `tuwen_api`:NestJS API,容器内监听 `3100`,宿主机端口 `3002`。
|
||||
- `tuwen_db`:PostgreSQL 16,宿主机端口 `5433`。
|
||||
|
||||
@@ -149,7 +151,7 @@ VITE_API_PROXY_TARGET="http://localhost:3002"
|
||||
|
||||
完成启动后建议按顺序验证:
|
||||
|
||||
1. 打开 `http://localhost:4002` 或本地开发地址 `http://localhost:3001`。
|
||||
1. 打开 `http://localhost:4002`、语音演示地址 `https://localhost:4443` 或本地开发地址 `http://localhost:3001`。
|
||||
2. 使用 `admin / 123456` 登录。
|
||||
3. 进入工作台,确认统计卡片能正常加载。
|
||||
4. 进入“系统设置”,确认全局配置可保存。
|
||||
@@ -190,7 +192,7 @@ Docker 方式应访问 `http://localhost:4002`。本地开发方式应确保 `np
|
||||
检查占用:
|
||||
|
||||
```bash
|
||||
ss -ltnp | grep -E ':3001|:3002|:3100|:4002|:5433'
|
||||
ss -ltnp | grep -E ':3001|:3002|:3100|:4002|:4443|:5433'
|
||||
```
|
||||
|
||||
本项目不应使用 `3000` 作为默认 API 端口。如果必须改端口,同步修改 `.env`、`docker-compose.yaml`、`vite.config.ts` 或 Nginx upstream,并更新文档。
|
||||
|
||||
@@ -85,7 +85,7 @@ AI 面板支持两种模式:
|
||||
|
||||
- 前端连接 `/api/speech/iat`,不再生成讯飞鉴权 URL,也不读取 APPID/APIKey/APISecret。
|
||||
- 浏览器采集麦克风音频,转换为 16k PCM 后发送音频帧。
|
||||
- 启动前会检查浏览器是否支持 `navigator.mediaDevices.getUserMedia` 和 `AudioContext`;如果不是 `localhost` 或 HTTPS 等安全上下文,浏览器会禁止麦克风能力,页面会提示切换访问方式。
|
||||
- 启动前会检查浏览器是否支持 `navigator.mediaDevices.getUserMedia` 和 `AudioContext`;如果不是 `localhost` 或 HTTPS 等安全上下文,浏览器会禁止麦克风能力。Docker 演示环境可使用 `https://localhost:4443`,局域网普通 HTTP 只能通过 Chrome/Edge 演示启动参数临时标记为可信来源。
|
||||
- 后端读取 Settings API 中的 `xfSpeechConfig`,连接讯飞 IAT,上游首帧由后端补齐 `common.app_id` 和默认 `business` 参数。
|
||||
- 识别结果由后端转发回前端,并追加到 AI 输入框。
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- 系统设置、抽帧策略、AI Provider、语音参数和默认模板已优先接入 Settings API,只有开发/显式回退模式下才保留本地缓存回退。
|
||||
- Docker/Nginx 静态部署配置已存在。
|
||||
- Docker/Nginx 与 NestJS API 已把请求体上限统一到 `100mb`,避免图文报告和文件 Data URL 上传触发默认 413。
|
||||
- Docker Nginx 已额外提供自签名 HTTPS 演示入口 `4443`,用于浏览器麦克风听写权限;普通局域网 HTTP 仍受浏览器限制。
|
||||
- 开发端口已调整为 `3001`。
|
||||
- 已补充 Vitest 测试框架和核心功能单元/组件测试。
|
||||
- 已补充功能盘点,区分真实功能、外部集成、前端演示和预留项。
|
||||
@@ -79,3 +80,4 @@
|
||||
| 2026-05-02 | 将默认“腹腔镜胆囊切除术报告”后端 seed 与前端默认报告内容对齐,并把系统设置重置改为演示模式恢复出厂设置。 |
|
||||
| 2026-05-02 | 修正报告草稿后端校验和保存失败提示,补充麦克风启动前置检查。 |
|
||||
| 2026-05-02 | 增加 Nginx 和 NestJS 请求体上限配置,修复大图文报告保存 `request entity too large`。 |
|
||||
| 2026-05-02 | 新增 Docker HTTPS 演示入口和麦克风访问说明,解决非安全上下文下语音听写不可启动的问题。 |
|
||||
|
||||
37
nginx.conf
37
nginx.conf
@@ -33,3 +33,40 @@ server {
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name localhost;
|
||||
client_max_body_size 100m;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/surclaw-demo.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/surclaw-demo.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://api:3100/api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $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 $scheme;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1123,7 +1123,7 @@ export default function ReportEditor() {
|
||||
if (!mediaDevices?.getUserMedia) {
|
||||
alert(window.isSecureContext
|
||||
? '当前浏览器不支持麦克风采集,请更换新版 Chrome/Edge 后重试。'
|
||||
: '麦克风需要在 localhost 或 HTTPS 环境下使用。请通过 localhost 访问,或配置 HTTPS 后重试。');
|
||||
: '浏览器不允许在普通局域网 HTTP 页面中调用麦克风。请使用 http://localhost:4002、https://localhost:4443,或用浏览器演示参数把当前 HTTP 地址标记为可信。');
|
||||
return;
|
||||
}
|
||||
if (!AudioContextClass) {
|
||||
|
||||
Reference in New Issue
Block a user