import { promises as fs } from "node:fs"; import path from "node:path"; import { Readable } from "node:stream"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; const dataRoot = path.resolve(process.cwd(), "..", "..", "data"); const mimeTypes: Record = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg", ".flac": "audio/flac", ".webm": "audio/webm", }; function hasCode(error: unknown, code: string): boolean { return error instanceof Error && "code" in error && error.code === code; } function within(root: string, candidate: string): boolean { const relative = path.relative(root, candidate); return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative); } function failure(message: string, status: number, head: boolean): Response { return new Response(head ? null : message, { status, headers: { "Cache-Control": "no-store", "Content-Type": "text/plain; charset=utf-8" }, }); } function parseRange(value: string, size: number): { start: number; end: number } | null { const match = /^bytes=(\d*)-(\d*)$/.exec(value.trim()); if (!match || (!match[1] && !match[2]) || size === 0) return null; if (!match[1]) { const length = Number(match[2]); if (!Number.isSafeInteger(length) || length <= 0) return null; return { start: Math.max(0, size - length), end: size - 1 }; } const start = Number(match[1]); const requestedEnd = match[2] ? Number(match[2]) : size - 1; if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd) || start >= size || requestedEnd < start) return null; return { start, end: Math.min(requestedEnd, size - 1) }; } async function serve(request: Request, head: boolean): Promise { const relativePath = new URL(request.url).searchParams.get("path"); if (!relativePath || relativePath.includes("\0") || path.isAbsolute(relativePath)) { return failure("录音路径无效。", 400, head); } const candidate = path.resolve(dataRoot, relativePath); if (!within(dataRoot, candidate)) return failure("不能访问 data 目录之外的文件。", 403, head); const extension = path.extname(candidate).toLowerCase(); if (!mimeTypes[extension]) return failure("不支持该录音格式。", 415, head); let handle; try { const [realRoot, realCandidate] = await Promise.all([fs.realpath(dataRoot), fs.realpath(candidate)]); if (!within(realRoot, realCandidate) || !mimeTypes[path.extname(realCandidate).toLowerCase()]) { return failure("不能访问 data 目录之外的文件。", 403, head); } handle = await fs.open(realCandidate, "r"); const stat = await handle.stat(); if (!stat.isFile()) { await handle.close(); return failure("录音不存在。", 404, head); } const headers = new Headers({ "Accept-Ranges": "bytes", "Cache-Control": "no-store", "Content-Type": mimeTypes[path.extname(realCandidate).toLowerCase()], "X-Content-Type-Options": "nosniff", }); const rangeHeader = request.headers.get("range"); const range = rangeHeader ? parseRange(rangeHeader, stat.size) : null; if (rangeHeader && !range) { await handle.close(); headers.set("Content-Range", `bytes */${stat.size}`); headers.set("Content-Length", "0"); return new Response(null, { status: 416, headers }); } const start = range?.start ?? 0; const end = range?.end ?? stat.size - 1; const status = range ? 206 : 200; headers.set("Content-Length", String(Math.max(0, end - start + 1))); if (range) headers.set("Content-Range", `bytes ${start}-${end}/${stat.size}`); if (head || stat.size === 0) { await handle.close(); return new Response(null, { status, headers }); } const stream = handle.createReadStream({ start, end, autoClose: true }); return new Response(Readable.toWeb(stream) as ReadableStream, { status, headers }); } catch (error) { await handle?.close().catch(() => undefined); if (hasCode(error, "ENOENT") || hasCode(error, "ENOTDIR")) return failure("录音不存在。", 404, head); console.error("EOT audio request failed", error); return failure("无法读取录音。", 500, head); } } export async function GET(request: Request): Promise { return serve(request, false); } export async function HEAD(request: Request): Promise { return serve(request, true); }