Files
quoter/docs/vercel-og-integration.md
Untone 7497b8c426
Some checks failed
Deploy / deploy (push) Has been skipped
CI / test (push) Failing after 20s
CI / lint (push) Successful in 7m1s
build-reconfig2
2025-09-02 10:46:51 +03:00

362 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🖼️ Интеграция @vercel/og с Quoter Proxy
## 📋 Обзор
`@vercel/og` - это мощная библиотека для генерации динамических OpenGraph изображений на Edge Runtime. Quoter теперь поддерживает интеграцию с @vercel/og для создания красивых социальных превью.
## 🔄 Изменения в архитектуре
### Удалённая функциональность (Legacy)
-`src/overlay.rs` - удалена встроенная логика наложения текста
-`src/Muller-Regular.woff2` - удалён встроенный шрифт
-`imageproc`, `ab_glyph` dependencies - удалены из Cargo.toml
- ❌ Параметр `s=<shout_id>` в API - больше не поддерживается
### Новая архитектура
-@vercel/og обрабатывает генерацию OpenGraph изображений
- ✅ Quoter выступает как proxy для статических файлов
- ✅ Improved caching и performance optimization
## 🚀 Настройка @vercel/og
### 1. Установка пакета
```bash
npm install @vercel/og
# или
yarn add @vercel/og
```
### 2. Базовый пример использования
```typescript
import { ImageResponse } from '@vercel/og'
export default function handler(req: Request) {
return new ImageResponse(
(
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
}}
>
Hello world!
</div>
),
{
width: 1200,
height: 600,
},
)
}
```
## 🔗 Интеграция с Quoter Proxy
### Сценарий использования
1. @vercel/og генерирует динамические OpenGraph изображения
2. Результат сохраняется через Quoter API
3. Quoter обслуживает изображения с кэшированием и оптимизацией
### Настройка Endpoint для @vercel/og
```typescript
// pages/api/og/[...slug].ts
import { ImageResponse } from '@vercel/og'
import { NextRequest } from 'next/server'
export const config = {
runtime: 'edge',
}
export default async function handler(req: NextRequest) {
try {
const { searchParams } = new URL(req.url)
// Получение параметров
const title = searchParams.get('title') ?? 'Default Title'
const description = searchParams.get('description') ?? 'Default Description'
const imageUrl = searchParams.get('image') // URL изображения из Quoter
// Загрузка изображения через Quoter proxy
let backgroundImage = null
if (imageUrl) {
try {
const imageResponse = await fetch(imageUrl)
const imageBuffer = await imageResponse.arrayBuffer()
backgroundImage = `data:image/jpeg;base64,${Buffer.from(imageBuffer).toString('base64')}`
} catch (error) {
console.error('Failed to load image from Quoter:', error)
}
}
return new ImageResponse(
(
<div
style={{
background: backgroundImage
? `url(${backgroundImage})`
: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
backgroundSize: 'cover',
backgroundPosition: 'center',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
fontFamily: 'system-ui',
}}
>
{/* Overlay для читаемости */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)',
}}
/>
{/* Контент */}
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
zIndex: 1,
padding: '40px',
textAlign: 'center',
}}
>
<h1
style={{
fontSize: '72px',
fontWeight: 'bold',
color: 'white',
margin: 0,
marginBottom: '20px',
textShadow: '2px 2px 4px rgba(0, 0, 0, 0.8)',
}}
>
{title}
</h1>
<p
style={{
fontSize: '36px',
color: '#f1f5f9',
margin: 0,
textShadow: '1px 1px 2px rgba(0, 0, 0, 0.8)',
}}
>
{description}
</p>
</div>
</div>
),
{
width: 1200,
height: 630,
}
)
} catch (e: any) {
console.log(`${e.message}`)
return new Response(`Failed to generate the image`, {
status: 500,
})
}
}
```
## 📤 Сохранение сгенерированных изображений в Quoter
### Пример интеграции
```typescript
// utils/saveToQuoter.ts
export async function saveOgImageToQuoter(
imageBuffer: Buffer,
filename: string,
token: string
): Promise<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,
})
if (!response.ok) {
throw new Error(`Failed to upload to Quoter: ${response.statusText}`)
}
const result = await response.text()
return result // URL загруженного файла
}
// Использование
async function generateAndSaveOgImage(title: string, description: string, token: string) {
// Генерация изображения через @vercel/og
const ogResponse = await fetch(`/api/og?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}`)
const imageBuffer = Buffer.from(await ogResponse.arrayBuffer())
// Сохранение в Quoter
const filename = `og-${Date.now()}.png`
const quoterUrl = await saveOgImageToQuoter(imageBuffer, filename, token)
return quoterUrl
}
```
## 🎨 Расширенные возможности
### Кастомные шрифты
```typescript
// Загрузка шрифтов
const font = fetch(
new URL('./assets/Muller-Regular.woff', import.meta.url)
).then((res) => res.arrayBuffer())
// Использование в ImageResponse
return new ImageResponse(
<div style={{ fontFamily: 'Muller' }}>
Custom font text
</div>,
{
width: 1200,
height: 630,
fonts: [
{
name: 'Muller',
data: await font,
style: 'normal',
},
],
}
)
```
### Динамическая загрузка изображений из Quoter
```typescript
async function loadQuoterImage(imageId: string): Promise<string> {
const quoterUrl = `https://quoter.staging.discours.io/${imageId}`
try {
const response = await fetch(quoterUrl)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const buffer = await response.arrayBuffer()
return `data:image/jpeg;base64,${Buffer.from(buffer).toString('base64')}`
} catch (error) {
console.error('Failed to load image from Quoter:', error)
return '' // fallback
}
}
```
## 🔧 Конфигурация Quoter для @vercel/og
### Environment Variables
```bash
# .env.local
QUOTER_API_URL=https://quoter.staging.discours.io
QUOTER_AUTH_TOKEN=your_jwt_token_here
```
### Типы для TypeScript
```typescript
// types/quoter.ts
export interface QuoterUploadResponse {
url: string
filename: string
size: number
contentType: string
}
export interface OgImageParams {
title: string
description?: string
backgroundImage?: string
template?: 'default' | 'article' | 'profile'
}
```
## 📊 Performance & Caching
### Оптимизация производительности
-@vercel/og работает на Edge Runtime
- ✅ Quoter обеспечивает кэширование с ETag
- ✅ Автоматическое сжатие изображений
- ✅ CDN-дружественные HTTP заголовки
### Рекомендации по кэшированию
```typescript
// Добавление cache headers для OG изображений
export default function handler(req: NextRequest) {
const imageResponse = new ImageResponse(/* ... */)
// Кэширование на 1 день
imageResponse.headers.set('Cache-Control', 'public, max-age=86400, s-maxage=86400')
imageResponse.headers.set('CDN-Cache-Control', 'public, max-age=86400')
return imageResponse
}
```
## 🚀 Deployment
### Vercel
1. Deploy @vercel/og endpoints на Vercel
2. Configure Quoter URL в environment variables
3. Set up proper CORS если нужно
### Standalone
1. Use Next.js standalone mode
2. Configure Quoter integration
3. Deploy где угодно с Node.js support
## 🔍 Troubleshooting
### Распространённые проблемы
1. **"Failed to load image from Quoter"**
- Проверьте доступность Quoter API
- Убедитесь в правильности токена авторизации
2. **"Font loading failed"**
- Убедитесь, что шрифты доступны в build time
- Используйте правильные MIME types
3. **"Image generation timeout"**
- Оптимизируйте сложность layout
- Уменьшите размер внешних ресурсов
## 📚 Дополнительные ресурсы
- [Vercel OG Documentation](https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation)
- [Quoter API Reference](./api-reference.md)
- [Performance Best Practices](./monitoring.md)
---
💋 **Упрощение через разделение ответственности**: @vercel/og занимается генерацией, Quoter - хранением и доставкой.