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 | import fs from 'fs' import path from 'path' import { type NextRequest, NextResponse } from 'next/server' import { withAuth } from '@/lib/auth/withAuth' const BLOG_IMAGES_DIR = path.join(process.cwd(), 'public', 'blog') /** * POST /api/admin/blog-images/capture-storybook * * Captures a screenshot of a Storybook story using Puppeteer. * Requires Storybook running locally on port 6006. */ export const POST = withAuth( async (request: NextRequest) => { let body: { slug: string; storyId: string; width?: number; height?: number } try { body = await request.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) } const { slug, storyId, width = 1200, height = 500 } = body if (!slug || !storyId) { return NextResponse.json({ error: 'slug and storyId are required' }, { status: 400 }) } let puppeteer: typeof import('puppeteer') try { puppeteer = await import('puppeteer') } catch { return NextResponse.json( { error: 'puppeteer is not installed. Run: pnpm add -D puppeteer' }, { status: 500 } ) } let browser try { browser = await puppeteer.default.launch({ headless: true, protocolTimeout: 120000, args: ['--no-sandbox', '--disable-setuid-sandbox'], }) const page = await browser.newPage() await page.setViewport({ width, height }) const storybookUrl = `http://localhost:6006/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story` await page.goto(storybookUrl, { waitUntil: 'domcontentloaded', timeout: 30000 }) // Wait for story content to render (Storybook HMR keeps connections open, so networkidle0 times out) await new Promise((r) => setTimeout(r, 5000)) // Ensure output directory exists if (!fs.existsSync(BLOG_IMAGES_DIR)) { fs.mkdirSync(BLOG_IMAGES_DIR, { recursive: true }) } const outputPath = path.join(BLOG_IMAGES_DIR, `${slug}.png`) await page.screenshot({ path: outputPath, type: 'png' }) const stats = fs.statSync(outputPath) return NextResponse.json({ success: true, path: `/blog/${slug}.png`, sizeBytes: stats.size, }) } catch (err) { return NextResponse.json( { error: err instanceof Error ? err.message : 'Screenshot capture failed' }, { status: 500 } ) } finally { if (browser) { await browser.close() } } }, { role: 'admin' } ) |