zlwu před 1 rokem
rodič
revize
c7d86c63fa

+ 1 - 1
frontend/.env

@@ -1,2 +1,2 @@
 # Tolan 前端环境配置
-REACT_APP_WS_URL=ws://192.168.2.113:8765
+REACT_APP_WS_URL=ws://192.168.2.59:8765

+ 1 - 1
frontend/.env.development

@@ -1,5 +1,5 @@
 # 开发环境配置
-REACT_APP_WS_URL=wss://192.168.2.113:8766
+REACT_APP_WS_URL=wss://www.hikelly.chat/ws
 #REACT_APP_API_URL=http://192.168.2.59:8000
 
 # 开发模式设置

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
frontend/node_modules/.cache/.eslintcache


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
frontend/node_modules/.cache/tsconfig.tsbuildinfo


+ 1 - 1
frontend/package.json

@@ -22,7 +22,7 @@
   },
   "scripts": {
     "start": "react-scripts start",
-    "start:https": "HTTPS=true SSL_CRT_FILE=../ssl/ios-server.crt SSL_KEY_FILE=../ssl/ios-server.key react-scripts start",
+    "start:https": "HTTPS=true SSL_CRT_FILE=../ssl/tolan.crt SSL_KEY_FILE=../ssl/tolan.key react-scripts start",
     "build": "react-scripts build",
     "build:prod": "NODE_ENV=production npm run build",
     "build:analyze": "npm run build && npx serve -s build",

+ 176 - 0
frontend/src/utils/browserCompatibility.ts

@@ -0,0 +1,176 @@
+/**
+ * 浏览器兼容性检查工具
+ */
+
+export interface BrowserCompatibility {
+  supported: boolean;
+  incompatibleFeatures: string[];
+  browserInfo: {
+    name: string;
+    version: string;
+    isMobile: boolean;
+  };
+}
+
+/**
+ * 检查浏览器对录音功能的兼容性
+ */
+export function checkBrowserCompatibility(): BrowserCompatibility {
+  const incompatibleFeatures: string[] = [];
+  const userAgent = navigator.userAgent.toLowerCase();
+  
+  // 检测浏览器信息
+  const browserInfo = {
+    name: getBrowserName(userAgent),
+    version: getBrowserVersion(userAgent),
+    isMobile: isMobileDevice(userAgent)
+  };
+
+  // 检查必需的API
+  if (!('MediaRecorder' in window)) {
+    incompatibleFeatures.push('MediaRecorder');
+  }
+
+  if (!('navigator' in window) || !('mediaDevices' in navigator)) {
+    incompatibleFeatures.push('navigator.mediaDevices');
+  }
+
+  if (!navigator.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== 'function') {
+    incompatibleFeatures.push('getUserMedia');
+  }
+
+  if (!window.AudioContext && !(window as any).webkitAudioContext) {
+    incompatibleFeatures.push('AudioContext');
+  }
+
+  // 检查HTTPS要求(生产环境)
+  if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
+    incompatibleFeatures.push('HTTPS (录音功能需要安全连接)');
+  }
+
+  // 移动端特殊检查
+  if (browserInfo.isMobile) {
+    // iOS Safari版本检查
+    if (browserInfo.name === 'Safari' && browserInfo.version) {
+      const version = parseInt(browserInfo.version);
+      if (version < 14) {
+        incompatibleFeatures.push('iOS Safari版本过低 (需要14.0+)');
+      }
+    }
+
+    // 检查移动端支持的音频格式
+    if ('MediaRecorder' in window) {
+      const supportedFormats = [
+        'audio/webm;codecs=opus',
+        'audio/webm',
+        'audio/mp4',
+        'audio/ogg;codecs=opus'
+      ];
+
+      const hasSupportedFormat = supportedFormats.some(format => 
+        MediaRecorder.isTypeSupported(format)
+      );
+
+      if (!hasSupportedFormat) {
+        incompatibleFeatures.push('移动端音频格式支持');
+      }
+    }
+  }
+
+  return {
+    supported: incompatibleFeatures.length === 0,
+    incompatibleFeatures,
+    browserInfo
+  };
+}
+
+/**
+ * 检测是否为移动设备
+ */
+export function isMobileDevice(userAgent?: string): boolean {
+  const ua = userAgent || navigator.userAgent.toLowerCase();
+  return /android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua);
+}
+
+/**
+ * 获取浏览器名称
+ */
+function getBrowserName(userAgent: string): string {
+  if (userAgent.includes('chrome') && !userAgent.includes('edg')) {
+    return 'Chrome';
+  } else if (userAgent.includes('firefox')) {
+    return 'Firefox';
+  } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
+    return 'Safari';
+  } else if (userAgent.includes('edg')) {
+    return 'Edge';
+  } else if (userAgent.includes('opera')) {
+    return 'Opera';
+  }
+  return 'Unknown';
+}
+
+/**
+ * 获取浏览器版本
+ */
+function getBrowserVersion(userAgent: string): string {
+  let version = 'Unknown';
+  
+  if (userAgent.includes('chrome') && !userAgent.includes('edg')) {
+    const match = userAgent.match(/chrome\/(\d+)/i);
+    if (match) version = match[1];
+  } else if (userAgent.includes('firefox')) {
+    const match = userAgent.match(/firefox\/(\d+)/i);
+    if (match) version = match[1];
+  } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
+    const match = userAgent.match(/version\/(\d+)/i);
+    if (match) version = match[1];
+  } else if (userAgent.includes('edg')) {
+    const match = userAgent.match(/edg\/(\d+)/i);
+    if (match) version = match[1];
+  }
+  
+  return version;
+}
+
+/**
+ * 获取详细的兼容性报告
+ */
+export function getCompatibilityReport(): string {
+  const compatibility = checkBrowserCompatibility();
+  const { browserInfo, supported, incompatibleFeatures } = compatibility;
+  
+  let report = `浏览器: ${browserInfo.name} ${browserInfo.version}\n`;
+  report += `设备类型: ${browserInfo.isMobile ? '移动设备' : '桌面设备'}\n`;
+  report += `录音支持: ${supported ? '✅ 支持' : '❌ 不支持'}\n`;
+  
+  if (!supported && incompatibleFeatures.length > 0) {
+    report += `缺少功能: ${incompatibleFeatures.join(', ')}\n`;
+  }
+  
+  // API支持详情
+  report += '\nAPI支持详情:\n';
+  report += `- MediaRecorder: ${'MediaRecorder' in window ? '✅' : '❌'}\n`;
+  report += `- getUserMedia: ${navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function' ? '✅' : '❌'}\n`;
+  report += `- AudioContext: ${window.AudioContext || (window as any).webkitAudioContext ? '✅' : '❌'}\n`;
+  report += `- HTTPS: ${location.protocol === 'https:' || location.hostname === 'localhost' ? '✅' : '❌'}\n`;
+  
+  // 音频格式支持
+  if ('MediaRecorder' in window) {
+    const formats = [
+      'audio/webm;codecs=opus',
+      'audio/webm',
+      'audio/mp4',
+      'audio/ogg;codecs=opus',
+      'audio/wav'
+    ];
+    
+    report += '\n支持的音频格式:\n';
+    formats.forEach(format => {
+      const supported = MediaRecorder.isTypeSupported(format);
+      report += `- ${format}: ${supported ? '✅' : '❌'}\n`;
+    });
+  }
+  
+  return report;
+} 

