6.1 KiB
6.1 KiB
🚀 Quick Start: @vercel/og + Quoter
⚡ 5-минутная настройка
1. Установка зависимостей
npm install @vercel/og
2. Создание API endpoint
// pages/api/og.tsx (Next.js)
import { ImageResponse } from '@vercel/og'
export const config = { runtime: 'edge' }
export default function handler(req) {
const { searchParams } = new URL(req.url)
const title = searchParams.get('title') ?? 'Hello World'
return new ImageResponse(
(
<div style={{
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 60,
fontWeight: 700,
}}>
{title}
</div>
),
{ width: 1200, height: 630 }
)
}
3. Интеграция с Quoter
// utils/quoter.ts
export async function uploadToQuoter(imageBuffer: Buffer, filename: string, token: string) {
const formData = new FormData()
const blob = new Blob([imageBuffer], { type: 'image/png' })
formData.append('file', blob, filename)
const response = await fetch('https://quoter.staging.discours.io/', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData,
})
return response.text() // URL файла
}
4. Использование
// Генерация OG изображения
const ogResponse = await fetch('/api/og?title=My%20Amazing%20Post')
const imageBuffer = Buffer.from(await ogResponse.arrayBuffer())
// Загрузка в Quoter
const quoterUrl = await uploadToQuoter(imageBuffer, 'og-image.png', userToken)
// Использование в meta tags
<meta property="og:image" content={quoterUrl} />
🎨 Расширенный пример с изображением фона
// pages/api/og-advanced.tsx
import { ImageResponse } from '@vercel/og'
export default async function handler(req) {
const { searchParams } = new URL(req.url)
const title = searchParams.get('title')
const imageUrl = searchParams.get('image') // Quoter URL
// Загружаем изображение из Quoter
let backgroundImage = null
if (imageUrl) {
const imageResponse = await fetch(imageUrl)
const buffer = await imageResponse.arrayBuffer()
backgroundImage = `data:image/jpeg;base64,${Buffer.from(buffer).toString('base64')}`
}
return new ImageResponse(
(
<div style={{
background: backgroundImage ? `url(${backgroundImage})` : '#667eea',
backgroundSize: 'cover',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}>
{/* Затемнение для читаемости */}
<div style={{
position: 'absolute',
inset: 0,
background: 'rgba(0,0,0,0.4)',
}} />
{/* Заголовок */}
<h1 style={{
fontSize: 72,
fontWeight: 'bold',
color: 'white',
textAlign: 'center',
textShadow: '2px 2px 4px rgba(0,0,0,0.8)',
zIndex: 1,
}}>
{title}
</h1>
</div>
),
{ width: 1200, height: 630 }
)
}
📱 React Hook для удобства
// hooks/useOgImage.ts
import { useState } from 'react'
export function useOgImage() {
const [loading, setLoading] = useState(false)
const generateAndUpload = async (title: string, backgroundImage?: string) => {
setLoading(true)
try {
// Генерируем OG изображение
const ogUrl = `/api/og?title=${encodeURIComponent(title)}${
backgroundImage ? `&image=${encodeURIComponent(backgroundImage)}` : ''
}`
const response = await fetch(ogUrl)
const buffer = await response.arrayBuffer()
// Загружаем в Quoter
const formData = new FormData()
const blob = new Blob([buffer], { type: 'image/png' })
formData.append('file', blob, `og-${Date.now()}.png`)
const uploadResponse = await fetch('/api/upload-to-quoter', {
method: 'POST',
body: formData,
})
return await uploadResponse.text()
} finally {
setLoading(false)
}
}
return { generateAndUpload, loading }
}
// Использование в компоненте
function MyComponent() {
const { generateAndUpload, loading } = useOgImage()
const handleCreateOg = async () => {
const quoterUrl = await generateAndUpload('My Post Title', '/existing-image.jpg')
console.log('OG image uploaded to:', quoterUrl)
}
return (
<button onClick={handleCreateOg} disabled={loading}>
{loading ? 'Generating...' : 'Create OG Image'}
</button>
)
}
⚡ Production Tips
Кэширование
// Cache OG images for 24 hours
export default function handler(req) {
const response = new ImageResponse(/* ... */)
response.headers.set('Cache-Control', 'public, max-age=86400')
response.headers.set('CDN-Cache-Control', 'public, max-age=86400')
return response
}
Error Handling
export default async function handler(req) {
try {
return new ImageResponse(/* ... */)
} catch (error) {
console.error('OG generation failed:', error)
// Fallback изображение
return new Response('Failed to generate image', { status: 500 })
}
}
Environment Variables
# .env.local
QUOTER_API_URL=https://quoter.staging.discours.io
QUOTER_AUTH_TOKEN=your_jwt_token
NEXT_PUBLIC_OG_BASE_URL=https://yoursite.com/api/og
🔗 Полезные ссылки
💋 Упрощение: Один endpoint для генерации, один для загрузки - минимум кода, максимум результата!