route.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { NextRequest } from 'next/server';
  2. import { createLogger } from '@/lib/logger';
  3. import { validateUrlForSSRF } from '@/lib/server/ssrf-guard';
  4. import { apiError, apiSuccess } from '@/lib/server/api-response';
  5. const log = createLogger('Azure Voices');
  6. export const maxDuration = 30;
  7. /**
  8. * Azure TTS Voice List API
  9. * Fetches available voices from Azure Speech Services
  10. */
  11. export async function POST(req: NextRequest) {
  12. let baseUrl: string | undefined;
  13. try {
  14. const body = await req.json();
  15. const { apiKey } = body;
  16. baseUrl = body.baseUrl;
  17. if (!apiKey) {
  18. return apiError('MISSING_API_KEY', 400, 'API Key is required');
  19. }
  20. if (!baseUrl) {
  21. return apiError('MISSING_REQUIRED_FIELD', 400, 'Base URL is required');
  22. }
  23. // Validate baseUrl against SSRF
  24. const ssrfError = await validateUrlForSSRF(baseUrl);
  25. if (ssrfError) {
  26. return apiError('INVALID_URL', 403, ssrfError);
  27. }
  28. // Call Azure voices list endpoint; disable redirect following to prevent SSRF via redirect
  29. const response = await fetch(`${baseUrl}/cognitiveservices/voices/list`, {
  30. method: 'GET',
  31. headers: {
  32. 'Ocp-Apim-Subscription-Key': apiKey,
  33. },
  34. redirect: 'manual',
  35. });
  36. if (response.status >= 300 && response.status < 400) {
  37. return apiError('REDIRECT_NOT_ALLOWED', 403, 'Redirects are not allowed');
  38. }
  39. if (!response.ok) {
  40. const errorText = await response.text();
  41. return apiError(
  42. 'UPSTREAM_ERROR',
  43. response.status,
  44. 'Failed to fetch voices from Azure',
  45. errorText || response.statusText,
  46. );
  47. }
  48. const voices = await response.json();
  49. return apiSuccess({ voices });
  50. } catch (error) {
  51. log.error(`Azure voices fetch failed [baseUrl="${baseUrl ?? 'unknown'}"]:`, error);
  52. return apiError(
  53. 'INTERNAL_ERROR',
  54. 500,
  55. 'Failed to fetch voices',
  56. error instanceof Error ? error.message : 'Unknown error',
  57. );
  58. }
  59. }