+ 2 - 2
scripts/backend-start.sh

@@ -34,8 +34,8 @@ fi
 
 # 检查SSL证书文件
 SSL_DIR="$PROJECT_ROOT/ssl"
-SSL_CERT_FILE="$SSL_DIR/ios-server.crt"
-SSL_KEY_FILE="$SSL_DIR/ios-server.key"
+SSL_CERT_FILE="$SSL_DIR/tolan.crt"
+SSL_KEY_FILE="$SSL_DIR/tolan.key"
 
 if [ ! -f "$SSL_CERT_FILE" ]; then
     echo -e "${YELLOW}警告: SSL证书文件不存在: $SSL_CERT_FILE${NC}"

+ 23 - 20
ssl/server.crt

@@ -1,22 +1,25 @@
 -----BEGIN CERTIFICATE-----
-MIIDuTCCAqGgAwIBAgIUQ0p5t4Coo16ZcEAkrWIiLAGIrvEwDQYJKoZIhvcNAQEL
-BQAwYjELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
-aWppbmcxDjAMBgNVBAoMBVRvbGFuMQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9j
-YWxob3N0MB4XDTI1MDkyNDA0MTAyMloXDTI2MDkyNDA0MTAyMlowYjELMAkGA1UE
-BhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxDjAMBgNV
-BAoMBVRvbGFuMQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvi1rmUgKuoYP99xPGr/pHUFsOSFl
-YkRGmlQ1nhi240qUTGozeGQSUgk15AX9aYi2rQw4uMxyGpzifCpeh8+6lXXeVzOj
-ssPHec4Egmn5f36qQQmCXB7u2wH80RWjAp8G1LB8NZm1d4I9mMEy8ziR3thH5TN6
-pgP3W9MWyCXDQBey6/utNfVxihsWjftPGBuuur5MkTx7v/g3R3oTXwiWXPfvZKC6
-nsb34DWuQ6ptoQuHExlJZ+8BBbcW0GY31Tm52o3/9oVKU4sL23kt3dn7r5NHxgy1
-5gi/aONdGzI7hv/HbdCT5TLLq/bDxTXGyQ7fu/h760aeAgF08gnSBh3bmwIDAQAB
-o2cwZTBEBgNVHREEPTA7gglsb2NhbGhvc3SCCyoubG9jYWxob3N0ggkxMjcuMC4w
-LjGHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYDVR0OBBYEFHweIp9pGrwnWXiY
-P+gPm2qPXhFNMA0GCSqGSIb3DQEBCwUAA4IBAQAzS0HHPCyIlps4p83n4kK2hB5c
-VbBQoxXhEyUVWekSM5RMELPuLi+tOrLyBbCT9BFjhUS6EVd0YbklT4emGYg6b8i3
-L2uRYJDUTJvxHsMwFUGKPIeY1C3tBncdJSJhVJ2OgU/B0cnWBOEmU4l2UvCuQgq6
-jxcrZipq9M5Gvxs61gGJXie9CR9DQtGAvVpvGf9O1TWadAt/OytE7RYRjiuSIN29
-Ns5t1/exxfHzYKpulsBRIUrRPNXDky0jqTMvCpmuYv+V605MZkSLBZNfhTS92GlD
-V9s3uTM3ciaM2BpPbs65ezUafxxt77j7+ObSv/lK74/R4/j+StfCh1toCQM3
+MIIERTCCAy2gAwIBAgIUDrin0m6DarCjTuNZ/V2mrkOxjXgwDQYJKoZIhvcNAQEL
+BQAwezELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
+aWppbmcxGjAYBgNVBAoMEVRvbGFuIERldmVsb3BtZW50MRYwFAYDVQQLDA1JVCBE
+ZXBhcnRtZW50MRQwEgYDVQQDDAt0b2xhbi5sb2NhbDAeFw0yNTA5MjUwMjI0NDJa
+Fw0yNjA5MjUwMjI0NDJaMHsxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5n
+MRAwDgYDVQQHDAdCZWlqaW5nMRowGAYDVQQKDBFUb2xhbiBEZXZlbG9wbWVudDEW
+MBQGA1UECwwNSVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwLdG9sYW4ubG9jYWwwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiUdwLoR1dTbJNIJkqS/gt88J6
+tKWQpaPrEglHPC3qt9Nr7dGkFB/ON7BVvhG+BNdRD4j2SWKQmeqN6510N/F6Kurj
++ws7rUrDcav3pCq1Lk9xo35ste/gpadZtBL/3fdBOvh/gGd6+tBFoPOf7KxHkQcp
+wfGtUQxx6TbjUfoLclPI7S/THlgOrC8QLFfEiTB8ewnvDSPUjUukkQN8OdBBy6bf
+hn5zjHJoA1HCzH5xdHPFSHAEr1FkXjUYDWitqtlWOzkQ9mGDvu4I9HHNX/vWD2y8
+/hug1WKxD22Dg4E7k+zQ5abYgD1CuAY6DuorAn3lqiz8vwIvVngT0pxeNWzrAgMB
+AAGjgcAwgb0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYB
+BQUHAwEwTgYDVR0RBEcwRYIJbG9jYWxob3N0ggt0b2xhbi5sb2NhbIINKi50b2xh
+bi5sb2NhbIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYcEwKgCOzAdBgNVHQ4EFgQU
+282uJZdqn1Li0xh8Cw8nbSjw9mEwHwYDVR0jBBgwFoAUKjeH+GGTeYHgNcrpTC9j
+jqrPq48wDQYJKoZIhvcNAQELBQADggEBAErkC+SuqKahsWpwdRoAnPflaEqJD+eV
+fG78CJ0fQHDAHdWNn7QqqA2TR6gUdNAIWL4z4vtsp83Kyfm0aM9QN53/gBRkM+Tm
+6VpPmAx/DVNDpADBLWwy/RZ2ytMk5zKO3tKSrrUBE03gmD17ejZemXZC7MeyxqDR
+Sihb4jMJ0IfH5Sa4T/W1zOYqJGztEotdGvq2l5FqT4XK4wREzk+uPy7HWT8B2I1B
+X530jG3Ym3PD3NeYNOjtcXW4dbFakmJFg9FgM+So/psoTTKPrsKO62dNfkDWPeGU
+xqWTusYL7FWknmP66X0TlqDdUfUsEX58YXe8WETFFZZsjAwk4JWbdoQ=
 -----END CERTIFICATE-----

