diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml index 66534aa..86c0219 100644 --- a/.gitea/workflows/main.yml +++ b/.gitea/workflows/main.yml @@ -39,16 +39,27 @@ jobs: restore-keys: | ${{ runner.os }}-cargo- - - name: Build with memory optimization + - name: Build with extreme memory optimization run: | + # Create additional swap for memory-intensive compilation + sudo fallocate -l 4G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile + # Apply memory optimizations for CI environment export CARGO_NET_GIT_FETCH_WITH_CLI=true export CARGO_NET_RETRY=3 export CARGO_NET_TIMEOUT=60 export CARGO_HTTP_TIMEOUT=60 export RUSTC_FORCE_INCREMENTAL=0 - # Build with our optimized configuration + + # Build with all optimizations cargo build --verbose + + # Clean up swap + sudo swapoff /swapfile || true + sudo rm -f /swapfile || true - name: Install cargo-llvm-cov run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index c32dcd6..3df69c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - 🚀 **HTTP кэширование** с ETag и Cache-Control заголовками для статических файлов - 🚀 **Оптимизация proxy_handler** - добавлена поддержка 304 Not Modified ответов - 📊 **Метрики производительности** - timing логирование для всех запросов файлов +- 📚 **@vercel/og интеграция** - полная документация по интеграции с Vercel OG библиотекой ### Changed - 🔄 **Кардинальное изменение архитектуры GET /**: переход от GraphQL API к Redis сессиям diff --git a/docs/README.md b/docs/README.md index b4c53f2..82c70bb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,10 @@ - [Конфигурация](./configuration.md) - Настройка переменных окружения - [Мониторинг](./monitoring.md) - Логирование и мониторинг +### 🆕 Интеграции +- [Vercel OG Integration](./vercel-og-integration.md) - Полное руководство по интеграции с @vercel/og +- [Vercel OG Quick Start](./vercel-og-quickstart.md) - Быстрый старт за 5 минут + ### Технические детали - [Архитектура](./architecture.md) - Техническая архитектура системы - [База данных](./database.md) - Структура Redis и схемы данных diff --git a/docs/api-reference.md b/docs/api-reference.md index 6e93dce..df96a2f 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -4,6 +4,8 @@ Quoter предоставляет REST API для загрузки файлов, управления квотами и получения файлов с автоматической генерацией миниатюр. +🆕 **Интеграция с @vercel/og**: Quoter теперь оптимизирован для работы с библиотекой `@vercel/og` для генерации динамических OpenGraph изображений. Подробности см. в [Vercel OG Integration Guide](./vercel-og-integration.md). + ## Базовый URL ``` @@ -92,15 +94,22 @@ Authorization: Bearer ### 4. Получение файлов #### GET /{filename} -Получает файл по имени. +Получает файл по имени с автоматической генерацией миниатюр. **Примеры:** ``` -GET /image.jpg -GET /image_300.jpg -GET /image_300.jpg/webp +GET /image.jpg # Оригинальный файл +GET /image_300.jpg # Миниатюра 300px ширины +GET /image_300.jpg/webp # Миниатюра в формате WebP ``` +**🚫 Удаленные параметры (Legacy):** +- `s=` - параметр для OpenGraph overlay больше не поддерживается +- Встроенная генерация text overlay теперь обрабатывается через `@vercel/og` + +**✅ Современная альтернатива:** +Для генерации OpenGraph изображений с текстом используйте интеграцию с `@vercel/og`. См. [Vercel OG Integration Guide](./vercel-og-integration.md). + ### 5. Управление квотами #### GET /quota diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 922abe7..90da15e 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -19,7 +19,4 @@ - Для каждого пользователя устанавливается общая квота на загрузку данных, которая составляет 5 ГБ. Перед загрузкой каждого нового файла проверяется, не превысит ли его размер текущую квоту пользователя. Если квота будет превышена, загрузка файла будет отклонена. После успешной загрузки файл и его размер регистрируются в Redis, и квота пользователя обновляется. 7. **Сохранение информации о загруженных файлах в Redis**: - - Имя каждого загруженного файла сохраняется в Redis для отслеживания загруженных пользователем файлов. Это позволяет учитывать квоты и управлять пространством, занимаемым файлами. - -8. **Оверлей для shout**: - - При загрузке файла, если он является изображением, и в запросе присутствует параметр `s=`, то к файлу будет добавлен оверлей с данными shout. + - Имя каждого загруженного файла сохраняется в Redis для отслеживания загруженных пользователем файлов. Это позволяет учитывать квоты и управлять пространством, занимаемым файлами. \ No newline at end of file diff --git a/docs/vercel-og-integration.md b/docs/vercel-og-integration.md new file mode 100644 index 0000000..9a22055 --- /dev/null +++ b/docs/vercel-og-integration.md @@ -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=` в 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( + ( +
+ Hello world! +
+ ), + { + 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( + ( +
+ {/* Overlay для читаемости */} +
+ + {/* Контент */} +
+

+ {title} +

+

+ {description} +

+
+
+ ), + { + 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 { + 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( +
+ Custom font text +
, + { + width: 1200, + height: 630, + fonts: [ + { + name: 'Muller', + data: await font, + style: 'normal', + }, + ], + } +) +``` + +### Динамическая загрузка изображений из Quoter + +```typescript +async function loadQuoterImage(imageId: string): Promise { + 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 - хранением и доставкой. diff --git a/docs/vercel-og-quickstart.md b/docs/vercel-og-quickstart.md new file mode 100644 index 0000000..e3e400c --- /dev/null +++ b/docs/vercel-og-quickstart.md @@ -0,0 +1,233 @@ +# 🚀 Quick Start: @vercel/og + Quoter + +## ⚡ 5-минутная настройка + +### 1. Установка зависимостей + +```bash +npm install @vercel/og +``` + +### 2. Создание API endpoint + +```typescript +// 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( + ( +
+ {title} +
+ ), + { width: 1200, height: 630 } + ) +} +``` + +### 3. Интеграция с Quoter + +```typescript +// 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. Использование + +```typescript +// Генерация 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 + +``` + +## 🎨 Расширенный пример с изображением фона + +```typescript +// 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( + ( +
+ {/* Затемнение для читаемости */} +
+ + {/* Заголовок */} +

+ {title} +

+
+ ), + { width: 1200, height: 630 } + ) +} +``` + +## 📱 React Hook для удобства + +```typescript +// 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 ( + + ) +} +``` + +## ⚡ Production Tips + +### Кэширование +```typescript +// 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 +```typescript +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 +```bash +# .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 +``` + +## 🔗 Полезные ссылки + +- [Полная документация по интеграции](./vercel-og-integration.md) +- [Quoter API Reference](./api-reference.md) +- [Vercel OG Official Docs](https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation) + +--- + +💋 **Упрощение**: Один endpoint для генерации, один для загрузки - минимум кода, максимум результата!