import { NextRequest } from 'next/server'; import { transcribeAudio } from '@/lib/audio/asr-providers'; import { resolveASRApiKey, resolveASRBaseUrl } from '@/lib/server/provider-config'; import type { ASRProviderId } from '@/lib/audio/types'; import { createLogger } from '@/lib/logger'; import { apiError, apiSuccess } from '@/lib/server/api-response'; import { validateUrlForSSRF } from '@/lib/server/ssrf-guard'; const log = createLogger('Transcription'); export const maxDuration = 60; export async function POST(req: NextRequest) { let resolvedProviderId: string | undefined; let resolvedModelId: string | undefined; try { const formData = await req.formData(); const audioFile = formData.get('audio') as File; const providerId = formData.get('providerId') as ASRProviderId | null; const modelId = formData.get('modelId') as string | null; const language = formData.get('language') as string | null; const apiKey = formData.get('apiKey') as string | null; const baseUrl = formData.get('baseUrl') as string | null; if (!audioFile) { return apiError('MISSING_REQUIRED_FIELD', 400, 'Audio file is required'); } // providerId is required from the client — no server-side store to fall back to const effectiveProviderId = providerId || ('openai-whisper' as ASRProviderId); resolvedProviderId = effectiveProviderId; resolvedModelId = modelId ?? undefined; const clientBaseUrl = baseUrl || undefined; if (clientBaseUrl && process.env.NODE_ENV === 'production') { const ssrfError = await validateUrlForSSRF(clientBaseUrl); if (ssrfError) { return apiError('INVALID_URL', 403, ssrfError); } } const config = { providerId: effectiveProviderId, modelId: modelId || undefined, language: language || 'auto', apiKey: clientBaseUrl ? apiKey || '' : resolveASRApiKey(effectiveProviderId, apiKey || undefined), baseUrl: clientBaseUrl ? clientBaseUrl : resolveASRBaseUrl(effectiveProviderId, baseUrl || undefined), }; // Convert audio file to buffer const arrayBuffer = await audioFile.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); // Transcribe using the provider system const result = await transcribeAudio(config, buffer); return apiSuccess({ text: result.text }); } catch (error) { log.error( `Transcription failed [provider=${resolvedProviderId ?? 'unknown'}, model=${resolvedModelId ?? 'default'}]:`, error, ); return apiError( 'TRANSCRIPTION_FAILED', 500, 'Transcription failed', error instanceof Error ? error.message : 'Unknown error', ); } }