# 📤 Upload Client Guide - Quoter API **Версия**: 0.6.2 **Дата**: 2025-01-28 **Статус**: 🚀 Production Ready ## 🎯 Обзор Quoter предоставляет простой и надежный API для загрузки файлов с поддержкой: - JWT аутентификации - Квот пользователей (12 ГБ на пользователя) - Множественных файлов в одном запросе - Автоматического определения MIME типов - Streaming загрузки с проверкой квот ## 🔗 Endpoints ### Base URL ``` Production: https://files.dscrs.site Development: http://localhost:8080 ``` ### API Endpoints | Method | Endpoint | Описание | |--------|----------|----------| | `GET` | `/` | Информация о пользователе и квоте | | `POST` | `/` | Загрузка файлов | | `GET` | `/{filename}` | Получение файла | ## 🔐 Аутентификация POST запросы требуют JWT токен в заголовке `Authorization`: ```http Authorization: Bearer ``` ### Формат токена - JWT токен с claims: `{ user_id, username, exp?, iat? }` - Минимальная длина: 100 символов - Максимальная длина: 2048 символов ## 📊 Информация о пользователе ### GET / Получает информацию о текущем пользователе и его квоте. **Заголовки:** ```http Authorization: Bearer ``` **Ответ:** ```json { "user_id": "user123", "username": "john_doe", "token_type": "Bearer", "created_at": "2025-01-28T10:00:00Z", "last_activity": "2025-01-28T12:00:00Z", "auth_data": {...}, "device_info": {...}, "quota": { "current_quota": 1073741824, "max_quota": 12884901888, "usage_percentage": 8.33 } } ``` **Коды ответов:** - `200` - Успешно - `401` - Неверный или истекший токен ## 📤 Загрузка файлов ### POST / Загружает один или несколько файлов. **Заголовки:** ```http Authorization: Bearer Content-Type: multipart/form-data ``` **Параметры:** - `file` (multipart) - Файл для загрузки (можно несколько) **Лимиты:** - Максимальный размер одного файла: **500 МБ** - Максимальная квота пользователя: **12 ГБ** - Поддерживаемые форматы: изображения, аудио, видео, документы **Ответ (один файл):** ``` filename-uuid.ext ``` **Ответ (несколько файлов):** ```json { "uploaded_files": [ "file1-uuid.ext", "file2-uuid.ext" ], "count": 2 } ``` **Коды ответов:** - `200` - Файл(ы) успешно загружены - `400` - Нет файлов или все файлы пустые - `401` - Неверный токен авторизации - `413` - Превышен лимит размера файла или квоты - `415` - Неподдерживаемый формат файла - `500` - Ошибка сервера ## 📁 Получение файлов ### GET /{filename} Получает загруженный файл. **Параметры:** - `filename` - Имя файла (UUID + расширение) **Заголовки (опционально):** ```http If-None-Match: "etag-value" Range: bytes=0-1023 ``` **Ответ:** - `200` - Файл найден - `304` - Файл не изменился (если передан ETag) - `404` - Файл не найден - `206` - Частичный контент (если передан Range) ## 💻 Примеры кода ### JavaScript/TypeScript ```typescript class QuoterUploadClient { private baseUrl: string; private token: string; constructor(baseUrl: string, token: string) { this.baseUrl = baseUrl; this.token = token; } // Получение информации о пользователе async getUserInfo() { const response = await fetch(`${this.baseUrl}/`, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } // Загрузка одного файла async uploadFile(file: File): Promise { const formData = new FormData(); formData.append('file', file); const response = await fetch(`${this.baseUrl}/`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.token}` }, body: formData }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Upload failed: ${response.status} ${errorText}`); } return await response.text(); } // Загрузка нескольких файлов async uploadFiles(files: File[]): Promise<{uploaded_files: string[], count: number}> { const formData = new FormData(); files.forEach(file => formData.append('file', file)); const response = await fetch(`${this.baseUrl}/`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.token}` }, body: formData }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Upload failed: ${response.status} ${errorText}`); } return await response.json(); } // Получение файла async getFile(filename: string): Promise { const response = await fetch(`${this.baseUrl}/${filename}`, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (!response.ok) { throw new Error(`File not found: ${response.status}`); } return await response.blob(); } } // Использование const client = new QuoterUploadClient('https://files.dscrs.site', 'your-jwt-token'); // Получение информации о пользователе const userInfo = await client.getUserInfo(); console.log(`Квота: ${userInfo.quota.usage_percentage.toFixed(1)}%`); // Загрузка файла const fileInput = document.getElementById('fileInput') as HTMLInputElement; const file = fileInput.files[0]; const filename = await client.uploadFile(file); console.log(`Файл загружен: ${filename}`); ``` ### Python ```python import requests import json from typing import List, Dict, Any class QuoterUploadClient: def __init__(self, base_url: str, token: str): self.base_url = base_url.rstrip('/') self.token = token self.headers = {'Authorization': f'Bearer {token}'} def get_user_info(self) -> Dict[str, Any]: """Получение информации о пользователе""" response = requests.get(f'{self.base_url}/', headers=self.headers) response.raise_for_status() return response.json() def upload_file(self, file_path: str) -> str: """Загрузка одного файла""" with open(file_path, 'rb') as f: files = {'file': f} response = requests.post( f'{self.base_url}/', headers=self.headers, files=files ) response.raise_for_status() return response.text.strip() def upload_files(self, file_paths: List[str]) -> Dict[str, Any]: """Загрузка нескольких файлов""" files = [] for path in file_paths: files.append(('file', open(path, 'rb'))) try: response = requests.post( f'{self.base_url}/', headers=self.headers, files=files ) response.raise_for_status() return response.json() finally: # Закрываем все файлы for _, file_obj in files: file_obj.close() def get_file(self, filename: str, save_path: str = None) -> bytes: """Получение файла""" response = requests.get( f'{self.base_url}/{filename}', headers=self.headers ) response.raise_for_status() if save_path: with open(save_path, 'wb') as f: f.write(response.content) return response.content # Использование client = QuoterUploadClient('https://files.dscrs.site', 'your-jwt-token') # Получение информации о пользователе user_info = client.get_user_info() print(f"Квота: {user_info['quota']['usage_percentage']:.1f}%") # Загрузка файла filename = client.upload_file('/path/to/file.jpg') print(f"Файл загружен: {filename}") # Получение файла file_content = client.get_file(filename, '/path/to/downloaded_file.jpg') ``` ### cURL ```bash # Получение информации о пользователе curl -H "Authorization: Bearer your-jwt-token" \ https://files.dscrs.site/ # Загрузка одного файла curl -X POST \ -H "Authorization: Bearer your-jwt-token" \ -F "file=@/path/to/file.jpg" \ https://files.dscrs.site/ # Загрузка нескольких файлов curl -X POST \ -H "Authorization: Bearer your-jwt-token" \ -F "file=@/path/to/file1.jpg" \ -F "file=@/path/to/file2.mp3" \ https://files.dscrs.site/ # Получение файла curl -H "Authorization: Bearer your-jwt-token" \ -o downloaded_file.jpg \ https://files.dscrs.site/filename-uuid.jpg ``` ## 🚨 Обработка ошибок ### Типичные ошибки | Код | Описание | Решение | |-----|----------|---------| | `401` | Неверный токен | Проверить валидность JWT токена | | `413` | Превышена квота | Удалить старые файлы или увеличить квоту | | `413` | Файл слишком большой | Разделить файл на части | | `415` | Неподдерживаемый формат | Проверить MIME тип файла | | `500` | Ошибка сервера | Повторить запрос позже | ### Пример обработки ошибок ```typescript async function uploadWithRetry(client: QuoterUploadClient, file: File, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await client.uploadFile(file); } catch (error) { if (error.message.includes('413')) { throw new Error('Файл слишком большой или квота превышена'); } if (error.message.includes('401')) { throw new Error('Токен авторизации недействителен'); } if (attempt === maxRetries) { throw error; } // Ждем перед повторной попыткой await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } } } ``` ## 📋 Поддерживаемые форматы ### Изображения - JPEG, PNG, GIF, WebP, HEIC, BMP, TIFF ### Аудио - MP3, WAV, AAC, M4A, OGG, FLAC ### Видео - MP4, AVI, MOV, WebM, MKV ### Документы - PDF, DOC, DOCX, TXT, RTF ## 🔧 Конфигурация ### Environment Variables ```bash # Обязательные JWT_SECRET_KEY=your-jwt-secret-key REDIS_URL=redis://localhost:6379 STORJ_ACCESS_KEY=your-storj-access-key STORJ_SECRET_KEY=your-storj-secret-key STORJ_BUCKET=your-bucket-name # Опциональные PORT=8080 CORS_DOWNLOAD_ORIGINS=https://discours.io,https://*.discours.io ``` ### Лимиты ```rust // Максимальный размер одного файла const MAX_SINGLE_FILE_BYTES: u64 = 500 * 1024 * 1024; // 500 МБ // Максимальная квота пользователя const MAX_USER_QUOTA_BYTES: u64 = 12 * 1024 * 1024 * 1024; // 12 ГБ ``` ## 🧪 Тестирование ### Проверка подключения ```bash # Проверка доступности API curl -I https://files.dscrs.site/ # Проверка аутентификации curl -H "Authorization: Bearer your-token" \ https://files.dscrs.site/ ``` ### Тестовые файлы ```bash # Создание тестового файла echo "Test content" > test.txt # Загрузка тестового файла curl -X POST \ -H "Authorization: Bearer your-token" \ -F "file=@test.txt" \ https://files.dscrs.site/ ``` ## 📚 Дополнительные ресурсы - [API Reference](README.md) - [Setup Guide](SETUP.md) - [Features Overview](features.md) - [CORS Configuration](architecture.md) ## 🆘 Поддержка При возникновении проблем: 1. Проверьте валидность JWT токена 2. Убедитесь в правильности Content-Type 3. Проверьте размер файла и квоту пользователя 4. Изучите логи сервера для детальной диагностики --- **💡 Совет**: Используйте streaming загрузку для больших файлов и всегда проверяйте квоту пользователя перед загрузкой.