Some checks failed
Deploy quoter Microservice on push / deploy (push) Failing after 39m16s
- 🔑 **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)]`
13 KiB
13 KiB
📤 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:
Authorization: Bearer <your-jwt-token>
Формат токена
- JWT токен с claims:
{ user_id, username, exp?, iat? } - Минимальная длина: 100 символов
- Максимальная длина: 2048 символов
📊 Информация о пользователе
GET /
Получает информацию о текущем пользователе и его квоте.
Заголовки:
Authorization: Bearer <jwt-token>
Ответ:
{
"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 /
Загружает один или несколько файлов.
Заголовки:
Authorization: Bearer <jwt-token>
Content-Type: multipart/form-data
Параметры:
file(multipart) - Файл для загрузки (можно несколько)
Лимиты:
- Максимальный размер одного файла: 500 МБ
- Максимальная квота пользователя: 12 ГБ
- Поддерживаемые форматы: изображения, аудио, видео, документы
Ответ (один файл):
filename-uuid.ext
Ответ (несколько файлов):
{
"uploaded_files": [
"file1-uuid.ext",
"file2-uuid.ext"
],
"count": 2
}
Коды ответов:
200- Файл(ы) успешно загружены400- Нет файлов или все файлы пустые401- Неверный токен авторизации413- Превышен лимит размера файла или квоты415- Неподдерживаемый формат файла500- Ошибка сервера
📁 Получение файлов
GET /{filename}
Получает загруженный файл.
Параметры:
filename- Имя файла (UUID + расширение)
Заголовки (опционально):
If-None-Match: "etag-value"
Range: bytes=0-1023
Ответ:
200- Файл найден304- Файл не изменился (если передан ETag)404- Файл не найден206- Частичный контент (если передан Range)
💻 Примеры кода
JavaScript/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
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
# Получение информации о пользователе
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 |
Ошибка сервера | Повторить запрос позже |
Пример обработки ошибок
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
# Обязательные
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
Лимиты
// Максимальный размер одного файла
const MAX_SINGLE_FILE_BYTES: u64 = 500 * 1024 * 1024; // 500 МБ
// Максимальная квота пользователя
const MAX_USER_QUOTA_BYTES: u64 = 12 * 1024 * 1024 * 1024; // 12 ГБ
🧪 Тестирование
Проверка подключения
# Проверка доступности API
curl -I https://files.dscrs.site/
# Проверка аутентификации
curl -H "Authorization: Bearer your-token" \
https://files.dscrs.site/
Тестовые файлы
# Создание тестового файла
echo "Test content" > test.txt
# Загрузка тестового файла
curl -X POST \
-H "Authorization: Bearer your-token" \
-F "file=@test.txt" \
https://files.dscrs.site/
📚 Дополнительные ресурсы
🆘 Поддержка
При возникновении проблем:
- Проверьте валидность JWT токена
- Убедитесь в правильности Content-Type
- Проверьте размер файла и квоту пользователя
- Изучите логи сервера для детальной диагностики
💡 Совет: Используйте streaming загрузку для больших файлов и всегда проверяйте квоту пользователя перед загрузкой.