From 6025bbab128e39fa0c6c54014bc93f54bbdfeaf5 Mon Sep 17 00:00:00 2001 From: ailuckly Date: Sat, 11 Apr 2026 13:36:20 +0800 Subject: [PATCH] fix: add HTTPS support for microphone access on remote servers - Add getUserMedia availability check with clear error message - Add Nginx self-signed cert setup script (scripts/setup-https.sh) for enabling HTTPS on IP-only servers where mediaDevices is undefined --- scripts/setup-https.sh | 101 +++++++++++++++++++++++++++++++++ vocata-web/src/utils/aiChat.ts | 5 ++ 2 files changed, 106 insertions(+) create mode 100644 scripts/setup-https.sh diff --git a/scripts/setup-https.sh b/scripts/setup-https.sh new file mode 100644 index 0000000..230e799 --- /dev/null +++ b/scripts/setup-https.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# 生成自签证书 + 配置 Nginx HTTPS 反代 +# 用法: sudo bash setup-https.sh + +set -euo pipefail + +SERVER_IP="86.53.161.33" +SSL_DIR="/etc/nginx/ssl" +NGINX_CONF="/etc/nginx/sites-available/vocata-https" + +echo "=== 1. 生成自签 SSL 证书 ===" +mkdir -p "$SSL_DIR" + +# 生成含 IP SAN 的证书(现代浏览器要求 SAN,不认纯 CN) +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$SSL_DIR/vocata.key" \ + -out "$SSL_DIR/vocata.crt" \ + -subj "/C=CN/ST=Beijing/O=VocaTa/CN=$SERVER_IP" \ + -addext "subjectAltName=IP:$SERVER_IP" + +echo "证书已生成: $SSL_DIR/vocata.crt" + +echo "" +echo "=== 2. 写入 Nginx 配置 ===" +cat > "$NGINX_CONF" <<'NGINX' +# VocaTa HTTPS 反向代理 +# 前端: https://86.53.161.33 → localhost:3000 +# 后端: https://86.53.161.33/api → localhost:9009 +# WebSocket: wss://86.53.161.33/ws → localhost:9009 + +server { + listen 443 ssl; + server_name 86.53.161.33; + + ssl_certificate /etc/nginx/ssl/vocata.crt; + ssl_certificate_key /etc/nginx/ssl/vocata.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + # 前端 + location / { + proxy_pass http://127.0.0.1:3000; + 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; + } + + # Vite HMR WebSocket (开发模式) + location /vite-hmr { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # 后端 API + location /api/ { + proxy_pass http://127.0.0.1:9009/api/; + 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; + } + + # WebSocket (AI 语音对话) + location /ws/ { + proxy_pass http://127.0.0.1:9009/ws/; + 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_read_timeout 3600s; + proxy_send_timeout 3600s; + } +} + +# HTTP → HTTPS 重定向 +server { + listen 80; + server_name 86.53.161.33; + return 301 https://$host$request_uri; +} +NGINX + +echo "Nginx 配置已写入: $NGINX_CONF" + +echo "" +echo "=== 3. 启用站点并检测配置 ===" +ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/vocata-https +nginx -t + +echo "" +echo "=== 4. 重载 Nginx ===" +systemctl reload nginx + +echo "" +echo "✅ 完成!访问 https://86.53.161.33" +echo " 浏览器会提示不安全(自签证书),点「高级」→「继续访问」即可" +echo " mediaDevices.getUserMedia 在 HTTPS 下可正常使用" diff --git a/vocata-web/src/utils/aiChat.ts b/vocata-web/src/utils/aiChat.ts index 99e9933..9cc114f 100644 --- a/vocata-web/src/utils/aiChat.ts +++ b/vocata-web/src/utils/aiChat.ts @@ -415,6 +415,11 @@ export class AudioManager { console.log('🎤 请求麦克风权限...') + // 检查安全上下文(getUserMedia 仅在 HTTPS 或 localhost 下可用) + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + throw new Error('当前环境不支持语音功能,请使用 HTTPS 访问或在 localhost 上测试') + } + // 直接获取麦克风权限 this.audioStream = await navigator.mediaDevices.getUserMedia({ audio: {