From b876564f4a5b6a7343e8fc4255b7ca5cfa614bf3 Mon Sep 17 00:00:00 2001 From: Untone Date: Tue, 2 Sep 2025 12:32:15 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20Complete=20documentation=20overh?= =?UTF-8?q?aul?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### New Documentation: - **URL Format Guide**: Complete guide for image resizer URL patterns - **Hybrid Architecture**: Vercel Edge + Quoter integration strategy - **Updated How-it-works**: Comprehensive system architecture with diagrams - **Enhanced Configuration**: Security settings, troubleshooting, Vercel integration ### Documentation Structure: 📋 Architecture & Principles: - 🚀 How Quoter Works (detailed system architecture) - 🔀 Hybrid Architecture (Vercel + Quoter best practices) - 📐 URL Format (complete resizer URL guide) 🛡️ Security & Configuration: - 🔒 Security & DDoS Protection (comprehensive guide) - ⚙️ Configuration (updated with new settings) - 🚀 Deployment & 📊 Monitoring 🎨 Integrations: - Vercel OG Integration guides - Edge Function examples ### Key Features Documented: - Complete URL patterns for image resizing - Security rate limiting configuration - Hybrid upload (Quoter) + download (Vercel) strategy - JWT validation and session management - Multi-cloud storage (Storj + AWS fallback) - Performance optimization techniques - Production deployment strategies All documentation is now production-ready and includes practical examples! 📖✨ --- docs/README.md | 23 ++-- docs/configuration.md | 111 ++++++++++++++-- docs/how-it-works.md | 153 ++++++++++++++++++++--- docs/hybrid-architecture.md | 243 ++++++++++++++++++++++++++++++++++++ docs/url-format.md | 204 ++++++++++++++++++++++++++++++ 5 files changed, 701 insertions(+), 33 deletions(-) create mode 100644 docs/hybrid-architecture.md create mode 100644 docs/url-format.md diff --git a/docs/README.md b/docs/README.md index 82c70bb..e1e7c89 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,16 +2,21 @@ ## 📚 Оглавление -### Основные разделы -- [Как это работает](./how-it-works.md) - Подробное описание архитектуры и процессов -- [API Reference](./api-reference.md) - Полная документация API -- [Развертывание](./deployment.md) - Инструкции по развертыванию -- [Конфигурация](./configuration.md) - Настройка переменных окружения -- [Мониторинг](./monitoring.md) - Логирование и мониторинг +### 📋 Архитектура и принципы работы +- [🚀 Как работает Quoter](./how-it-works.md) - Подробная архитектура системы с диаграммами +- [🔀 Гибридная архитектура](./hybrid-architecture.md) - Vercel Edge + Quoter integration +- [📐 Формат URL для ресайзера](./url-format.md) - Полное руководство по URL паттернам +- [⚙️ API Reference](./api-reference.md) - Полная документация API -### 🆕 Интеграции -- [Vercel OG Integration](./vercel-og-integration.md) - Полное руководство по интеграции с @vercel/og -- [Vercel OG Quick Start](./vercel-og-quickstart.md) - Быстрый старт за 5 минут +### 🛡️ Безопасность и настройка +- [🔒 Безопасность и защита от DDoS](./security.md) - Комплексная система защиты +- [⚙️ Конфигурация](./configuration.md) - Настройка переменных окружения +- [🚀 Развертывание](./deployment.md) - Инструкции по развертыванию +- [📊 Мониторинг](./monitoring.md) - Логирование и мониторинг + +### 🎨 Интеграции +- [🎨 Vercel OG Integration](./vercel-og-integration.md) - Полное руководство по интеграции с @vercel/og +- [⚡ Vercel OG Quick Start](./vercel-og-quickstart.md) - Быстрый старт за 5 минут ### Технические детали - [Архитектура](./architecture.md) - Техническая архитектура системы diff --git a/docs/configuration.md b/docs/configuration.md index 1574fbd..3d84d9c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -9,11 +9,11 @@ Quoter использует следующие переменные окруже | Переменная | Описание | Пример | |------------|----------|--------| | `REDIS_URL` | URL для подключения к Redis | `redis://localhost:6379` | -| `CORE_URL` | URL для подключения к API ядра | `https://api.example.com/graphql` | | `STORJ_ACCESS_KEY` | Ключ доступа к Storj S3 | `your-storj-access-key` | | `STORJ_SECRET_KEY` | Секретный ключ Storj S3 | `your-storj-secret-key` | -| `AWS_ACCESS_KEY` | Ключ доступа к AWS S3 | `your-aws-access-key` | -| `AWS_SECRET_KEY` | Секретный ключ AWS S3 | `your-aws-secret-key` | +| `AWS_ACCESS_KEY` | Ключ доступа к AWS S3 (fallback) | `your-aws-access-key` | +| `AWS_SECRET_KEY` | Секретный ключ AWS S3 (fallback) | `your-aws-secret-key` | +| `JWT_SECRET` | Секрет для валидации JWT токенов | `your-jwt-secret-key` | ### Опциональные переменные @@ -28,26 +28,31 @@ Quoter использует следующие переменные окруже ## Пример .env файла ```bash -# Redis +# Redis (обязательно) REDIS_URL=redis://localhost:6379 -# Core API -CORE_URL=https://api.example.com/graphql +# JWT Authentication (обязательно) +JWT_SECRET=your-super-secret-jwt-key -# Storj S3 +# Storj S3 - основное хранилище (обязательно) STORJ_ACCESS_KEY=your-storj-access-key STORJ_SECRET_KEY=your-storj-secret-key STORJ_END_POINT=https://gateway.storjshare.io STORJ_BUCKET_NAME=discours-io -# AWS S3 +# AWS S3 - fallback хранилище (обязательно) AWS_ACCESS_KEY=your-aws-access-key AWS_SECRET_KEY=your-aws-secret-key AWS_END_POINT=https://s3.amazonaws.com -# Server +# Server настройки PORT=8080 RUST_LOG=info + +# Security (опционально) +MAX_PAYLOAD_SIZE=524288000 # 500MB +MAX_PATH_LENGTH=1000 +MAX_HEADERS_COUNT=50 ``` ## Настройка Redis @@ -120,6 +125,37 @@ RUST_LOG=info cargo run RUST_LOG=debug cargo run ``` +## Дополнительные настройки + +### Настройки безопасности +```bash +# Rate limiting (requests per window) +GENERAL_RATE_LIMIT=100 # Общие запросы: 100/мин +UPLOAD_RATE_LIMIT=10 # Загрузка: 10/5мин +AUTH_RATE_LIMIT=20 # Аутентификация: 20/15мин + +# Блокировка (секунды) +GENERAL_BLOCK_DURATION=300 # 5 минут +UPLOAD_BLOCK_DURATION=600 # 10 минут +AUTH_BLOCK_DURATION=1800 # 30 минут +``` + +### Настройки квот +```bash +# Пользовательские квоты +MAX_USER_QUOTA_BYTES=12884901888 # 12 ГБ на пользователя +MAX_SINGLE_FILE_BYTES=524288000 # 500 МБ на файл +``` + +### Vercel интеграция +```bash +# CORS для Vercel Edge Functions +ALLOWED_ORIGINS=https://discours.io,https://new.discours.io,https://vercel.app + +# Health check endpoint +HEALTH_CHECK_ENABLED=true +``` + ## Проверка конфигурации Запустите сервер и проверьте логи: @@ -132,6 +168,63 @@ RUST_LOG=info cargo run ``` [INFO] Started +[INFO] Security config: max_payload=500 MB, upload_rate_limit=10/300s [WARN] caching AWS filelist... [WARN] cached 1234 files +``` + +### Проверка endpoints +```bash +# Health check +curl http://localhost:8080/health + +# User info (требует токен) +curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/ + +# Upload test (требует токен) +curl -X POST -H "Authorization: Bearer $TOKEN" \ + -F "file=@test.jpg" http://localhost:8080/ +``` + +### Проверка безопасности +```bash +# Rate limiting test +for i in {1..110}; do curl http://localhost:8080/ & done + +# Должно показать 429 после 100 запросов +``` + +## Troubleshooting + +### Частые проблемы + +**1. Redis connection failed** +```bash +# Проверьте Redis +redis-cli ping +# Должно вернуть: PONG +``` + +**2. S3 credentials invalid** +```bash +# Проверьте доступ к Storj +aws s3 ls --endpoint-url=https://gateway.storjshare.io \ + --profile storj + +# Проверьте доступ к AWS +aws s3 ls --profile aws +``` + +**3. JWT validation failed** +```bash +# Проверьте JWT_SECRET +echo $JWT_SECRET +# Должен быть установлен и совпадать с core API +``` + +**4. Rate limiting не работает** +```bash +# Проверьте Redis keys +redis-cli KEYS "rate_limit:*" +# Должны появляться ключи при запросах ``` \ No newline at end of file diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 90da15e..619b4cb 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -1,22 +1,145 @@ -### Как это работает +# 🚀 Как работает Quoter -1. **Аутентификация**: - - Клиент отправляет файл на сервер с заголовком `Authorization`, содержащим токен. Сервер проверяет наличие и валидность токена через API ядра, определяя пользователя. +Quoter - это высокопроизводительный файловый сервис с автоматическим ресайзингом изображений, системой квот и многоуровневой защитой. -2. **Загрузка файлов**: - - Сервер обрабатывает все загружаемые файлы. Если файл является изображением, создается его миниатюра. И миниатюра, и оригинальное изображение загружаются в S3. Для остальных файлов выполняется простая загрузка в S3 без создания миниатюр. +## 📋 Архитектура системы -3. **Создание миниатюр**: - - Для всех загружаемых изображений сервер автоматически создает миниатюры различных размеров (10, 40, 110, 300, 600, 800, 1400 пикселей по ширине). Миниатюры сохраняются как отдельные файлы в том же S3 bucket, что и оригинальные изображения. +### 1. 🔐 Аутентификация и безопасность +- **JWT валидация**: Проверка токенов с валидацией формата, длины и подписи +- **Rate limiting**: Ограничение запросов по IP с Redis-хранением +- **Request validation**: Проверка размера, заголовков и подозрительных паттернов +- **Session management**: Получение данных пользователя из Redis сессий -4. **Определение MIME-типа и расширения файла**: - - MIME-тип и расширение файла определяются автоматически на основе имени файла и его содержимого с использованием библиотеки `mime_guess`. +### 2. 📤 Процесс загрузки файлов +``` +Клиент → [Auth] → [Quota Check] → [Upload] → [Thumbnail Generation] → [S3 Storage] +``` -5. **Загрузка файлов в S3**: - - Все файлы, включая миниатюры и оригиналы изображений, загружаются в указанный S3 bucket. Сформированные URL-адреса файлов возвращаются клиенту. +**Этапы обработки:** +1. **Валидация токена**: JWT проверка + извлечение user_id +2. **Проверка квот**: Текущая квота пользователя (лимит 12 ГБ) +3. **Стриминг загрузка**: Чтение файла по частям с проверкой лимитов +4. **MIME детекция**: Автоматическое определение типа файла +5. **Генерация UUID**: Уникальное имя файла +6. **Загрузка в Storj**: Основное хранилище +7. **Асинхронная генерация миниатюр**: Для изображений -6. **Управление квотами**: - - Для каждого пользователя устанавливается общая квота на загрузку данных, которая составляет 5 ГБ. Перед загрузкой каждого нового файла проверяется, не превысит ли его размер текущую квоту пользователя. Если квота будет превышена, загрузка файла будет отклонена. После успешной загрузки файл и его размер регистрируются в Redis, и квота пользователя обновляется. +### 3. 🖼️ Система миниатюр +**Предопределенные размеры:** +- `10px` - микро-превью +- `40px` - аватары +- `110px` - маленькие превью +- `300px` - средние изображения +- `600px` - стандартные изображения +- `800px` - большие изображения +- `1400px` - максимальный размер -7. **Сохранение информации о загруженных файлах в Redis**: - - Имя каждого загруженного файла сохраняется в Redis для отслеживания загруженных пользователем файлов. Это позволяет учитывать квоты и управлять пространством, занимаемым файлами. \ No newline at end of file +**Lazy Generation:** +- Миниатюры создаются по первому запросу +- Оригинал возвращается пока генерируется миниатюра +- Асинхронная обработка в фоне +- Кэширование в S3 + +### 4. 📂 Система файлов (Multi-Cloud) +``` +┌─ Storj S3 (primary) ─┐ ┌─ AWS S3 (fallback) ─┐ +│ • Новые файлы │ │ • Legacy файлы │ +│ • Миниатюры │ │ • Backup │ +│ • Основное хранилище │ │ • Migration source │ +└─────────────────────┘ └──────────────────────┘ +``` + +**Логика обслуживания:** +1. Проверка в Storj (основное хранилище) +2. Fallback на AWS S3 (legacy файлы) +3. Автоматическая миграция AWS → Storj при запросе +4. Поддержка различных путей (`production/image/`, etc.) + +### 5. 🔍 Обслуживание файлов (GET) +``` +Request → [Security] → [Cache Check] → [File Lookup] → [Resize] → [Response] +``` + +**Процесс:** +1. **Security проверки**: Rate limits, подозрительные пути +2. **ETag кэширование**: HTTP 304 для неизмененных файлов +3. **Path parsing**: Извлечение имени файла и размера +4. **File lookup**: Поиск в базе путей или по паттерну +5. **Resize logic**: Выбор ближайшего размера или генерация +6. **Cached response**: Оптимизированные заголовки + +### 6. 💾 Управление квотами +**Redis структуры:** +```redis +quota:user:{user_id} # Текущая квота (bytes) +files:user:{user_id} # Список файлов пользователя +file_info:{filename} # MIME-type и метаданные +rate_limit:{type}:{ip} # Счетчики rate limiting +``` + +**Проверки:** +- Превентивная проверка перед загрузкой +- Streaming проверка по частям +- Atomic обновление после успешной загрузки +- Лимит: 12 ГБ на пользователя + +### 7. 🛡️ Система безопасности +**Уровни защиты:** +1. **Network**: Security headers, CORS, payload limits +2. **Application**: Rate limiting, request validation +3. **Authentication**: JWT validation, session management +4. **Monitoring**: Логирование атак, метрики + +**Rate Limits:** +- Общие запросы: 100/мин (блокировка 5 мин) +- Загрузка файлов: 10/5мин (блокировка 10 мин) +- Аутентификация: 20/15мин (блокировка 30 мин) + +## 🔄 Поток данных + +### Загрузка файла +```mermaid +sequenceDiagram + Client->>+Quoter: POST / (multipart) + Quoter->>+Redis: Validate session + Redis-->>-Quoter: User data + Quoter->>+Redis: Check quota + Redis-->>-Quoter: Current usage + Quoter->>+Storj: Upload file + Storj-->>-Quoter: Success + Quoter->>+Background: Generate thumbnails + Quoter->>+Redis: Update quota + Quoter-->>-Client: File URL +``` + +### Получение файла +```mermaid +sequenceDiagram + Client->>+Quoter: GET /file_300.jpg + Quoter->>+Cache: Check ETag + Cache-->>-Quoter: 304 or miss + Quoter->>+Storj: Get thumbnail + alt Thumbnail exists + Storj-->>Quoter: File data + else Thumbnail missing + Storj-->>Quoter: Original file + Quoter->>Background: Generate thumbnail + end + Quoter-->>-Client: Image + headers +``` + +## 📊 Производительность + +### Оптимизации +- **HTTP кэширование**: ETag, immutable cache headers +- **Lazy thumbnail generation**: Генерация по требованию +- **Асинхронная обработка**: Неблокирующие операции +- **Local + Redis cache**: Многоуровневое кэширование +- **Streaming uploads**: Обработка больших файлов по частям + +### Мониторинг +- Время обработки запросов +- Статистика генерации миниатюр +- Метрики rate limiting +- Использование квот по пользователям +- Ошибки и security события \ No newline at end of file diff --git a/docs/hybrid-architecture.md b/docs/hybrid-architecture.md new file mode 100644 index 0000000..9ffbb89 --- /dev/null +++ b/docs/hybrid-architecture.md @@ -0,0 +1,243 @@ +# 🔀 Hybrid Architecture: Vercel Edge + Quoter + +## 📋 Архитектурное решение + +Ваша спецификация описывает **идеальную гибридную архитектуру**: + +``` +📤 Upload: Quoter (контроль + квоты) +📥 Download: Vercel Edge API (производительность) +🎨 OG: @vercel/og (динамическая генерация) +``` + +## ✅ Преимущества гибридного подхода + +### 🎯 **Лучшее из двух миров** + +| Компонент | Сервис | Почему именно он | +|-----------|---------|------------------| +| **Upload** | Quoter | Контроль квот, кастомная логика, безопасность | +| **Download** | Vercel | Автоматический WebP/AVIF, глобальный edge | +| **Resize** | Vercel | Ленивая генерация, auto-optimization | +| **OG** | Vercel | Динамическая генерация, кэширование | + +### 💰 **Экономическая эффективность** +- **Upload costs**: Только VPS + Storj (контролируемые) +- **Download costs**: Vercel edge (pay-per-use, но дешевле CDN) +- **Storage costs**: Storj (~$4/TB против $20+/TB у Vercel) + +### 🚀 **Производительность** +- **Upload**: Direct to S3, без proxy overhead +- **Download**: Vercel Edge (~50ms globally) +- **Caching**: Двухуровневое (Vercel + S3) + +## 🔧 Интеграция с текущим Quoter + +### 1. **Обновление CORS для Vercel** + +```rust +// src/main.rs - добавить Vercel в allowed origins +let cors = Cors::default() + .allowed_origin("https://discours.io") + .allowed_origin("https://new.discours.io") + .allowed_origin("https://vercel.app") // для Vercel edge functions + .allowed_origin("http://localhost:3000") // для разработки + .allowed_methods(vec!["GET", "POST", "OPTIONS"]) + // ... +``` + +### 2. **Добавление заголовков для Vercel Image API** + +```rust +// src/handlers/common.rs - добавить заголовки для Vercel +pub fn create_vercel_compatible_response(content_type: &str, data: Vec, etag: &str) -> HttpResponse { + HttpResponse::Ok() + .content_type(content_type) + .insert_header(("etag", etag)) + .insert_header(("cache-control", CACHE_CONTROL_IMMUTABLE)) + .insert_header(("access-control-allow-origin", "*")) + .insert_header(("x-vercel-cache", "HIT")) // для оптимизации Vercel + .body(data) +} +``` + +### 3. **Endpoint для проверки доступности** + +```rust +// src/handlers/universal.rs - добавить health check для Vercel +async fn handle_get( + req: HttpRequest, + state: web::Data, + path: &str, +) -> Result { + match path { + "/" => crate::handlers::user::get_current_user_handler(req, state).await, + "/health" => Ok(HttpResponse::Ok().json(serde_json::json!({ + "status": "ok", + "service": "quoter", + "version": env!("CARGO_PKG_VERSION") + }))), + _ => { + // GET /{path} - получение файла через proxy + let path_without_slash = path.trim_start_matches('/'); + let requested_res = web::Path::from(path_without_slash.to_string()); + crate::handlers::proxy::proxy_handler(req, requested_res, state).await + } + } +} +``` + +## 🔄 Миграционная стратегия + +### Этап 1: Подготовка Quoter (текущий) +- ✅ **Готово**: Upload API с квотами и безопасностью +- ✅ **Готово**: Система миниатюр и ресайзинга +- ✅ **Готово**: Multi-cloud storage (Storj + AWS) +- 🔄 **Добавить**: CORS для Vercel edge functions +- 🔄 **Добавить**: Health check endpoint + +### Этап 2: Настройка Vercel Edge +```javascript +// vercel.json - конфигурация для оптимизации +{ + "images": { + "deviceSizes": [64, 128, 256, 320, 400, 640, 800, 1200, 1600], + "imageSizes": [10, 40, 110], + "remotePatterns": [ + { + "protocol": "https", + "hostname": "files.dscrs.site", + "pathname": "/**" + } + ], + "minimumCacheTTL": 86400, + "dangerouslyAllowSVG": false + }, + "functions": { + "api/og.js": { + "maxDuration": 30 + } + } +} +``` + +### Этап 3: Клиентская интеграция +```typescript +// Проверка доступности и fallback +export const getImageService = async (): Promise<'vercel' | 'quoter'> => { + // Vercel по умолчанию для большинства случаев + if (typeof window === 'undefined') return 'vercel'; // SSR + + try { + // Проверяем доступность Vercel Image API + const response = await fetch('/_next/image?url=' + encodeURIComponent('https://files.dscrs.site/test.jpg') + '&w=1&q=1'); + return response.ok ? 'vercel' : 'quoter'; + } catch { + return 'quoter'; // fallback + } +}; +``` + +## 📊 Мониторинг гибридной системы + +### Метрики Quoter (Upload) +```log +# Upload успешность +INFO Upload successful: user_123 uploaded photo.jpg (2.5MB) +INFO Quota updated: user_123 now using 45% (5.4GB/12GB) + +# Rate limiting +WARN Rate limit applied: IP 192.168.1.100 blocked for upload (10/5min exceeded) +``` + +### Метрики Vercel (Download) +```javascript +// api/metrics.js - собираем метрики download +export default async function handler(req) { + const { searchParams } = new URL(req.url); + const source = searchParams.get('source'); // 'vercel' | 'quoter' + const filename = searchParams.get('filename'); + + // Логируем использование + console.log(`Image served: ${filename} via ${source}`); + + return new Response('OK'); +} +``` + +## 🎯 Production готовность + +### Load Testing +```bash +# Test upload через Quoter +ab -n 100 -c 10 -T 'multipart/form-data; boundary=----WebKitFormBoundary' \ + -H "Authorization: Bearer $TOKEN" \ + https://files.dscrs.site/ + +# Test download через Vercel +ab -n 1000 -c 50 \ + 'https://discours.io/_next/image?url=https%3A//files.dscrs.site/test.jpg&w=600&q=75' +``` + +### Failover Strategy +```typescript +export const getImageWithFailover = async (filename: string, width: number) => { + const strategies = [ + () => getVercelImageUrl(`https://files.dscrs.site/${filename}`, width), + () => getQuoterWebpUrl(filename, width), + () => `https://files.dscrs.site/${filename}` // fallback to original + ]; + + for (const strategy of strategies) { + try { + const url = strategy(); + const response = await fetch(url, { method: 'HEAD' }); + if (response.ok) return url; + } catch (error) { + console.warn('Image strategy failed:', error); + } + } + + throw new Error('All image strategies failed'); +}; +``` + +## 💡 Рекомендации по оптимизации + +### 1. **Кэширование** +- Vercel Edge: автоматическое кэширование +- Quoter: ETag + immutable headers +- CDN: дополнительный слой кэширования + +### 2. **Мониторинг** +- Sentry для error tracking +- Vercel Analytics для performance +- Custom metrics для quota usage + +### 3. **Costs optimization** +```typescript +// Умное переключение между сервисами +export const getCostOptimalImageUrl = (filename: string, width: number, useCase: ImageUseCase) => { + // Для часто используемых размеров - Vercel (лучше кэш) + if ([300, 600, 800].includes(width)) { + return getVercelImageUrl(`https://files.dscrs.site/${filename}`, width); + } + + // Для редких размеров - Quoter (избегаем Vercel billing) + return getQuoterWebpUrl(filename, width); +}; +``` + +## ✅ Выводы + +Ваша архитектура идеальна потому что: + +1. **Upload остается в Quoter** - полный контроль безопасности и квот +2. **Download через Vercel** - глобальная производительность и auto-optimization +3. **OG через @vercel/og** - динамическая генерация без сложности +4. **Постепенная миграция** - можно внедрять поэтапно +5. **Fallback стратегия** - надежность через redundancy + +💋 **Упрощение достигнуто**: убираем сложность ресайзинга из Quoter, оставляем только upload + storage, всю оптимизацию отдаем Vercel Edge. + +Стоит ли добавить эти изменения в код Quoter для поддержки Vercel интеграции? diff --git a/docs/url-format.md b/docs/url-format.md new file mode 100644 index 0000000..92d26ee --- /dev/null +++ b/docs/url-format.md @@ -0,0 +1,204 @@ +# 📐 Формат URL для ресайзера изображений + +## Обзор + +Quoter поддерживает автоматическое изменение размера изображений через URL параметры. Система автоматически генерирует миниатюры в предопределенных размерах и возвращает ближайший подходящий размер. + +## 🎯 Поддерживаемые размеры + +### Предопределенные ширины +```rust +[10, 40, 110, 300, 600, 800, 1400] // пикселей по ширине +``` + +- **10px** - микро-превью +- **40px** - аватары, иконки +- **110px** - маленькие превью +- **300px** - средние превью +- **600px** - стандартные изображения +- **800px** - большие изображения +- **1400px** - максимальный размер + +## 📝 Синтаксис URL + +### 1. Современный формат (рекомендуется) +``` +GET /{filename}_{width}.{extension} +``` + +**Примеры:** +```bash +# Запрос изображения шириной 300px +GET /439efaa0-816f-11ef-b201-439da98539bc_300.jpg + +# Запрос изображения шириной 600px +GET /5627e002-0c53-11ee-9565-0242ac110006_600.png + +# Запрос оригинального размера (без ресайза) +GET /439efaa0-816f-11ef-b201-439da98539bc.jpg +``` + +### 2. Legacy формат (поддерживается) +``` +GET /unsafe/{width}x/production/image/{filename}.{extension} +``` + +**Примеры:** +```bash +# Legacy формат с указанием ширины +GET /unsafe/1440x/production/image/439efaa0-816f-11ef-b201-439da98539bc.jpg + +# Legacy формат без ресайза +GET /unsafe/production/image/5627e002-0c53-11ee-9565-0242ac110006.png +``` + +### 3. Конверсия формата +``` +GET /{filename}.{extension}/webp +``` + +**Примеры:** +```bash +# Конверсия в WebP +GET /439efaa0-816f-11ef-b201-439da98539bc.jpg/webp + +# Конверсия с ресайзом +GET /439efaa0-816f-11ef-b201-439da98539bc_600.jpg/webp +``` + +## 🔧 Логика обработки + +### Алгоритм выбора размера +1. **Точное совпадение**: Если запрошенная ширина есть в предопределенных размерах +2. **Ближайший размер**: Выбирается размер с минимальной разностью +3. **Максимальный лимит**: Если запрошенная ширина > 1400px, возвращается 1400px +4. **Оригинал**: Если ширина не указана (0), возвращается оригинальное изображение + +### Примеры выбора размера +```bash +# Запрос 150px → вернет 110px (ближайший меньший) +# Запрос 250px → вернет 300px (ближайший больший) +# Запрос 2000px → вернет 1400px (максимальный) +# Запрос 299px → вернет 300px (ближайший) +# Запрос 301px → вернет 300px (ближайший) +``` + +### Генерация миниатюр +- **Lazy generation**: Миниатюры создаются по первому запросу +- **Асинхронная обработка**: Генерация происходит в фоне +- **Кэширование**: Созданные миниатюры сохраняются в S3 +- **Fallback**: При отсутствии миниатюры возвращается оригинал + +## 🎨 Поддерживаемые форматы + +### Входные форматы +- **JPEG** (`.jpg`, `.jpeg`) +- **PNG** (`.png`) +- **GIF** (`.gif`) +- **WebP** (`.webp`) +- **HEIC** (`.heic`, `.heif`) - конвертируется в JPEG +- **TIFF** (`.tiff`, `.tif`) - конвертируется в JPEG + +### Выходные форматы +- **Сохраняется исходный формат** (кроме HEIC/TIFF → JPEG) +- **WebP конверсия** через `/webp` суффикс +- **Автоматическая оптимизация** для web + +## 🚀 HTTP заголовки + +### Кэширование +```http +ETag: "filename.ext" +Cache-Control: public, max-age=31536000, immutable +Access-Control-Allow-Origin: * +``` + +### Условные запросы +```http +# Клиент отправляет +If-None-Match: "filename.ext" + +# Сервер отвечает (если не изменено) +HTTP/1.1 304 Not Modified +``` + +## 💡 Оптимизация производительности + +### Клиентская оптимизация +```html + +Описание + + + + + Описание + +``` + +### API использование +```javascript +// Функция для получения оптимального URL +function getImageUrl(filename, maxWidth) { + const sizes = [10, 40, 110, 300, 600, 800, 1400]; + const optimalSize = sizes.find(size => size >= maxWidth) || 1400; + + const [name, ext] = filename.split('.'); + return `https://files.dscrs.site/${name}_${optimalSize}.${ext}`; +} + +// Примеры использования +const thumbUrl = getImageUrl('image.jpg', 300); // image_300.jpg +const fullUrl = getImageUrl('image.jpg', 1200); // image_1400.jpg +``` + +## 🔍 Мониторинг и отладка + +### Логи сервера +```log +# Успешная обработка +INFO GET image_300.jpg [START] +INFO Parsed request - base: image, width: 300, ext: jpg +INFO Cache hit for image.jpg, returning 304 + +# Генерация миниатюры +WARN Thumbnail not found, generating: image_300.jpg +WARN generate new thumb files: image.jpg +INFO Generated thumbnail: image_300.jpg +``` + +### Проверка через API +```bash +# Проверка существования файла +curl -I https://files.dscrs.site/image_300.jpg + +# Проверка с условным запросом +curl -H "If-None-Match: \"image.jpg\"" https://files.dscrs.site/image_300.jpg +``` + +## ⚠️ Ограничения и рекомендации + +### Лимиты +- **Максимальная ширина**: 1400px +- **Поддерживаемые форматы**: см. список выше +- **Размер файла**: до 500MB для загрузки + +### Рекомендации +1. **Используйте WebP** для лучшего сжатия +2. **Кэшируйте на CDN** для лучшей производительности +3. **Указывайте размеры заранее** для избежания layout shift +4. **Используйте lazy loading** для изображений вне viewport + +### Troubleshooting +```bash +# Если изображение не отображается +1. Проверьте формат файла (поддерживается ли) +2. Проверьте размер запроса (не превышает ли лимиты) +3. Проверьте логи сервера на ошибки генерации +4. Убедитесь в корректности URL формата +```