474 lines
13 KiB
Markdown
474 lines
13 KiB
Markdown
# 📤 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 <your-jwt-token>
|
||
```
|
||
|
||
### Формат токена
|
||
- JWT токен с claims: `{ user_id, username, exp?, iat? }`
|
||
- Минимальная длина: 100 символов
|
||
- Максимальная длина: 2048 символов
|
||
|
||
## 📊 Информация о пользователе
|
||
|
||
### GET /
|
||
|
||
Получает информацию о текущем пользователе и его квоте.
|
||
|
||
**Заголовки:**
|
||
```http
|
||
Authorization: Bearer <jwt-token>
|
||
```
|
||
|
||
**Ответ:**
|
||
```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 <jwt-token>
|
||
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<string> {
|
||
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<Blob> {
|
||
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=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 загрузку для больших файлов и всегда проверяйте квоту пользователя перед загрузкой.
|