Download src/server/staticFriend.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 4.06 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/staticFriend.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/staticFriend.ts
-
curl -L -o staticFriend.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/staticFriend.ts
4.06 kB
| /** | |
| * Static file serving for the Friend VRM frontend. | |
| * | |
| * Serves the built Vite/React frontend from src/components/friend/frontend/dist/ at /friend/*. | |
| * All asset files (VRM, FBX, VRMA, VMD, MP3) are in the dist root and served | |
| * under /friend/<filename>. | |
| */ | |
| import fs from 'node:fs/promises' | |
| import path from 'node:path' | |
| import { fileURLToPath } from 'node:url' | |
| const CACHEABLE_RE = /^\/friend\/assets\// | |
| const MIME_TYPES: Record<string, string> = { | |
| '.vrm': 'application/octet-stream', | |
| '.fbx': 'application/octet-stream', | |
| '.vrma': 'application/octet-stream', | |
| '.vmd': 'application/octet-stream', | |
| '.mp3': 'audio/mpeg', | |
| '.css': 'text/css; charset=utf-8', | |
| '.js': 'text/javascript; charset=utf-8', | |
| '.mjs': 'text/javascript; charset=utf-8', | |
| '.html': 'text/html; charset=utf-8', | |
| '.json': 'application/json; charset=utf-8', | |
| '.png': 'image/png', | |
| '.svg': 'image/svg+xml', | |
| '.ico': 'image/x-icon', | |
| '.woff': 'font/woff', | |
| '.woff2': 'font/woff2', | |
| '.onnx': 'application/octet-stream', | |
| '.wasm': 'application/wasm', | |
| } | |
| export async function handleFriendStaticRequest(req: Request, url: URL): Promise<Response | null> { | |
| if (req.method !== 'GET' && req.method !== 'HEAD') { | |
| return null | |
| } | |
| // Only handle /friend/* paths | |
| if (!url.pathname.startsWith('/friend/') && url.pathname !== '/friend') { | |
| return null | |
| } | |
| const distDir = await resolveFriendDistDir() | |
| if (!distDir) { | |
| return null | |
| } | |
| // Map /friend/* to dist root | |
| // /friend/ → index.html | |
| // /friend/model1.vrm → dist/model1.vrm | |
| // /friend/assets/foo.js → dist/assets/foo.js | |
| const relativePath = url.pathname.replace(/^\/friend\/?/, '') || 'index.html' | |
| const filePath = await resolveFriendFilePath(distDir, relativePath) | |
| if (!filePath) { | |
| return null | |
| } | |
| const headers = new Headers({ | |
| 'Content-Type': contentTypeForPath(filePath), | |
| 'Cache-Control': CACHEABLE_RE.test(url.pathname) | |
| ? 'public, max-age=31536000, immutable' | |
| : 'no-store', | |
| }) | |
| // Enable cross-origin isolation for SharedArrayBuffer support, | |
| // needed by onnxruntime-web WASM threading (used by @ricky0123/vad-web). | |
| if (relativePath === 'index.html') { | |
| headers.set('Cross-Origin-Opener-Policy', 'same-origin') | |
| headers.set('Cross-Origin-Embedder-Policy', 'require-corp') | |
| } | |
| if (req.method === 'HEAD') { | |
| const stat = await fs.stat(filePath) | |
| headers.set('Content-Length', String(stat.size)) | |
| return new Response(null, { status: 200, headers }) | |
| } | |
| return new Response(Bun.file(filePath), { status: 200, headers }) | |
| } | |
| async function resolveFriendDistDir(): Promise<string | null> { | |
| // Method 1: relative to source file (works in dev mode / server subprocess) | |
| const _srcDir = path.dirname(fileURLToPath(import.meta.url)) | |
| const candidate1 = path.resolve(_srcDir, '..', '..', 'src', 'components', 'friend', 'frontend', 'dist') | |
| try { | |
| const stat = await fs.stat(path.join(candidate1, 'index.html')) | |
| if (stat.isFile()) return candidate1 | |
| } catch { | |
| // Not found | |
| } | |
| // Method 2: relative to cwd (works in compiled binary where import.meta.url is virtual) | |
| const candidate2 = path.resolve(process.cwd(), 'src', 'components', 'friend', 'frontend', 'dist') | |
| try { | |
| const stat = await fs.stat(path.join(candidate2, 'index.html')) | |
| if (stat.isFile()) return candidate2 | |
| } catch { | |
| // Not found | |
| } | |
| return null | |
| } | |
| async function resolveFriendFilePath(distDir: string, relativePath: string): Promise<string | null> { | |
| if (!relativePath) return null | |
| const decoded = decodeURIComponent(relativePath) | |
| const candidate = path.resolve(distDir, decoded) | |
| const relativeToRoot = path.relative(distDir, candidate) | |
| if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) { | |
| return null | |
| } | |
| try { | |
| const stat = await fs.stat(candidate) | |
| return stat.isFile() ? candidate : null | |
| } catch { | |
| return null | |
| } | |
| } | |
| function contentTypeForPath(filePath: string): string { | |
| return MIME_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream' | |
| } | |