build-reconfig2
This commit is contained in:
361
docs/vercel-og-integration.md
Normal file
361
docs/vercel-og-integration.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# 🖼️ Интеграция @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 - хранением и доставкой.
|
||||
Reference in New Issue
Block a user