+ 26 - 26
ssl/server.key

@@ -1,28 +1,28 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+LWuZSAq6hg/3
-3E8av+kdQWw5IWViREaaVDWeGLbjSpRMajN4ZBJSCTXkBf1piLatDDi4zHIanOJ8
-Kl6Hz7qVdd5XM6Oyw8d5zgSCafl/fqpBCYJcHu7bAfzRFaMCnwbUsHw1mbV3gj2Y
-wTLzOJHe2EflM3qmA/db0xbIJcNAF7Lr+6019XGKGxaN+08YG666vkyRPHu/+DdH
-ehNfCJZc9+9koLqexvfgNa5Dqm2hC4cTGUln7wEFtxbQZjfVObnajf/2hUpTiwvb
-eS3d2fuvk0fGDLXmCL9o410bMjuG/8dt0JPlMsur9sPFNcbJDt+7+HvrRp4CAXTy
-CdIGHdubAgMBAAECggEAC0gm18SE0mo350nRWDJl/LTQBpbwbJhv9yF4vs9o22n7
-YInKUjbe5Aak2klFWRiMUbllt1zK22ukKfPaK29gz74FecyCm7fD4nocyAnLtyBc
-ru1b/OVe4Rd97SoTQ162E9xWZK2g13KeqdcIPpKEC3bEKRjZNUu18GNrjhBjbq6p
-mAr6lE67N72ET7w07OKFemMm4xSowNTHLzc5IufTdDa76uGZv4/OU2MewZBP+ZAG
-bV/99CpggHRwTZ4R19VzlPxkITwQ833TIu4OoiPjmQBS5yDqmaN6dihtA7jlv0KX
-+WUxLydTkmdzstWSSqLH2fllzDRczFgIlnv4j/byeQKBgQDzGj+FlyRYxlRBNgcx
-aVkCpzui6f2R5H3Ncc43X7qdRizWc3OMBgMh8ta3ChjoI+6O+vucW7+s8SG/eMGc
-W5uwLr4840/l9ANQymdfcFymeuuiwIPQig+ToOUrBROqCBdaXfDewii0uwVfyx7q
-V2uXQqnoCHaJZWfUJu3eGyHJkwKBgQDIRFudpzmEPS6dbGAxO6dEYpKL61bCxzIG
-c2md2gm4sEeuZRvjiDZF8PaRVlxb3vuhKVne8uZnSSTK6P8VO0Nn241QuXJrZJqq
-BINX6b2HRzTveaJJYFugdo8tnC12bvYsKPu7IMn5JwXzJvIkt/qhFh1k+YIhDdhZ
-8QLL4GTK2QKBgH7o1AKEncfueau1bdngukQFU7+/0c1o8DfhUptb0UsriFrH2Klf
-tiDx6h+SGOCPaYmhAttkllLNayXO/5wFwugd7thQfrR4R4bUs1SCjwumHf9P60Bh
-4rHaC+kCdHqOUMqvQgC4cZGwFKUmn9QMoTFM7vAgAunFU4fQbttFZMUbAoGBAKqL
-HAHboG1utL22xwvmIwFneCjTVuU0QPOhYTXdsh2ncMOgsulG3AEfvLnz818BOwP5
-dOEZtQAMF9r8JA3uk+5j16oA4uF1oQhcI4XANXh4OSoeSQd3mU/C5xcOQuDyiYz9
-sbGfsKAOjVCNv/NpAXQgoxOULy763V1qktkP1IWxAoGAW5zrEcJtGabVogg+7JCf
-ABwTkPP3jDmlvvV1ap0DbREHBJbKOzfV8cMWT+kO810klvPHSGvq33zy/M1ZHNLq
-lLSS2J04hXDIf9nUQHoB7fBKKuAeTlWBk9tT5qEhFOegI+MtIq1qg9ZDvNfBVkWZ
-ErIFP/qkFgucoNWQBUD/bUo=
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCiUdwLoR1dTbJN
+IJkqS/gt88J6tKWQpaPrEglHPC3qt9Nr7dGkFB/ON7BVvhG+BNdRD4j2SWKQmeqN
+6510N/F6Kurj+ws7rUrDcav3pCq1Lk9xo35ste/gpadZtBL/3fdBOvh/gGd6+tBF
+oPOf7KxHkQcpwfGtUQxx6TbjUfoLclPI7S/THlgOrC8QLFfEiTB8ewnvDSPUjUuk
+kQN8OdBBy6bfhn5zjHJoA1HCzH5xdHPFSHAEr1FkXjUYDWitqtlWOzkQ9mGDvu4I
+9HHNX/vWD2y8/hug1WKxD22Dg4E7k+zQ5abYgD1CuAY6DuorAn3lqiz8vwIvVngT
+0pxeNWzrAgMBAAECggEAQwQtJ5gDeaxEe1eEJVRsSPylROgkm03oGXu+3OxZwNjY
+r2zm6ylr4EZZxWzOAngZNdyGHbk6upzxnFq1DXCzgE8L38icYK0xevQJnhrnjxpp
+rm9kGRocydFbAMw6M+2+0e24/1tSXhIDv1uuek7K6llUbccnMvmNe9Jf2QbVh7ep
+aBZJfVE0kaKfY5//4xxVaK8u2pycLpaC7f7iAqYDfvNxV7aiuu145AdqypI05ytt
+Rxqfa182bzCEVHY0b6ZteM4QjCtIImfsfssbL32GM1XK+AuhVXehkJxuL5W4v/QI
+E4sO8s5a35+b6TFXHOkGyAt9VDNIj5QutsWwSreu4QKBgQDaivFRwFGjt4FZqxpw
+g86QnvGy1R8tsP5v27IT9/zcyd1SHsp4+H2do+C2X18a9iohkX7FD+TNZF/a2Srn
+QuN6Jo7kdiAYw6Tybir7WG6R/tAdcs2G7PILihMY+8AqjNL0CG/bAkpyt+jvu6rp
+6pR1NLBgmbItx9+JfJVdFRtbgwKBgQC+JAGkjafYSPIE4zFlCbNo//0JH9afbO6l
+2evvaE77jt/3XDz94MhKWqjHASKmgqXhx6D2AI4F11IM63hcnVS/ramOFq9qNT++
+RUfiBkdS3GnEvf0ZYCXOBR1hQ9Banx3XncK6ztZGPNNUs+Qe/4GLt9xmJw+CbK4b
+UGjCKthkeQKBgDObY+keNSkoCE+YbCHtvjXAkurcEQTFKxBXHPJCOWAZ+z2NL9rp
+MyCqtkHau6auQryykvEOE8W80jXn2o3J6dXKjSu+CU5PxP/KrDO2akXnmlWZ6pbW
+FUJMVL4tlO93WcluxJs/Kz1z2HnkIzQhxPT4gD4PjVfqPwtEdDEK7u7pAoGBAKaS
+GpJSurjCZqsbcVlHX1Hb+Is2ximSH5tRxtxL7+125APqXVA3zJ8+vt7fwsBGchXS
+cdMw9zpswGerHR1dgzIMbB6bBv8vOPFx9iAvSepwR4RgwEqrrVLHrk5YPz40aMtM
+XJyUuKh9ftGUaoMxaQbCgqVHENI8bWZmUe/P4xRZAoGBAJpaRSBLDMjFNm9tArDD
+X4bnMKixxw/q+3mvQq44gCf5NXqxXuIEoWcT/3dlWuMEOcs83NwjEte+/2Ux6nJT
+TEoOlm0TdCCSsgF9uL0hMHDBmdAF2X1rrkqEhfcPL5D5Wgv8UM3JlPz0x21k9lVm
+Z3qbYBNJW1Z8hxvdR7HCl4TS
 -----END PRIVATE KEY-----

