All files / web/src/app/api/admin/blog-images/generate route.ts

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

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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                                                                                                                                                                                                             
import { type NextRequest, NextResponse } from 'next/server'
import { getAllPostsMetadata } from '@/lib/blog'
import { startBlogImageGeneration } from '@/lib/tasks/blog-image-generate'
import type { BlogImageGenerateInput } from '@/lib/tasks/blog-image-generate'
import { withAuth } from '@/lib/auth/withAuth'

const VALID_PROVIDERS = ['gemini', 'openai'] as const

/**
 * POST /api/admin/blog-images/generate
 *
 * Starts a background task to generate blog hero images.
 * Body: { provider, model, fallbackProvider?, fallbackModel?, targets?, forceRegenerate? }
 * Response: { taskId }
 */
export const POST = withAuth(
  async (request: NextRequest, { userId }) => {
    try {
      const body = await request.json()
      const { provider, model, fallbackProvider, fallbackModel, targets, forceRegenerate } = body

      // Validate provider
      if (!provider || !VALID_PROVIDERS.includes(provider)) {
        return NextResponse.json(
          { error: `provider must be one of: ${VALID_PROVIDERS.join(', ')}` },
          { status: 400 }
        )
      }

      // Validate model
      if (typeof model !== 'string' || model.trim().length === 0) {
        return NextResponse.json({ error: 'model must be a non-empty string' }, { status: 400 })
      }

      // Validate fallback if provided (both or neither)
      if (fallbackProvider || fallbackModel) {
        if (!fallbackProvider || !VALID_PROVIDERS.includes(fallbackProvider)) {
          return NextResponse.json(
            { error: `fallbackProvider must be one of: ${VALID_PROVIDERS.join(', ')}` },
            { status: 400 }
          )
        }
        if (typeof fallbackModel !== 'string' || fallbackModel.trim().length === 0) {
          return NextResponse.json(
            { error: 'fallbackModel must be a non-empty string when fallbackProvider is set' },
            { status: 400 }
          )
        }
      }

      // Build targets list
      let resolvedTargets: BlogImageGenerateInput['targets']

      if (targets && Array.isArray(targets) && targets.length > 0) {
        // Validate each target has slug and prompt
        for (const t of targets) {
          if (typeof t.slug !== 'string' || !t.slug) {
            return NextResponse.json({ error: 'Each target must have a slug' }, { status: 400 })
          }
          if (typeof t.prompt !== 'string' || !t.prompt) {
            return NextResponse.json({ error: 'Each target must have a prompt' }, { status: 400 })
          }
        }
        resolvedTargets = targets
      } else {
        // Default: all posts with heroPrompt
        const posts = await getAllPostsMetadata()
        resolvedTargets = posts
          .filter((p) => p.heroPrompt)
          .map((p) => ({ slug: p.slug, prompt: p.heroPrompt! }))

        if (resolvedTargets.length === 0) {
          return NextResponse.json(
            { error: 'No blog posts have a heroPrompt in their frontmatter' },
            { status: 400 }
          )
        }
      }

      const input: BlogImageGenerateInput = {
        provider,
        model: model.trim(),
        targets: resolvedTargets,
        forceRegenerate: !!forceRegenerate,
        _userId: userId,
      }

      if (fallbackProvider && fallbackModel) {
        input.fallbackProvider = fallbackProvider
        input.fallbackModel = fallbackModel.trim()
      }

      const taskId = await startBlogImageGeneration(input)

      return NextResponse.json({ taskId })
    } catch (error) {
      console.error('Error starting blog image generation:', error)
      return NextResponse.json({ error: 'Failed to start blog image generation' }, { status: 500 })
    }
  },
  { role: 'admin' }
)