fix(dreams): proxy tolerates octet-stream + invalid form bodies

Two adjustments after end-to-end testing the voice flow against the
self-hosted mana-stt on the GPU server:

- Browser MediaRecorder always sets a clean audio/* mime type, but
  CLI clients (curl, scripts) often send application/octet-stream
  for audio files. Empty mime types should also pass through. Tighten
  rejection to clearly non-audio types only.
- await request.formData() throws on a missing/invalid body which
  surfaces as a SvelteKit 500 with "Internal Error". Catch it and
  return a 400 with a useful message instead.

Verified end-to-end with WhisperX large-v3-turbo: m4a (Anna voice)
transcribed in ~2.4s through the proxy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-07 15:48:36 +02:00
parent 984c516788
commit 63a6f62529

View file

@ -13,9 +13,14 @@ import { error, json } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import type { RequestHandler } from './$types';
const ALLOWED_MIME_PREFIXES = ['audio/'];
const MAX_BYTES = 25 * 1024 * 1024; // 25 MB
function isAcceptableType(mime: string): boolean {
if (!mime) return true; // tolerate missing type — let upstream validate
if (mime === 'application/octet-stream') return true;
return mime.startsWith('audio/') || mime.startsWith('video/'); // m4a often reports video/mp4
}
export const POST: RequestHandler = async ({ request }) => {
const sttUrl = env.MANA_STT_URL;
const apiKey = env.MANA_STT_API_KEY;
@ -24,7 +29,12 @@ export const POST: RequestHandler = async ({ request }) => {
throw error(503, 'mana-stt is not configured (MANA_STT_URL missing)');
}
const incoming = await request.formData();
let incoming: FormData;
try {
incoming = await request.formData();
} catch {
throw error(400, 'Expected multipart/form-data with a file field');
}
const file = incoming.get('file');
const language = (incoming.get('language') as string | null) ?? null;
@ -37,7 +47,7 @@ export const POST: RequestHandler = async ({ request }) => {
if (file.size > MAX_BYTES) {
throw error(413, `Audio too large (max ${MAX_BYTES / 1024 / 1024} MB)`);
}
if (file.type && !ALLOWED_MIME_PREFIXES.some((p) => file.type.startsWith(p))) {
if (!isAcceptableType(file.type)) {
throw error(415, `Unsupported audio type: ${file.type}`);
}