+ 46 - 20
ssl/tolan.crt

@@ -1,22 +1,48 @@
 -----BEGIN CERTIFICATE-----
-MIIDuTCCAqGgAwIBAgIUQ0p5t4Coo16ZcEAkrWIiLAGIrvEwDQYJKoZIhvcNAQEL
-BQAwYjELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
-aWppbmcxDjAMBgNVBAoMBVRvbGFuMQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9j
-YWxob3N0MB4XDTI1MDkyNDA0MTAyMloXDTI2MDkyNDA0MTAyMlowYjELMAkGA1UE
-BhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxDjAMBgNV
-BAoMBVRvbGFuMQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvi1rmUgKuoYP99xPGr/pHUFsOSFl
-YkRGmlQ1nhi240qUTGozeGQSUgk15AX9aYi2rQw4uMxyGpzifCpeh8+6lXXeVzOj
-ssPHec4Egmn5f36qQQmCXB7u2wH80RWjAp8G1LB8NZm1d4I9mMEy8ziR3thH5TN6
-pgP3W9MWyCXDQBey6/utNfVxihsWjftPGBuuur5MkTx7v/g3R3oTXwiWXPfvZKC6
-nsb34DWuQ6ptoQuHExlJZ+8BBbcW0GY31Tm52o3/9oVKU4sL23kt3dn7r5NHxgy1
-5gi/aONdGzI7hv/HbdCT5TLLq/bDxTXGyQ7fu/h760aeAgF08gnSBh3bmwIDAQAB
-o2cwZTBEBgNVHREEPTA7gglsb2NhbGhvc3SCCyoubG9jYWxob3N0ggkxMjcuMC4w
-LjGHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYDVR0OBBYEFHweIp9pGrwnWXiY
-P+gPm2qPXhFNMA0GCSqGSIb3DQEBCwUAA4IBAQAzS0HHPCyIlps4p83n4kK2hB5c
-VbBQoxXhEyUVWekSM5RMELPuLi+tOrLyBbCT9BFjhUS6EVd0YbklT4emGYg6b8i3
-L2uRYJDUTJvxHsMwFUGKPIeY1C3tBncdJSJhVJ2OgU/B0cnWBOEmU4l2UvCuQgq6
-jxcrZipq9M5Gvxs61gGJXie9CR9DQtGAvVpvGf9O1TWadAt/OytE7RYRjiuSIN29
-Ns5t1/exxfHzYKpulsBRIUrRPNXDky0jqTMvCpmuYv+V605MZkSLBZNfhTS92GlD
-V9s3uTM3ciaM2BpPbs65ezUafxxt77j7+ObSv/lK74/R4/j+StfCh1toCQM3
+MIIERTCCAy2gAwIBAgIUDrin0m6DarCjTuNZ/V2mrkOxjXgwDQYJKoZIhvcNAQEL
+BQAwezELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
+aWppbmcxGjAYBgNVBAoMEVRvbGFuIERldmVsb3BtZW50MRYwFAYDVQQLDA1JVCBE
+ZXBhcnRtZW50MRQwEgYDVQQDDAt0b2xhbi5sb2NhbDAeFw0yNTA5MjUwMjI0NDJa
+Fw0yNjA5MjUwMjI0NDJaMHsxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5n
+MRAwDgYDVQQHDAdCZWlqaW5nMRowGAYDVQQKDBFUb2xhbiBEZXZlbG9wbWVudDEW
+MBQGA1UECwwNSVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwLdG9sYW4ubG9jYWwwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiUdwLoR1dTbJNIJkqS/gt88J6
+tKWQpaPrEglHPC3qt9Nr7dGkFB/ON7BVvhG+BNdRD4j2SWKQmeqN6510N/F6Kurj
++ws7rUrDcav3pCq1Lk9xo35ste/gpadZtBL/3fdBOvh/gGd6+tBFoPOf7KxHkQcp
+wfGtUQxx6TbjUfoLclPI7S/THlgOrC8QLFfEiTB8ewnvDSPUjUukkQN8OdBBy6bf
+hn5zjHJoA1HCzH5xdHPFSHAEr1FkXjUYDWitqtlWOzkQ9mGDvu4I9HHNX/vWD2y8
+/hug1WKxD22Dg4E7k+zQ5abYgD1CuAY6DuorAn3lqiz8vwIvVngT0pxeNWzrAgMB
+AAGjgcAwgb0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYB
+BQUHAwEwTgYDVR0RBEcwRYIJbG9jYWxob3N0ggt0b2xhbi5sb2NhbIINKi50b2xh
+bi5sb2NhbIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYcEwKgCOzAdBgNVHQ4EFgQU
+282uJZdqn1Li0xh8Cw8nbSjw9mEwHwYDVR0jBBgwFoAUKjeH+GGTeYHgNcrpTC9j
+jqrPq48wDQYJKoZIhvcNAQELBQADggEBAErkC+SuqKahsWpwdRoAnPflaEqJD+eV
+fG78CJ0fQHDAHdWNn7QqqA2TR6gUdNAIWL4z4vtsp83Kyfm0aM9QN53/gBRkM+Tm
+6VpPmAx/DVNDpADBLWwy/RZ2ytMk5zKO3tKSrrUBE03gmD17ejZemXZC7MeyxqDR
+Sihb4jMJ0IfH5Sa4T/W1zOYqJGztEotdGvq2l5FqT4XK4wREzk+uPy7HWT8B2I1B
+X530jG3Ym3PD3NeYNOjtcXW4dbFakmJFg9FgM+So/psoTTKPrsKO62dNfkDWPeGU
+xqWTusYL7FWknmP66X0TlqDdUfUsEX58YXe8WETFFZZsjAwk4JWbdoQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5zCCAs+gAwIBAgIUA1SCPYPk5q1tX9F+i8MJHapTqx8wDQYJKoZIhvcNAQEL
+BQAwezELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
+aWppbmcxGjAYBgNVBAoMEVRvbGFuIERldmVsb3BtZW50MRYwFAYDVQQLDA1JVCBE
+ZXBhcnRtZW50MRQwEgYDVQQDDAt0b2xhbi5sb2NhbDAeFw0yNTA5MjUwMjI0MjJa
+Fw0zNTA5MjMwMjI0MjJaMHsxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5n
+MRAwDgYDVQQHDAdCZWlqaW5nMRowGAYDVQQKDBFUb2xhbiBEZXZlbG9wbWVudDEW
+MBQGA1UECwwNSVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwLdG9sYW4ubG9jYWwwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvHi1qym1GDyf/IL9EzDgoOvZM
+hP0LWUWj+MWT1RumS5sZ6zW5FNSFLs9s2Z/6sarxALW6iNdsHYp+wqJkYOrLuTdk
+1aqkPdpbE9k2YKNaXkT8F+HI3gPr8K0r6anfpLFF9XxJXB/lfCSy9lDJ1aIo41Ef
+IGxC1UcV5NHyGDC7whD6dvybAtbRKOyZSF3R4sdvVwwOLMgNyWYJMo0o/y19CJol
+92CpmhOdpZFIqvy2x+9DxGYfZQktHzY7oS870oQe+Up0fQZflS3nENyiCjNo2vGq
+ylY/CcygnWXaNU1MkTcbTRTtiuxccJ5PeGz0Y8sjHlKNPiUU0VgPWXpRKbuxAgMB
+AAGjYzBhMB0GA1UdDgQWBBQqN4f4YZN5geA1yulML2OOqs+rjzAfBgNVHSMEGDAW
+gBQqN4f4YZN5geA1yulML2OOqs+rjzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAmYKy26r2/cyipQlw0tz5Ny3XyRoO
+hTlBUjQ2R+/NVK4FHo88yrn7k5v34QEeUtkzbTWlcRiIjas0dX4zYKRUyjBJyYlf
+kzB4UNcd9Oh0FtPG1nfBbE1Ii7wAPBRygTpIysPlDgTOrJMt0de0qdAVMdKwWsO8
+OkIOUkg8ndzpn6uyFdoBOQoTdmrIXnfKyIvAyqH9Yu/cY4aXCBZI0HDS4vRFkxQT
+Mb61U4x/wgHPUIxYLU4J3FUyw28F2RZJnNhKhW0iexq1vqMcg7jlW3K0Ak8j47xk
+W+cHXVYtmNwNf8l0tumZxMDNLjP2HIIh5VJz1mG6CTp1kcnruRJyi/3snA==
 -----END CERTIFICATE-----

