All files / web/src/app/api/admin/audio/preview route.ts

0% Statements 0/78
0% Branches 0/1
0% Functions 0/1
0% Lines 0/78

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79                                                                                                                                                             
import { type NextRequest, NextResponse } from 'next/server'
import { withAuth } from '@/lib/auth/withAuth'
import { recordTtsUsage } from '@/lib/ai-usage/helpers'
import { AiFeature } from '@/lib/ai-usage/features'

/**
 * POST /api/admin/audio/preview
 *
 * Ephemeral streaming proxy — calls OpenAI TTS and pipes the audio back.
 * Nothing is saved to disk or DB.
 */
export const POST = withAuth(
  async (request: NextRequest, { userId }) => {
    const apiKey = process.env.LLM_OPENAI_API_KEY || process.env.OPENAI_API_KEY
    if (!apiKey) {
      return NextResponse.json({ error: 'LLM_OPENAI_API_KEY is not configured' }, { status: 500 })
    }

    let body: { voice?: string; text?: string; tone?: string }
    try {
      body = await request.json()
    } catch {
      return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })
    }

    const { voice, text, tone } = body
    if (!voice || !text) {
      return NextResponse.json({ error: 'voice and text are required' }, { status: 400 })
    }

    try {
      const response = await fetch('https://api.openai.com/v1/audio/speech', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: 'gpt-4o-mini-tts',
          voice,
          input: text,
          instructions: tone || undefined,
          response_format: 'mp3',
        }),
      })

      if (!response.ok) {
        const errText = await response.text()
        return NextResponse.json(
          { error: `OpenAI API error: HTTP ${response.status}: ${errText}` },
          { status: response.status }
        )
      }

      if (!response.body) {
        return NextResponse.json({ error: 'No response body from OpenAI' }, { status: 502 })
      }

      recordTtsUsage(text, 'gpt-4o-mini-tts', { userId, feature: AiFeature.TTS_PREVIEW })

      // Stream the mp3 back directly
      return new NextResponse(response.body as ReadableStream, {
        status: 200,
        headers: {
          'Content-Type': 'audio/mpeg',
          'Transfer-Encoding': 'chunked',
          'Cache-Control': 'no-store',
        },
      })
    } catch (err) {
      return NextResponse.json(
        { error: err instanceof Error ? err.message : 'Unknown error' },
        { status: 500 }
      )
    }
  },
  { role: 'admin' }
)