Files
quoter/docs/upload-client-guide.md
Untone 5baba346e0
Some checks failed
Deploy quoter Microservice on push / deploy (push) Failing after 39m16s
;### Changed
- 🔑 **JWT_SECRET → JWT_SECRET_KEY**: Используется `JWT_SECRET_KEY` для совместимости с `@core`, `@inbox`, `@presence`
  - Fallback на `JWT_SECRET` для обратной совместимости
  - Обновлена документация: README.md, configuration.md
  - **BREAKING**: Требует установки `JWT_SECRET_KEY` в production (или использование legacy `JWT_SECRET`)

### Fixed (Tests & Code Quality)
- 🧪 **Удален мертвый код**: Removed unused mock functions and structs from tests
- 🔧 **Исправлены async тесты**: Changed `#[test]` → `#[tokio::test]` для async функций
- 🧹 **Чистые warnings**: Все тесты компилируются без warnings
- 📝 **Префиксы unused полей**: `_field` вместо `#[allow(dead_code)]`
2025-09-30 21:46:47 +03:00

474 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📤 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_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 загрузку для больших файлов и всегда проверяйте квоту пользователя перед загрузкой.