+ 26 - 26
ssl/tolan.key

@@ -1,28 +1,28 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+LWuZSAq6hg/3
-3E8av+kdQWw5IWViREaaVDWeGLbjSpRMajN4ZBJSCTXkBf1piLatDDi4zHIanOJ8
-Kl6Hz7qVdd5XM6Oyw8d5zgSCafl/fqpBCYJcHu7bAfzRFaMCnwbUsHw1mbV3gj2Y
-wTLzOJHe2EflM3qmA/db0xbIJcNAF7Lr+6019XGKGxaN+08YG666vkyRPHu/+DdH
-ehNfCJZc9+9koLqexvfgNa5Dqm2hC4cTGUln7wEFtxbQZjfVObnajf/2hUpTiwvb
-eS3d2fuvk0fGDLXmCL9o410bMjuG/8dt0JPlMsur9sPFNcbJDt+7+HvrRp4CAXTy
-CdIGHdubAgMBAAECggEAC0gm18SE0mo350nRWDJl/LTQBpbwbJhv9yF4vs9o22n7
-YInKUjbe5Aak2klFWRiMUbllt1zK22ukKfPaK29gz74FecyCm7fD4nocyAnLtyBc
-ru1b/OVe4Rd97SoTQ162E9xWZK2g13KeqdcIPpKEC3bEKRjZNUu18GNrjhBjbq6p
-mAr6lE67N72ET7w07OKFemMm4xSowNTHLzc5IufTdDa76uGZv4/OU2MewZBP+ZAG
-bV/99CpggHRwTZ4R19VzlPxkITwQ833TIu4OoiPjmQBS5yDqmaN6dihtA7jlv0KX
-+WUxLydTkmdzstWSSqLH2fllzDRczFgIlnv4j/byeQKBgQDzGj+FlyRYxlRBNgcx
-aVkCpzui6f2R5H3Ncc43X7qdRizWc3OMBgMh8ta3ChjoI+6O+vucW7+s8SG/eMGc
-W5uwLr4840/l9ANQymdfcFymeuuiwIPQig+ToOUrBROqCBdaXfDewii0uwVfyx7q
-V2uXQqnoCHaJZWfUJu3eGyHJkwKBgQDIRFudpzmEPS6dbGAxO6dEYpKL61bCxzIG
-c2md2gm4sEeuZRvjiDZF8PaRVlxb3vuhKVne8uZnSSTK6P8VO0Nn241QuXJrZJqq
-BINX6b2HRzTveaJJYFugdo8tnC12bvYsKPu7IMn5JwXzJvIkt/qhFh1k+YIhDdhZ
-8QLL4GTK2QKBgH7o1AKEncfueau1bdngukQFU7+/0c1o8DfhUptb0UsriFrH2Klf
-tiDx6h+SGOCPaYmhAttkllLNayXO/5wFwugd7thQfrR4R4bUs1SCjwumHf9P60Bh
-4rHaC+kCdHqOUMqvQgC4cZGwFKUmn9QMoTFM7vAgAunFU4fQbttFZMUbAoGBAKqL
-HAHboG1utL22xwvmIwFneCjTVuU0QPOhYTXdsh2ncMOgsulG3AEfvLnz818BOwP5
-dOEZtQAMF9r8JA3uk+5j16oA4uF1oQhcI4XANXh4OSoeSQd3mU/C5xcOQuDyiYz9
-sbGfsKAOjVCNv/NpAXQgoxOULy763V1qktkP1IWxAoGAW5zrEcJtGabVogg+7JCf
-ABwTkPP3jDmlvvV1ap0DbREHBJbKOzfV8cMWT+kO810klvPHSGvq33zy/M1ZHNLq
-lLSS2J04hXDIf9nUQHoB7fBKKuAeTlWBk9tT5qEhFOegI+MtIq1qg9ZDvNfBVkWZ
-ErIFP/qkFgucoNWQBUD/bUo=
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCiUdwLoR1dTbJN
+IJkqS/gt88J6tKWQpaPrEglHPC3qt9Nr7dGkFB/ON7BVvhG+BNdRD4j2SWKQmeqN
+6510N/F6Kurj+ws7rUrDcav3pCq1Lk9xo35ste/gpadZtBL/3fdBOvh/gGd6+tBF
+oPOf7KxHkQcpwfGtUQxx6TbjUfoLclPI7S/THlgOrC8QLFfEiTB8ewnvDSPUjUuk
+kQN8OdBBy6bfhn5zjHJoA1HCzH5xdHPFSHAEr1FkXjUYDWitqtlWOzkQ9mGDvu4I
+9HHNX/vWD2y8/hug1WKxD22Dg4E7k+zQ5abYgD1CuAY6DuorAn3lqiz8vwIvVngT
+0pxeNWzrAgMBAAECggEAQwQtJ5gDeaxEe1eEJVRsSPylROgkm03oGXu+3OxZwNjY
+r2zm6ylr4EZZxWzOAngZNdyGHbk6upzxnFq1DXCzgE8L38icYK0xevQJnhrnjxpp
+rm9kGRocydFbAMw6M+2+0e24/1tSXhIDv1uuek7K6llUbccnMvmNe9Jf2QbVh7ep
+aBZJfVE0kaKfY5//4xxVaK8u2pycLpaC7f7iAqYDfvNxV7aiuu145AdqypI05ytt
+Rxqfa182bzCEVHY0b6ZteM4QjCtIImfsfssbL32GM1XK+AuhVXehkJxuL5W4v/QI
+E4sO8s5a35+b6TFXHOkGyAt9VDNIj5QutsWwSreu4QKBgQDaivFRwFGjt4FZqxpw
+g86QnvGy1R8tsP5v27IT9/zcyd1SHsp4+H2do+C2X18a9iohkX7FD+TNZF/a2Srn
+QuN6Jo7kdiAYw6Tybir7WG6R/tAdcs2G7PILihMY+8AqjNL0CG/bAkpyt+jvu6rp
+6pR1NLBgmbItx9+JfJVdFRtbgwKBgQC+JAGkjafYSPIE4zFlCbNo//0JH9afbO6l
+2evvaE77jt/3XDz94MhKWqjHASKmgqXhx6D2AI4F11IM63hcnVS/ramOFq9qNT++
+RUfiBkdS3GnEvf0ZYCXOBR1hQ9Banx3XncK6ztZGPNNUs+Qe/4GLt9xmJw+CbK4b
+UGjCKthkeQKBgDObY+keNSkoCE+YbCHtvjXAkurcEQTFKxBXHPJCOWAZ+z2NL9rp
+MyCqtkHau6auQryykvEOE8W80jXn2o3J6dXKjSu+CU5PxP/KrDO2akXnmlWZ6pbW
+FUJMVL4tlO93WcluxJs/Kz1z2HnkIzQhxPT4gD4PjVfqPwtEdDEK7u7pAoGBAKaS
+GpJSurjCZqsbcVlHX1Hb+Is2ximSH5tRxtxL7+125APqXVA3zJ8+vt7fwsBGchXS
+cdMw9zpswGerHR1dgzIMbB6bBv8vOPFx9iAvSepwR4RgwEqrrVLHrk5YPz40aMtM
+XJyUuKh9ftGUaoMxaQbCgqVHENI8bWZmUe/P4xRZAoGBAJpaRSBLDMjFNm9tArDD
+X4bnMKixxw/q+3mvQq44gCf5NXqxXuIEoWcT/3dlWuMEOcs83NwjEte+/2Ux6nJT
+TEoOlm0TdCCSsgF9uL0hMHDBmdAF2X1rrkqEhfcPL5D5Wgv8UM3JlPz0x21k9lVm
+Z3qbYBNJW1Z8hxvdR7HCl4TS
 -----END PRIVATE KEY-----

