From 083ede8b6da3953549566088c4758c304485b2c0 Mon Sep 17 00:00:00 2001 From: cfy666 <3087823110@qq.com> Date: Sun, 14 Jun 2026 21:24:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=83=A8=E7=BD=B2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.gitignore | 4 +- frontend/.vite/deps/_metadata.json | 8 +++ frontend/.vite/deps/package.json | 3 ++ frontend/nginx.conf | 36 ++++++++++++- .../src/components/EdgeProcessor/index.tsx | 5 +- frontend/vite.config.ts | 50 ++++++++++++++----- 6 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 frontend/.vite/deps/_metadata.json create mode 100644 frontend/.vite/deps/package.json diff --git a/frontend/.gitignore b/frontend/.gitignore index f3c1586..57234d6 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -12,9 +12,11 @@ dist dist-ssr *.local -# VAD 静态资源(从 node_modules 自动复制) +# VAD / ONNX Runtime 静态资源(从 node_modules 自动复制) public/silero_vad_*.onnx public/vad.worklet.bundle.min.js +public/ort-wasm*.mjs +public/ort-wasm*.wasm # Editor directories and files .vscode/* diff --git a/frontend/.vite/deps/_metadata.json b/frontend/.vite/deps/_metadata.json new file mode 100644 index 0000000..b8c588d --- /dev/null +++ b/frontend/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "949ba93d", + "configHash": "caf561c6", + "lockfileHash": "76f9e0b5", + "browserHash": "26e6c47e", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/frontend/.vite/deps/package.json b/frontend/.vite/deps/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/frontend/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 1490d6a..d5f4c57 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -5,6 +5,10 @@ server { root /usr/share/nginx/html; index index.html; + # 跨域隔离头(SharedArrayBuffer / WASM 线程模式所需) + add_header Cross-Origin-Opener-Policy "same-origin"; + add_header Cross-Origin-Embedder-Policy "require-corp"; + # 前端静态资源 location / { try_files $uri $uri/ /index.html; @@ -33,7 +37,35 @@ server { proxy_send_timeout 86400s; } - # 静态资源缓存 + # ONNX Runtime WASM 模块(.mjs 必须是 JS MIME 类型) + location ~* \.mjs$ { + types { } + default_type application/javascript; + expires 7d; + add_header Cache-Control "public, immutable"; + add_header Cross-Origin-Opener-Policy "same-origin"; + add_header Cross-Origin-Embedder-Policy "require-corp"; + } + + # WebAssembly 二进制文件 + location ~* \.wasm$ { + types { } + default_type application/wasm; + expires 7d; + add_header Cache-Control "public, immutable"; + add_header Cross-Origin-Opener-Policy "same-origin"; + add_header Cross-Origin-Embedder-Policy "require-corp"; + } + + # ONNX 模型文件 + location ~* \.onnx$ { + types { } + default_type application/octet-stream; + expires 7d; + add_header Cache-Control "public, immutable"; + } + + # 其他静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 7d; add_header Cache-Control "public, immutable"; @@ -41,6 +73,6 @@ server { # Gzip 压缩 gzip on; - gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/wasm; gzip_min_length 256; } diff --git a/frontend/src/components/EdgeProcessor/index.tsx b/frontend/src/components/EdgeProcessor/index.tsx index b03e3f9..23f8e90 100644 --- a/frontend/src/components/EdgeProcessor/index.tsx +++ b/frontend/src/components/EdgeProcessor/index.tsx @@ -50,8 +50,9 @@ export function useVAD(options?: VADOptions) { getStream: () => Promise.resolve(stream), startOnLoad: true, model: "legacy", - // 指向 node_modules 中的 WASM 文件(由 Vite 中间件提供) - onnxWASMBasePath: "/node_modules/onnxruntime-web/dist/", + // WASM 和模型文件均由 Vite 插件复制到 public/ (即 dist 根目录) + onnxWASMBasePath: "/", + modelURL: "/silero_vad_legacy.onnx", onSpeechStart: () => { setIsSpeaking(true); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 3f3146b..a6f4064 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -7,13 +7,34 @@ const ortDistDir = path.resolve(__dirname, 'node_modules/onnxruntime-web/dist') const vadDistDir = path.resolve(__dirname, 'node_modules/@ricky0123/vad-web/dist') const publicDir = path.resolve(__dirname, 'public') -// 需要复制到 public 的静态资源 -const staticFiles = [ +// 需要复制到 public 的 VAD 模型/工作线程 +const vadFiles = [ { src: vadDistDir, name: 'silero_vad_legacy.onnx' }, { src: vadDistDir, name: 'silero_vad_v5.onnx' }, { src: vadDistDir, name: 'vad.worklet.bundle.min.js' }, ] +// 需要复制到 public 的 ONNX Runtime WASM 文件(生产环境需要) +const ortFiles = [ + { src: ortDistDir, name: 'ort-wasm-simd-threaded.mjs' }, + { src: ortDistDir, name: 'ort-wasm-simd-threaded.wasm' }, + { src: ortDistDir, name: 'ort-wasm-simd.mjs' }, + { src: ortDistDir, name: 'ort-wasm-simd.wasm' }, + { src: ortDistDir, name: 'ort-wasm.mjs' }, + { src: ortDistDir, name: 'ort-wasm.wasm' }, +] + +/** 把所需文件复制到 public 目录(不存在则复制) */ +function copyAssetsToPublic() { + for (const { src, name } of [...vadFiles, ...ortFiles]) { + const srcFile = path.join(src, name) + const destFile = path.join(publicDir, name) + if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) { + fs.copyFileSync(srcFile, destFile) + } + } +} + export default defineConfig({ server: { proxy: { @@ -30,21 +51,15 @@ export default defineConfig({ react(), { name: 'serve-vad-assets', - // 1. 启动时将 VAD 模型/工作线程复制到 public + // 1. 启动时将 VAD 模型 + ORT WASM 复制到 public configResolved() { - for (const { src, name } of staticFiles) { - const dest = path.join(publicDir, name) - if (!fs.existsSync(dest)) { - fs.copyFileSync(path.join(src, name), dest) - } - } + copyAssetsToPublic() }, - // 2. 中间件拦截所有 WASM 相关请求,从原始路径提供文件 + // 2. 开发模式中间件:拦截 WASM 相关请求,确保 MIME 类型正确 configureServer(server) { server.middlewares.use((req, res, next) => { const url = req.url?.split('?')[0] ?? '' - // 匹配任意路径下的 ort-wasm 文件(包括 .vite/deps/ 和根路径) - const wasmMatch = url.match(/\/(ort-wasm-simd-threaded\.(mjs|wasm))$/) + const wasmMatch = url.match(/\/(ort-wasm[\w-]*\.(mjs|wasm))$/) if (wasmMatch) { const fileName = wasmMatch[1] const filePath = path.join(ortDistDir, fileName) @@ -58,6 +73,17 @@ export default defineConfig({ next() }) }, + // 3. 构建阶段确保所有资源都在 dist 中 + writeBundle() { + const distDir = path.resolve(__dirname, 'dist') + for (const { src, name } of [...vadFiles, ...ortFiles]) { + const srcFile = path.join(src, name) + const destFile = path.join(distDir, name) + if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) { + fs.copyFileSync(srcFile, destFile) + } + } + }, }, ], }) \ No newline at end of file -- 2.49.1