Files
openmaic/OpenMAIC/app/api/transcription/route.ts
2026-08-16 14:58:47 +08:00

76 lines
2.8 KiB
TypeScript

import { NextRequest } from 'next/server';
import { transcribeAudio } from '@/lib/audio/asr-providers';
import {
isServerConfiguredProvider,
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';
import { providerAccessApiError } from '@/lib/server/provider-access-response';
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;
// Managed providers are admin-owned: ignore any client-sent key/baseUrl.
const managed = isServerConfiguredProvider('asr', effectiveProviderId);
const clientBaseUrl = managed ? undefined : 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: resolveASRApiKey(effectiveProviderId, managed ? undefined : apiKey || undefined),
baseUrl: resolveASRBaseUrl(effectiveProviderId, clientBaseUrl),
};
// Transcribe using the provider system
const result = await transcribeAudio(config, audioFile);
return apiSuccess({ text: result.text });
} catch (error) {
log.error(
`Transcription failed [provider=${resolvedProviderId ?? 'unknown'}, model=${resolvedModelId ?? 'default'}]:`,
error,
);
const providerAccessError = providerAccessApiError(error);
if (providerAccessError) return providerAccessError;
return apiError(
'TRANSCRIPTION_FAILED',
500,
'Transcription failed',
error instanceof Error ? error.message : 'Unknown error',
);
}
}