+ 234 - 0
移动端录音问题解决方案.md

@@ -0,0 +1,234 @@
+# 移动端录音问题解决方案
+
+## 问题现象
+
+- **现象描述**:移动端浏览器已经授予录音权限,但无法进行录音
+- **控制台错误**:`[useAudio] 尝试停止录音但录音器当前没有在录音`
+
+## 问题分析
+
+### 根本原因
+
+1. **状态同步问题**:
+   - `isCurrentlyRecording()` 方法需要等待 `MediaRecorder.onstart` 事件触发后才返回 `true`
+   - 但组件状态会立即设置为"录音中",导致状态不一致
+   - 移动端浏览器的 `MediaRecorder` 启动可能有延迟或失败
+
+2. **移动端浏览器特殊要求**:
+   - 需要明确的用户交互才能访问麦克风
+   - 对 `MediaRecorder` 的支持程度不同
+   - 异步操作时序问题更明显
+
+### 技术细节
+
+```javascript
+// 问题代码 - 状态检查不准确
+isCurrentlyRecording(): boolean {
+  return this.isRecording && this.mediaRecorder !== null && this.mediaRecorder.state === 'recording';
+}
+
+// this.isRecording 只有在 onstart 事件触发后才为 true
+this.mediaRecorder.onstart = () => {
+  this.isRecording = true;
+};
+```
+
+## 解决方案
+
+### 1. 修复录音状态同步问题
+
+**文件:`frontend/src/utils/audioProcessor.ts`**
+
+- **增加Promise等待机制**:确保录音真正启动后才返回
+- **添加启动超时处理**:防止启动卡死(5秒超时)
+- **改进状态重置**:确保异常情况下状态正确清理
+
+```javascript
+// 关键修复:使用Promise来等待录音真正开始
+const recordingStartPromise = new Promise<void>((resolve, reject) => {
+  const startTimeout = setTimeout(() => {
+    reject(new Error('录音启动超时,可能是浏览器兼容性问题'));
+  }, 5000);
+
+  this.mediaRecorder!.onstart = () => {
+    clearTimeout(startTimeout);
+    this.isRecording = true;
+    resolve();
+  };
+});
+
+// 等待录音真正开始
+await recordingStartPromise;
+```
+
+### 2. 增加移动端兼容性检查
+
+**新增功能:`checkMobileCompatibility()`**
+
+- **设备检测**:识别移动设备类型
+- **浏览器版本检查**:特别是iOS Safari的版本要求
+- **音频格式支持检测**:确保有可用的录音格式
+
+```javascript
+private checkMobileCompatibility(): { supported: boolean; issues: string[] } {
+  const issues: string[] = [];
+  const userAgent = navigator.userAgent.toLowerCase();
+  const isMobile = /android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
+  
+  // iOS Safari 版本检查
+  if (userAgent.includes('safari') && userAgent.includes('mobile')) {
+    const match = userAgent.match(/version\/(\d+)/i);
+    if (match && parseInt(match[1]) < 14) {
+      issues.push('iOS Safari 版本过低,建议升级到14+');
+    }
+  }
+  
+  return { supported: issues.length === 0, issues };
+}
+```
+
+### 3. 优化用户体验
+
+**文件:`frontend/src/components/VoiceRecorder.tsx`**
+
+- **移动端点击模式**:替代复杂的长按/拖拽操作
+- **智能错误提示**:针对移动端的友好错误信息
+- **设备自适应**:自动检测移动设备并调整界面
+
+```javascript
+// 移动端友好的错误提示
+if (isMobileDevice) {
+  if (errorMsg.includes('权限')) {
+    addDebugLog('移动端麦克风权限问题,请检查浏览器设置');
+  } else if (errorMsg.includes('兼容性')) {
+    addDebugLog('当前移动端浏览器可能不完全支持录音,建议使用Chrome或Safari最新版');
+  }
+}
+```
+
+### 4. 改进错误处理
+
+**增强功能:**
+
+- **超时处理**:录音启动和停止的超时保护
+- **状态修复**:自动修复不一致的状态
+- **用户友好提示**:替代技术性错误消息
+
+## 测试验证
+
+### 测试页面
+
+创建了专门的移动端录音测试页面:`测试用例文件夹/mobile_recording_test.html`
+
+**功能特点:**
+- 完整的兼容性检查
+- 详细的调试信息显示
+- 录音功能完整测试流程
+- 支持录音回放验证
+
+### 测试步骤
+
+1. 在移动端浏览器中打开测试页面
+2. 查看兼容性检查结果
+3. 点击录音按钮测试录音功能
+4. 观察调试信息和状态变化
+5. 验证录音结果播放
+
+## 浏览器兼容性
+
+### 推荐浏览器版本
+
+- **iOS Safari**: 14.0+
+- **Chrome for Android**: 70.0+
+- **Firefox Mobile**: 68.0+
+- **Samsung Internet**: 12.0+
+
+### 支持的音频格式
+
+```javascript
+const supportedFormats = [
+  'audio/webm;codecs=opus', // 现代浏览器首选
+  'audio/webm',             // WebM备选
+  'audio/mp4',              // 移动端Safari支持
+  'audio/ogg;codecs=opus'   // Firefox支持
+];
+```
+
+## 部署注意事项
+
+### 1. HTTPS要求
+
+移动端浏览器要求在HTTPS环境下才能访问麦克风:
+
+```javascript
+if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
+  throw new Error('录音功能需要HTTPS环境');
+}
+```
+
+### 2. 用户交互要求
+
+确保录音操作在用户主动交互后执行:
+
+```javascript
+// ✅ 正确:在用户点击事件中启动录音
+button.addEventListener('click', async () => {
+  await startRecording();
+});
+
+// ❌ 错误:页面自动启动录音
+window.addEventListener('load', () => {
+  startRecording(); // 会被浏览器阻止
+});
+```
+
+### 3. 权限引导
+
+提供清晰的权限请求引导:
+
+```javascript
+try {
+  await startRecording();
+} catch (error) {
+  if (error.name === 'NotAllowedError') {
+    showPermissionGuide(); // 显示权限设置指南
+  }
+}
+```
+
+## 监控和调试
+
+### 1. 调试信息
+
+在开发模式下显示详细的调试信息:
+
+```javascript
+if (process.env.NODE_ENV === 'development' && isMobileDevice) {
+  showDebugPanel(); // 显示移动端调试面板
+}
+```
+
+### 2. 错误上报
+
+建议添加错误监控:
+
+```javascript
+window.addEventListener('unhandledrejection', (event) => {
+  if (event.reason?.message?.includes('录音')) {
+    // 上报录音相关错误
+    reportError('mobile_recording_error', event.reason);
+  }
+});
+```
+
+## 总结
+
+本次修复主要解决了以下问题:
+
+1. ✅ **状态同步问题**:确保录音状态的准确性
+2. ✅ **移动端兼容性**:增加设备检测和格式支持
+3. ✅ **用户体验优化**:简化移动端操作流程
+4. ✅ **错误处理增强**:提供友好的错误提示
+5. ✅ **测试验证工具**:提供完整的测试环境
+
+经过修复,移动端录音功能现在应该能够正常工作,用户可以在支持的移动端浏览器中顺利进行录音操作。 

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů