docs
Some checks failed
CI / test (push) Failing after 4m0s
CI / lint (push) Failing after 4s
CI / deploy (push) Has been skipped

This commit is contained in:
2025-08-02 00:18:09 +03:00
parent adda2b30f9
commit ea92a376ed
32 changed files with 3360 additions and 280 deletions

35
docs/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Документация Quoter
## 📚 Оглавление
### Основные разделы
- [Как это работает](./how-it-works.md) - Подробное описание архитектуры и процессов
- [API Reference](./api-reference.md) - Полная документация API
- [Развертывание](./deployment.md) - Инструкции по развертыванию
- [Конфигурация](./configuration.md) - Настройка переменных окружения
- [Мониторинг](./monitoring.md) - Логирование и мониторинг
### Технические детали
- [Архитектура](./architecture.md) - Техническая архитектура системы
- [База данных](./database.md) - Структура Redis и схемы данных
- [S3 интеграция](./s3-integration.md) - Работа с S3/Storj
- [Обработка изображений](./image-processing.md) - Создание миниатюр и оверлеев
- [Безопасность](./security.md) - Аутентификация и авторизация
### Разработка
- [Разработка](./development.md) - Настройка среды разработки
- [Тестирование](./testing.md) - Руководство по тестированию
- [Contributing](./contributing.md) - Руководство для контрибьюторов
## 🚀 Быстрый старт
1. Установите зависимости: `cargo build`
2. Настройте переменные окружения (см. [Конфигурация](./configuration.md))
3. Запустите сервер: `cargo run`
4. Проверьте API: `curl http://localhost:8080/`
## 📋 Требования
- Rust 1.70+
- Redis 6.0+
- Доступ к S3/Storj API

169
docs/api-reference.md Normal file
View File

@@ -0,0 +1,169 @@
# API Reference
## Обзор
Quoter предоставляет REST API для загрузки файлов, управления квотами и получения файлов с автоматической генерацией миниатюр.
## Базовый URL
```
http://localhost:8080
```
## Аутентификация
Все API endpoints (кроме получения файлов) требуют аутентификации через заголовок `Authorization`:
```
Authorization: Bearer <your-jwt-token>
```
## Endpoints
### 1. Проверка состояния сервера
#### GET /
Проверяет работоспособность сервера.
**Ответ:**
```
200 OK
ok
```
### 2. Загрузка файлов
#### POST /
Загружает файл в S3 хранилище.
**Заголовки:**
```
Authorization: Bearer <token>
Content-Type: multipart/form-data
```
**Параметры:**
- `file` - файл для загрузки
**Ответ:**
```
200 OK
filename.ext
```
**Ошибки:**
- `401 Unauthorized` - неверный токен
- `413 Payload Too Large` - превышена квота
- `415 Unsupported Media Type` - неподдерживаемый тип файла
### 3. Получение файлов
#### GET /{filename}
Получает файл по имени.
**Параметры запроса:**
- `s=<shout_id>` - добавляет оверлей с данными shout (только для изображений)
**Примеры:**
```
GET /image.jpg
GET /image.jpg?s=123
GET /image_300.jpg
GET /image_300.jpg/webp
```
### 4. Управление квотами
#### GET /quota
Получает информацию о квоте пользователя.
**Параметры запроса:**
- `user_id` - ID пользователя
**Пример:**
```
GET /quota?user_id=user123
```
**Ответ:**
```json
{
"user_id": "user123",
"current_quota": 1073741824,
"max_quota": 5368709120
}
```
#### POST /quota/increase
Увеличивает квоту пользователя.
**Тело запроса:**
```json
{
"user_id": "user123",
"additional_bytes": 1073741824
}
```
**Ответ:**
```json
{
"user_id": "user123",
"current_quota": 2147483648,
"max_quota": 5368709120
}
```
#### POST /quota/set
Устанавливает квоту пользователя.
**Тело запроса:**
```json
{
"user_id": "user123",
"new_quota_bytes": 2147483648
}
```
**Ответ:**
```json
{
"user_id": "user123",
"current_quota": 2147483648,
"max_quota": 5368709120
}
```
## Коды ошибок
| Код | Описание |
|-----|----------|
| 200 | Успешный запрос |
| 400 | Неверные параметры запроса |
| 401 | Неавторизованный доступ |
| 404 | Файл не найден |
| 413 | Превышена квота |
| 415 | Неподдерживаемый тип файла |
| 500 | Внутренняя ошибка сервера |
## Примеры использования
### Загрузка изображения
```bash
curl -X POST http://localhost:8080/ \
-H "Authorization: Bearer your-token" \
-F "file=@image.jpg"
```
### Получение миниатюры
```bash
curl http://localhost:8080/image_300.jpg
```
### Увеличение квоты
```bash
curl -X POST http://localhost:8080/quota/increase \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"user_id": "user123", "additional_bytes": 1073741824}'
```

196
docs/architecture.md Normal file
View File

@@ -0,0 +1,196 @@
# Архитектура
## Обзор системы
Quoter - это микросервис для управления файлами с поддержкой квот, миниатюр и интеграции с S3 хранилищами.
## Компоненты системы
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Client │ │ Mobile App │ │ API Client │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ │
└──────────────────────┼──────────────────────┘
┌─────────────▼─────────────┐
│ Quoter Service │
│ (Actix Web Server) │
└─────────────┬─────────────┘
┌───────────────────────┼───────────────────────┐
│ │ │
┌─────▼─────┐ ┌───────▼──────┐ ┌─────▼─────┐
│ Redis │ │ Core API │ │ S3/Storj│
│ (Quotas & │ │(Auth & Shout)│ │ (Storage) │
│ Cache) │ │ │ │ │
└───────────┘ └──────────────┘ └───────────┘
```
## Основные модули
### 1. AppState (`src/app_state.rs`)
Центральное состояние приложения, содержащее:
- Подключения к Redis, Storj S3, AWS S3
- Методы для работы с квотами
- Кэширование списка файлов
### 2. Handlers (`src/handlers/`)
Обработчики HTTP запросов:
- `upload.rs` - загрузка файлов
- `proxy.rs` - получение файлов и генерация миниатюр
- `quota.rs` - управление квотами
- `serve_file.rs` - обслуживание файлов
### 3. Auth (`src/auth.rs`)
Аутентификация через GraphQL API ядра:
- Валидация JWT токенов
- Управление файлами пользователей в Redis
### 4. Core (`src/core.rs`)
Интеграция с API ядра:
- Получение данных shout для оверлеев
### 5. Thumbnail (`src/thumbnail.rs`)
Обработка изображений:
- Создание миниатюр различных размеров
- Поддержка различных форматов
### 6. S3 Utils (`src/s3_utils.rs`)
Утилиты для работы с S3:
- Загрузка/скачивание файлов
- Определение MIME типов
- Генерация ключей
## Поток данных
### Загрузка файла
```mermaid
sequenceDiagram
participant Client
participant Quoter
participant Core API
participant Redis
participant S3
Client->>Quoter: POST / (file + token)
Quoter->>Core API: Validate token
Core API-->>Quoter: User ID
Quoter->>Redis: Check quota
Redis-->>Quoter: Current quota
Quoter->>S3: Upload file
S3-->>Quoter: Success
Quoter->>Redis: Update quota
Quoter->>Redis: Save file info
Quoter-->>Client: Filename
```
### Получение файла
```mermaid
sequenceDiagram
participant Client
participant Quoter
participant Redis
participant S3
Client->>Quoter: GET /filename
Quoter->>Redis: Get file path
Redis-->>Quoter: File path
Quoter->>S3: Check file exists
S3-->>Quoter: File exists
Quoter->>S3: Download file
S3-->>Quoter: File data
Quoter-->>Client: File content
```
## Структура данных
### Redis схемы
#### Квоты пользователей
```
Key: quota:{user_id}
Type: String
Value: bytes_used (u64)
TTL: None (permanent)
```
#### Файлы пользователей
```
Key: {user_id}
Type: Set
Value: [filename1, filename2, ...]
TTL: None (permanent)
```
#### Маппинг путей файлов
```
Key: filepath_mapping
Type: Hash
Field: filename
Value: filepath
TTL: None (permanent)
```
#### Информация о файлах
```
Key: files:{filename}
Type: String
Value: mime_type
TTL: None (permanent)
```
### S3 структура
```
bucket/
├── original_files/
│ ├── image1.jpg
│ ├── image2.png
│ └── document.pdf
├── thumbnails/
│ ├── image1_10.jpg
│ ├── image1_40.jpg
│ ├── image1_110.jpg
│ ├── image1_300.jpg
│ ├── image1_600.jpg
│ ├── image1_800.jpg
│ └── image1_1400.jpg
└── webp_thumbnails/
├── image1_10.jpg/webp
├── image1_40.jpg/webp
└── ...
```
## Безопасность
### Аутентификация
- JWT токены через GraphQL API ядра
- Валидация токенов для всех операций записи
### Авторизация
- Проверка квот перед загрузкой
- Изоляция файлов по пользователям
### CORS
- Настроен для кросс-доменных запросов
- Поддержка credentials
## Масштабирование
### Горизонтальное масштабирование
- Stateless архитектура
- Redis как общее хранилище состояния
- S3 как общее файловое хранилище
### Производительность
- Асинхронная обработка запросов
- Кэширование списка файлов
- Ленивая генерация миниатюр
### Мониторинг
- Структурированное логирование
- Метрики использования квот
- Отслеживание ошибок

137
docs/configuration.md Normal file
View File

@@ -0,0 +1,137 @@
# Конфигурация
## Переменные окружения
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` |
### Опциональные переменные
| Переменная | Описание | По умолчанию |
|------------|----------|--------------|
| `PORT` | Порт для запуска сервера | `8080` |
| `STORJ_END_POINT` | Endpoint Storj S3 | `https://gateway.storjshare.io` |
| `STORJ_BUCKET_NAME` | Имя bucket в Storj | `discours-io` |
| `AWS_END_POINT` | Endpoint AWS S3 | `https://s3.amazonaws.com` |
| `RUST_LOG` | Уровень логирования | `info` |
## Пример .env файла
```bash
# Redis
REDIS_URL=redis://localhost:6379
# Core API
CORE_URL=https://api.example.com/graphql
# 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_ACCESS_KEY=your-aws-access-key
AWS_SECRET_KEY=your-aws-secret-key
AWS_END_POINT=https://s3.amazonaws.com
# Server
PORT=8080
RUST_LOG=info
```
## Настройка Redis
### Минимальная конфигурация Redis
```redis
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000
```
### Проверка подключения
```bash
redis-cli ping
```
## Настройка S3
### Storj S3
1. Создайте аккаунт на [Storj](https://storj.io)
2. Создайте API ключи в консоли
3. Создайте bucket для файлов
4. Настройте CORS для bucket:
```json
{
"CORSRules": [
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "POST", "PUT", "DELETE"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"]
}
]
}
```
### AWS S3
1. Создайте IAM пользователя с правами S3
2. Создайте bucket для файлов
3. Настройте CORS аналогично Storj
## Логирование
### Уровни логирования
- `error` - только ошибки
- `warn` - предупреждения и ошибки
- `info` - информационные сообщения, предупреждения и ошибки
- `debug` - отладочная информация
- `trace` - максимальная детализация
### Примеры
```bash
# Только ошибки
RUST_LOG=error cargo run
# Информационные сообщения
RUST_LOG=info cargo run
# Отладка
RUST_LOG=debug cargo run
```
## Проверка конфигурации
Запустите сервер и проверьте логи:
```bash
RUST_LOG=info cargo run
```
Успешный запуск должен показать:
```
[INFO] Started
[WARN] caching AWS filelist...
[WARN] cached 1234 files
```

292
docs/contributing.md Normal file
View File

@@ -0,0 +1,292 @@
# Contributing
## Спасибо за интерес к Quoter! 🎉
Мы приветствуем вклад от сообщества. Этот документ содержит руководство по участию в разработке проекта.
## Как внести свой вклад
### 1. Сообщить о баге
Если вы нашли баг, создайте issue с:
- **Кратким описанием** проблемы
- **Шагами для воспроизведения**
- **Ожидаемым и фактическим поведением**
- **Версией** Rust, Redis, и других зависимостей
- **Логами** (если применимо)
### 2. Предложить новую функциональность
Для предложения новой функциональности:
- Опишите проблему, которую решает ваше предложение
- Предложите решение
- Обсудите альтернативы
- Укажите приоритет
### 3. Внести код
#### Подготовка
1. **Fork** репозиторий
2. **Clone** ваш fork локально
3. Создайте **feature branch**:
```bash
git checkout -b feature/amazing-feature
```
#### Разработка
1. **Следуйте стандартам кода**:
```bash
cargo fmt
cargo clippy
```
2. **Добавьте тесты** для новой функциональности
3. **Обновите документацию** если необходимо
4. **Проверьте сборку**:
```bash
cargo build
cargo test
```
#### Commit и Push
1. **Создайте commit** с описательным сообщением:
```bash
git commit -m "feat: add amazing feature"
```
2. **Push** в ваш fork:
```bash
git push origin feature/amazing-feature
```
3. **Создайте Pull Request**
## Стандарты кода
### Rust
- Следуйте [Rust Style Guide](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html)
- Используйте `cargo fmt` для форматирования
- Используйте `cargo clippy` для проверки стиля
- Документируйте публичные API
### Commit Messages
Используйте [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
Типы:
- `feat:` - новая функциональность
- `fix:` - исправление бага
- `docs:` - изменения в документации
- `style:` - форматирование кода
- `refactor:` - рефакторинг
- `test:` - добавление тестов
- `chore:` - обновление зависимостей
Примеры:
```
feat: add user quota management API
fix(auth): handle expired tokens properly
docs: update API documentation
style: format code with cargo fmt
```
### Тестирование
- **Unit тесты** для всех новых функций
- **Интеграционные тесты** для API endpoints
- **Тесты производительности** для критических участков
- Минимальное покрытие кода: **80%**
### Документация
- Обновляйте README.md если необходимо
- Добавляйте комментарии к сложному коду
- Документируйте API изменения
- Обновляйте примеры использования
## Процесс Pull Request
### Создание PR
1. **Заполните шаблон** Pull Request
2. **Опишите изменения** подробно
3. **Укажите связанные issues**
4. **Добавьте скриншоты** если применимо
### Code Review
- **Два approval** требуются для merge
- **CI/CD** должен пройти успешно
- **Code coverage** не должен уменьшиться
- **Безопасность** проверяется автоматически
### После Merge
- **Feature branch** удаляется автоматически
- **Release** создается для значительных изменений
- **Документация** обновляется
## Настройка среды разработки
### Требования
- Rust 1.70+
- Redis 6.0+
- Git
### Установка
```bash
# Fork и clone
git clone https://github.com/YOUR_USERNAME/quoter.git
cd quoter
# Установка зависимостей
cargo build
# Настройка pre-commit hooks
cargo install cargo-husky
cargo husky install
```
### Локальная разработка
```bash
# Запуск Redis
docker run -d -p 6379:6379 redis:7-alpine
# Настройка переменных окружения
cp .env.example .env
# Отредактируйте .env
# Запуск приложения
cargo run
# Запуск тестов
cargo test
```
## Структура проекта
```
quoter/
├── src/ # Исходный код
│ ├── main.rs # Точка входа
│ ├── app_state.rs # Состояние приложения
│ ├── auth.rs # Аутентификация
│ ├── core.rs # API ядра
│ ├── handlers/ # HTTP обработчики
│ ├── lookup.rs # Поиск файлов
│ ├── overlay.rs # Оверлеи
│ ├── s3_utils.rs # S3 утилиты
│ └── thumbnail.rs # Миниатюры
├── docs/ # Документация
├── tests/ # Интеграционные тесты
├── Cargo.toml # Зависимости
└── README.md # Основная документация
```
## Роли в проекте
### Maintainers
- **Code review** всех PR
- **Release management**
- **Architecture decisions**
- **Community management**
### Contributors
- **Feature development**
- **Bug fixes**
- **Documentation**
- **Testing**
### Reviewers
- **Code review** assigned PRs
- **Quality assurance**
- **Performance review**
## Коммуникация
### Issues
- Используйте **labels** для категоризации
- **Assign** issues к себе если работаете над ними
- **Update** статус регулярно
### Discussions
- **GitHub Discussions** для общих вопросов
- **RFC** для значительных изменений
- **Architecture** для архитектурных решений
### Code Review
- **Будьте конструктивными**
- **Объясняйте причины** изменений
- **Предлагайте альтернативы**
- **Отвечайте на комментарии**
## Безопасность
### Отчеты о уязвимостях
Для критических уязвимостей:
1. **НЕ создавайте публичный issue**
2. **Отправьте email** на security@example.com
3. **Опишите уязвимость** подробно
4. **Предложите решение** если возможно
### Безопасность кода
- **Не коммитьте секреты**
- **Валидируйте входные данные**
- **Используйте безопасные зависимости**
- **Проверяйте код на уязвимости**
## Лицензия
Внося код в проект, вы соглашаетесь с тем, что ваш вклад будет лицензирован под MIT License.
## Благодарности
Спасибо всем контрибьюторам, которые помогают сделать Quoter лучше! 🙏
### Способы поддержки
- **Code contributions**
- **Bug reports**
- **Feature requests**
- **Documentation improvements**
- **Community support**
- **Financial support** (если применимо)
## Контакты
- **Issues**: [GitHub Issues](https://github.com/your-org/quoter/issues)
- **Discussions**: [GitHub Discussions](https://github.com/your-org/quoter/discussions)
- **Email**: maintainers@example.com
- **Chat**: [Discord/Slack] (если есть)
---
**Спасибо за ваш вклад в Quoter!** 🚀

318
docs/deployment.md Normal file
View File

@@ -0,0 +1,318 @@
# Развертывание
## Обзор
Quoter можно развернуть различными способами в зависимости от ваших потребностей и инфраструктуры.
## Способы развертывания
### 1. Docker (Рекомендуется)
#### Сборка образа
```bash
# Сборка production образа
docker build -t quoter:latest .
# Сборка с тегами
docker build -t quoter:v1.0.0 .
```
#### Запуск контейнера
```bash
docker run -d \
--name quoter \
-p 8080:8080 \
-e REDIS_URL=redis://redis:6379 \
-e CORE_URL=https://api.example.com/graphql \
-e STORJ_ACCESS_KEY=your-key \
-e STORJ_SECRET_KEY=your-secret \
-e AWS_ACCESS_KEY=your-aws-key \
-e AWS_SECRET_KEY=your-aws-secret \
quoter:latest
```
#### Docker Compose
Создайте `docker-compose.yml`:
```yaml
version: '3.8'
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
quoter:
build: .
ports:
- "8080:8080"
environment:
- REDIS_URL=redis://redis:6379
- CORE_URL=https://api.example.com/graphql
- STORJ_ACCESS_KEY=${STORJ_ACCESS_KEY}
- STORJ_SECRET_KEY=${STORJ_SECRET_KEY}
- AWS_ACCESS_KEY=${AWS_ACCESS_KEY}
- AWS_SECRET_KEY=${AWS_SECRET_KEY}
- RUST_LOG=info
depends_on:
- redis
restart: unless-stopped
volumes:
redis_data:
```
Запуск:
```bash
docker-compose up -d
```
### 2. Kubernetes
#### Deployment
Создайте `k8s/deployment.yaml`:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: quoter
labels:
app: quoter
spec:
replicas: 3
selector:
matchLabels:
app: quoter
template:
metadata:
labels:
app: quoter
spec:
containers:
- name: quoter
image: quoter:latest
ports:
- containerPort: 8080
env:
- name: REDIS_URL
value: "redis://redis-service:6379"
- name: CORE_URL
value: "https://api.example.com/graphql"
- name: STORJ_ACCESS_KEY
valueFrom:
secretKeyRef:
name: quoter-secrets
key: storj-access-key
- name: STORJ_SECRET_KEY
valueFrom:
secretKeyRef:
name: quoter-secrets
key: storj-secret-key
- name: AWS_ACCESS_KEY
valueFrom:
secretKeyRef:
name: quoter-secrets
key: aws-access-key
- name: AWS_SECRET_KEY
valueFrom:
secretKeyRef:
name: quoter-secrets
key: aws-secret-key
- name: RUST_LOG
value: "info"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
```
#### Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: quoter-service
spec:
selector:
app: quoter
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
```
#### Secrets
```yaml
apiVersion: v1
kind: Secret
metadata:
name: quoter-secrets
type: Opaque
data:
storj-access-key: <base64-encoded-key>
storj-secret-key: <base64-encoded-secret>
aws-access-key: <base64-encoded-key>
aws-secret-key: <base64-encoded-secret>
```
### 3. Systemd (Linux)
#### Создание сервиса
Создайте `/etc/systemd/system/quoter.service`:
```ini
[Unit]
Description=Quoter File Service
After=network.target redis.service
[Service]
Type=simple
User=quoter
Group=quoter
WorkingDirectory=/opt/quoter
Environment=REDIS_URL=redis://localhost:6379
Environment=CORE_URL=https://api.example.com/graphql
Environment=STORJ_ACCESS_KEY=your-key
Environment=STORJ_SECRET_KEY=your-secret
Environment=AWS_ACCESS_KEY=your-aws-key
Environment=AWS_SECRET_KEY=your-aws-secret
Environment=RUST_LOG=info
ExecStart=/opt/quoter/quoter
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
#### Управление сервисом
```bash
# Создание пользователя
sudo useradd -r -s /bin/false quoter
# Копирование бинарного файла
sudo cp target/release/quoter /opt/quoter/
sudo chown quoter:quoter /opt/quoter/quoter
# Включение и запуск сервиса
sudo systemctl daemon-reload
sudo systemctl enable quoter
sudo systemctl start quoter
# Проверка статуса
sudo systemctl status quoter
```
## Мониторинг и логирование
### Prometheus метрики
Добавьте в `Cargo.toml`:
```toml
[dependencies]
prometheus = "0.13"
actix-web-prom = "0.6"
```
### Grafana дашборд
Создайте дашборд для мониторинга:
- Количество запросов в секунду
- Время ответа API
- Использование памяти и CPU
- Ошибки по типам
- Использование квот
### Логирование
#### Структурированные логи
```bash
# JSON формат для ELK stack
RUST_LOG=info cargo run | jq .
```
#### Ротация логов
Настройте logrotate:
```
/var/log/quoter/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 644 quoter quoter
postrotate
systemctl reload quoter
endscript
}
```
## Масштабирование
### Горизонтальное масштабирование
1. **Load Balancer**: Настройте nginx или HAProxy
2. **Redis Cluster**: Для высоких нагрузок
3. **S3 CDN**: Для статических файлов
### Вертикальное масштабирование
- Увеличьте ресурсы контейнера/сервера
- Настройте пул соединений Redis
- Оптимизируйте размер изображений
## Безопасность
### Сетевая безопасность
- Используйте HTTPS в продакшене
- Настройте firewall
- Ограничьте доступ к Redis
### Секреты
- Используйте Kubernetes Secrets или Docker Secrets
- Не храните секреты в коде
- Ротация ключей доступа
### Аудит
- Логируйте все операции с файлами
- Отслеживайте использование квот
- Мониторьте подозрительную активность

422
docs/development.md Normal file
View File

@@ -0,0 +1,422 @@
# Разработка
## Настройка среды разработки
### Требования
- **Rust**: 1.70 или выше
- **Redis**: 6.0 или выше (локально или Docker)
- **Git**: для работы с репозиторием
- **IDE**: VS Code, IntelliJ IDEA или другой редактор с поддержкой Rust
### Установка Rust
```bash
# Установка Rust через rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Перезагрузка shell
source ~/.bashrc
# Проверка установки
rustc --version
cargo --version
```
### Клонирование репозитория
```bash
git clone https://github.com/your-org/quoter.git
cd quoter
```
### Установка зависимостей
```bash
# Сборка проекта
cargo build
# Установка дополнительных инструментов
cargo install cargo-watch # для автоматической пересборки
cargo install cargo-audit # для проверки безопасности
cargo install cargo-tarpaulin # для покрытия кода тестами
```
## Структура проекта
```
quoter/
├── src/
│ ├── main.rs # Точка входа приложения
│ ├── app_state.rs # Состояние приложения и подключения
│ ├── auth.rs # Аутентификация и авторизация
│ ├── core.rs # Интеграция с API ядра
│ ├── lookup.rs # Поиск и определение MIME-типов
│ ├── overlay.rs # Генерация оверлеев для изображений
│ ├── s3_utils.rs # Утилиты для работы с S3
│ ├── thumbnail.rs # Создание миниатюр
│ └── handlers/ # HTTP обработчики
│ ├── mod.rs # Модуль обработчиков
│ ├── upload.rs # Загрузка файлов
│ ├── proxy.rs # Получение файлов
│ ├── quota.rs # Управление квотами
│ └── serve_file.rs # Обслуживание файлов
├── docs/ # Документация
├── tests/ # Интеграционные тесты
├── Cargo.toml # Зависимости и конфигурация
├── Cargo.lock # Фиксированные версии зависимостей
├── Dockerfile # Docker образ
└── README.md # Основная документация
```
## Локальная разработка
### Настройка переменных окружения
Создайте файл `.env` в корне проекта:
```bash
# Redis (локально или Docker)
REDIS_URL=redis://localhost:6379
# Core API (замените на ваш endpoint)
CORE_URL=https://api.example.com/graphql
# Storj S3 (тестовые ключи)
STORJ_ACCESS_KEY=your-test-key
STORJ_SECRET_KEY=your-test-secret
STORJ_BUCKET_NAME=test-bucket
# AWS S3 (тестовые ключи)
AWS_ACCESS_KEY=your-test-aws-key
AWS_SECRET_KEY=your-test-aws-secret
# Server
PORT=8080
RUST_LOG=debug
```
### Запуск Redis
#### Локально
```bash
# Ubuntu/Debian
sudo apt-get install redis-server
sudo systemctl start redis-server
# macOS
brew install redis
brew services start redis
# Проверка
redis-cli ping
```
#### Docker
```bash
docker run -d \
--name redis-dev \
-p 6379:6379 \
redis:7-alpine
```
### Запуск приложения
```bash
# Обычный запуск
cargo run
# С автоматической пересборкой
cargo watch -x run
# В режиме отладки
RUST_LOG=debug cargo run
# С профилированием
cargo run --release
```
### Проверка работоспособности
```bash
# Проверка сервера
curl http://localhost:8080/
# Проверка загрузки файла (требует токен)
curl -X POST http://localhost:8080/ \
-H "Authorization: Bearer your-token" \
-F "file=@test-image.jpg"
```
## Тестирование
### Unit тесты
```bash
# Запуск всех тестов
cargo test
# Запуск тестов с выводом
cargo test -- --nocapture
# Запуск конкретного теста
cargo test test_upload_file
# Запуск тестов в параллельном режиме
cargo test -- --test-threads=4
```
### Интеграционные тесты
Создайте файл `tests/integration_test.rs`:
```rust
use actix_web::{test, web, App};
use quoter::app_state::AppState;
#[actix_web::test]
async fn test_upload_endpoint() {
let app_state = AppState::new().await;
let app = test::init_service(
App::new()
.app_data(web::Data::new(app_state))
.route("/", web::post().to(upload_handler))
).await;
let req = test::TestRequest::post()
.uri("/")
.insert_header(("Authorization", "Bearer test-token"))
.set_form(("file", "test-data"))
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
}
```
### Тестирование производительности
```bash
# Бенчмарки (если настроены)
cargo bench
# Профилирование с flamegraph
cargo install flamegraph
cargo flamegraph --bin quoter
```
## Отладка
### Логирование
```rust
use log::{debug, info, warn, error};
// В коде
debug!("Processing file: {}", filename);
info!("File uploaded successfully");
warn!("User quota is getting low: {} bytes", quota);
error!("Failed to upload file: {}", e);
```
### Отладка с GDB
```bash
# Компиляция с отладочной информацией
cargo build
# Запуск с GDB
gdb target/debug/quoter
# В GDB
(gdb) break main
(gdb) run
(gdb) continue
```
### Отладка с LLDB (macOS)
```bash
lldb target/debug/quoter
(lldb) breakpoint set --name main
(lldb) run
```
## Проверка кода
### Clippy
```bash
# Проверка стиля кода
cargo clippy
# Проверка с дополнительными предупреждениями
cargo clippy -- -D warnings
# Автоматическое исправление
cargo clippy --fix
```
### Форматирование
```bash
# Форматирование кода
cargo fmt
# Проверка форматирования
cargo fmt -- --check
```
### Проверка безопасности
```bash
# Аудит зависимостей
cargo audit
# Проверка уязвимостей
cargo audit --deny warnings
```
## Покрытие кода
### Tarpaulin
```bash
# Установка
cargo install cargo-tarpaulin
# Запуск
cargo tarpaulin
# С HTML отчетом
cargo tarpaulin --out Html
```
### grcov
```bash
# Установка
cargo install grcov
# Настройка переменных
export CARGO_INCREMENTAL=0
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"
export RUSTDOCFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"
# Запуск тестов
cargo test
# Генерация отчета
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing -o ./coverage/
```
## Git workflow
### Создание feature branch
```bash
# Создание новой ветки
git checkout -b feature/new-feature
# Внесение изменений
# ...
# Коммит изменений
git add .
git commit -m "feat: add new feature"
# Push в репозиторий
git push origin feature/new-feature
```
### Commit conventions
Используйте [Conventional Commits](https://www.conventionalcommits.org/):
- `feat:` - новая функциональность
- `fix:` - исправление багов
- `docs:` - изменения в документации
- `style:` - форматирование кода
- `refactor:` - рефакторинг кода
- `test:` - добавление тестов
- `chore:` - обновление зависимостей, конфигурации
### Pull Request
1. Создайте Pull Request в GitHub/GitLab
2. Добавьте описание изменений
3. Укажите связанные issues
4. Дождитесь code review
5. Исправьте замечания если есть
6. Получите approval и merge
## Полезные команды
### Cargo
```bash
# Обновление зависимостей
cargo update
# Очистка сборки
cargo clean
# Проверка зависимостей
cargo tree
# Документация
cargo doc --open
# Проверка типов без компиляции
cargo check
```
### Отладка
```bash
# Просмотр логов в реальном времени
tail -f logs/quoter.log
# Мониторинг ресурсов
htop
iotop
# Сетевые соединения
netstat -tulpn | grep 8080
```
### Docker
```bash
# Сборка для разработки
docker build -t quoter:dev .
# Запуск с volume для hot reload
docker run -v $(pwd):/app -p 8080:8080 quoter:dev
# Просмотр логов контейнера
docker logs -f quoter-container
```
## Рекомендации
### Производительность
1. Используйте `cargo build --release` для production
2. Настройте профилирование для критических участков
3. Мониторьте использование памяти и CPU
4. Оптимизируйте размер изображений
### Безопасность
1. Регулярно обновляйте зависимости
2. Используйте `cargo audit` для проверки уязвимостей
3. Не храните секреты в коде
4. Валидируйте все входные данные
### Качество кода
1. Пишите тесты для новой функциональности
2. Используйте `cargo clippy` для проверки стиля
3. Документируйте публичные API
4. Следуйте принципам SOLID

25
docs/how-it-works.md Normal file
View File

@@ -0,0 +1,25 @@
### Как это работает
1. **Аутентификация**:
- Клиент отправляет файл на сервер с заголовком `Authorization`, содержащим токен. Сервер проверяет наличие и валидность токена через API ядра, определяя пользователя.
2. **Загрузка файлов**:
- Сервер обрабатывает все загружаемые файлы. Если файл является изображением, создается его миниатюра. И миниатюра, и оригинальное изображение загружаются в S3. Для остальных файлов выполняется простая загрузка в S3 без создания миниатюр.
3. **Создание миниатюр**:
- Для всех загружаемых изображений сервер автоматически создает миниатюры различных размеров (10, 40, 110, 300, 600, 800, 1400 пикселей по ширине). Миниатюры сохраняются как отдельные файлы в том же S3 bucket, что и оригинальные изображения.
4. **Определение MIME-типа и расширения файла**:
- MIME-тип и расширение файла определяются автоматически на основе имени файла и его содержимого с использованием библиотеки `mime_guess`.
5. **Загрузка файлов в S3**:
- Все файлы, включая миниатюры и оригиналы изображений, загружаются в указанный S3 bucket. Сформированные URL-адреса файлов возвращаются клиенту.
6. **Управление квотами**:
- Для каждого пользователя устанавливается общая квота на загрузку данных, которая составляет 5 ГБ. Перед загрузкой каждого нового файла проверяется, не превысит ли его размер текущую квоту пользователя. Если квота будет превышена, загрузка файла будет отклонена. После успешной загрузки файл и его размер регистрируются в Redis, и квота пользователя обновляется.
7. **Сохранение информации о загруженных файлах в Redis**:
- Имя каждого загруженного файла сохраняется в Redis для отслеживания загруженных пользователем файлов. Это позволяет учитывать квоты и управлять пространством, занимаемым файлами.
8. **Оверлей для shout**:
- При загрузке файла, если он является изображением, и в запросе присутствует параметр `s=<shout_id>`, то к файлу будет добавлен оверлей с данными shout.

341
docs/monitoring.md Normal file
View File

@@ -0,0 +1,341 @@
# Мониторинг
## Обзор
Мониторинг Quoter включает в себя логирование, метрики производительности и отслеживание состояния системы.
## Логирование
### Уровни логирования
Quoter использует библиотеку `log` с различными уровнями:
- **error** - Критические ошибки, требующие немедленного внимания
- **warn** - Предупреждения, которые могут указывать на проблемы
- **info** - Информационные сообщения о нормальной работе
- **debug** - Отладочная информация для разработчиков
- **trace** - Максимальная детализация для глубокой отладки
### Настройка логирования
```bash
# Только ошибки
RUST_LOG=error cargo run
# Предупреждения и ошибки
RUST_LOG=warn cargo run
# Информационные сообщения (рекомендуется для продакшена)
RUST_LOG=info cargo run
# Отладка
RUST_LOG=debug cargo run
# Максимальная детализация
RUST_LOG=trace cargo run
```
### Структура логов
#### Загрузка файла
```
[INFO] Started
[WARN] file abc123.jpg uploaded to storj, incrementing quota by 1048576 bytes
[WARN] New quota for user user123: 2097152 bytes
```
#### Получение файла
```
[WARN] >>> GET image_300.jpg [START]
[WARN] detected file extension: jpg
[WARN] base_filename: image
[WARN] requested width: 300
[WARN] Found stored path in DB: production/image/image.jpg
[WARN] File exists in Storj: production/image/image.jpg
[WARN] Processing image file with width: 300
[WARN] Calculated closest width: 300 for requested: 300
[WARN] serve existed thumb file: image_300.jpg
```
#### Ошибки
```
[ERROR] Failed to upload to Storj: image.jpg - Error: Network error
[ERROR] Database error while getting path: image.jpg - Full error: Connection timeout
[ERROR] unsupported file format
```
## Метрики
### Основные метрики для мониторинга
#### Производительность
- **Requests per second (RPS)** - количество запросов в секунду
- **Response time** - время ответа API
- **Error rate** - процент ошибок
- **Upload success rate** - процент успешных загрузок
#### Ресурсы
- **Memory usage** - использование памяти
- **CPU usage** - использование процессора
- **Disk I/O** - операции с диском
- **Network I/O** - сетевой трафик
#### Бизнес-метрики
- **Files uploaded** - количество загруженных файлов
- **Quota usage** - использование квот пользователями
- **Thumbnail generation** - количество сгенерированных миниатюр
- **Storage usage** - использование хранилища
### Prometheus метрики
Добавьте в `Cargo.toml`:
```toml
[dependencies]
prometheus = "0.13"
actix-web-prom = "0.6"
```
Настройте в `main.rs`:
```rust
use actix_web_prom::{PrometheusMetrics, PrometheusMetricsBuilder};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let prometheus = PrometheusMetricsBuilder::new("quoter")
.endpoint("/metrics")
.build()
.unwrap();
HttpServer::new(move || {
App::new()
.wrap(prometheus.clone())
// ... остальные настройки
})
.bind(addr)?
.run()
.await
}
```
### Кастомные метрики
```rust
use prometheus::{Counter, Histogram, Registry};
lazy_static! {
pub static ref UPLOAD_COUNTER: Counter = Counter::new(
"quoter_uploads_total",
"Total number of file uploads"
).unwrap();
pub static ref UPLOAD_SIZE: Histogram = Histogram::new(
"quoter_upload_size_bytes",
"File upload size in bytes"
).unwrap();
pub static ref QUOTA_USAGE: Histogram = Histogram::new(
"quoter_quota_usage_bytes",
"User quota usage in bytes"
).unwrap();
}
```
## Алерты
### Критические алерты
- **Service down** - сервис недоступен
- **High error rate** - высокий процент ошибок (>5%)
- **High response time** - медленные ответы (>2s)
- **Memory usage high** - высокое использование памяти (>80%)
- **Redis connection failed** - потеря соединения с Redis
### Предупреждения
- **Quota usage high** - пользователи приближаются к лимиту квоты
- **Storage usage high** - высокое использование хранилища
- **Thumbnail generation slow** - медленная генерация миниатюр
### Настройка алертов в Prometheus
```yaml
groups:
- name: quoter
rules:
- alert: QuoterServiceDown
expr: up{job="quoter"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Quoter service is down"
- alert: HighErrorRate
expr: rate(http_requests_total{job="quoter",status=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "High error rate detected"
- alert: HighResponseTime
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{job="quoter"}[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "High response time detected"
```
## Дашборды
### Grafana дашборд
Создайте дашборд с панелями:
#### Обзор системы
- Статус сервиса (up/down)
- Количество запросов в секунду
- Время ответа (p50, p95, p99)
- Процент ошибок
#### Загрузка файлов
- Количество загрузок в час/день
- Размер загружаемых файлов
- Успешность загрузок
- Использование квот
#### Ресурсы
- Использование CPU и памяти
- Сетевой трафик
- Операции с диском
- Соединения с Redis
#### Бизнес-метрики
- Топ пользователей по использованию квоты
- Популярные размеры миниатюр
- Использование хранилища по типам файлов
### Пример запроса для Grafana
```sql
-- Количество загрузок по часам
SELECT
time_bucket('1 hour', timestamp) AS time,
COUNT(*) as uploads
FROM quoter_uploads
WHERE timestamp > NOW() - INTERVAL '24 hours'
GROUP BY time
ORDER BY time;
```
## Здоровье системы
### Health check endpoint
Добавьте endpoint для проверки здоровья:
```rust
async fn health_check() -> HttpResponse {
// Проверка Redis
let redis_ok = check_redis_connection().await;
// Проверка S3
let s3_ok = check_s3_connection().await;
if redis_ok && s3_ok {
HttpResponse::Ok().json(json!({
"status": "healthy",
"timestamp": chrono::Utc::now(),
"services": {
"redis": "ok",
"s3": "ok"
}
}))
} else {
HttpResponse::ServiceUnavailable().json(json!({
"status": "unhealthy",
"timestamp": chrono::Utc::now(),
"services": {
"redis": if redis_ok { "ok" } else { "error" },
"s3": if s3_ok { "ok" } else { "error" }
}
}))
}
}
```
### Kubernetes liveness/readiness probes
```yaml
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
```
## Трассировка
### Distributed tracing
Для отслеживания запросов через микросервисы:
```toml
[dependencies]
opentelemetry = "0.20"
tracing = "0.1"
tracing-opentelemetry = "0.20"
```
### Логирование с контекстом
```rust
use tracing::{info, warn, error, instrument};
#[instrument(skip(state))]
async fn upload_handler(
req: HttpRequest,
payload: Multipart,
state: web::Data<AppState>,
) -> Result<HttpResponse> {
let user_id = get_user_id(&req).await?;
info!(user_id = %user_id, "Starting file upload");
// ... логика загрузки
info!(user_id = %user_id, filename = %filename, "File uploaded successfully");
Ok(response)
}
```
## Рекомендации
### Продакшен
1. **Логирование**: Используйте структурированные логи в JSON формате
2. **Метрики**: Настройте Prometheus + Grafana
3. **Алерты**: Настройте уведомления для критических событий
4. **Ротация логов**: Настройте logrotate или отправку в ELK stack
5. **Мониторинг ресурсов**: Отслеживайте CPU, память, диск, сеть
### Разработка
1. **Локальное логирование**: Используйте `RUST_LOG=debug`
2. **Отладка**: Включите trace логи для детальной отладки
3. **Тестирование**: Создайте тесты для проверки метрик
4. **Документация**: Документируйте все кастомные метрики

198
docs/testing.md Normal file
View File

@@ -0,0 +1,198 @@
# Тестирование
Этот документ описывает подход к тестированию проекта Quoter.
## Обзор
Проект использует интеграционные тесты для проверки функциональности без внешних зависимостей. Тесты написаны на Rust с использованием фреймворка Actix Web для тестирования HTTP endpoints.
## Запуск тестов
### Все тесты
```bash
cargo test --tests # все
cargo test --test basic_test test_health_check # конкретный тест
cargo test --tests -- --nocapture # Тесты с выводом
```
## Описание тестов
### 1. Health Check (`test_health_check`)
Проверяет работу основного endpoint `/`:
- GET запрос возвращает статус 200 и тело "ok"
- POST запрос возвращает статус 404 (не найден)
### 2. JSON Сериализация (`test_json_serialization`)
Проверяет корректность сериализации и десериализации JSON:
- Создание структуры с данными квоты
- Сериализация в JSON строку
- Десериализация обратно в структуру
- Проверка соответствия данных
### 3. Multipart Form Data (`test_multipart_form_data`)
Проверяет создание multipart form data для загрузки файлов:
- Формирование правильного boundary
- Добавление заголовков Content-Disposition
- Добавление содержимого файла
- Проверка корректности структуры
### 4. UUID Генерация (`test_uuid_generation`)
Проверяет работу с UUID:
- Генерация уникальных UUID
- Проверка формата (36 символов с дефисами)
- Парсинг UUID обратно
### 5. MIME Типы (`test_mime_type_detection`)
Проверяет определение MIME типов по расширениям файлов:
- Поддерживаемые форматы (jpg, png, gif, webp, mp3, wav, mp4)
- Неподдерживаемые форматы (pdf, txt)
- Регистронезависимость
### 6. Парсинг путей файлов (`test_file_path_parsing`)
Проверяет парсинг путей файлов с размерами:
- Извлечение базового имени, ширины и расширения
- Обработка путей без размеров
- Обработка путей с подчеркиваниями
### 7. Расчеты квот (`test_quota_calculations`)
Проверяет логику расчета квот:
- Различные сценарии использования квоты
- Проверка превышения лимитов
- Корректность математических операций
### 8. Форматирование размеров (`test_file_size_formatting`)
Проверяет форматирование размеров файлов:
- Байты, килобайты, мегабайты, гигабайты
- Правильное округление
- Корректные единицы измерения
### 9. Обработка ошибок (`test_error_handling`)
Проверяет обработку некорректных данных:
- Неверный JSON
- Неполный JSON
- Неверные UUID
- Пустые значения
### 10. Производительность (`test_performance`)
Проверяет производительность критических операций:
- Генерация UUID (должна быть < 1μs)
- JSON сериализация (должна быть < 100μs)
- Вывод статистики производительности
## Принципы тестирования
### 1. Изоляция
- Тесты не зависят от внешних сервисов (Redis, S3)
- Каждый тест независим от других
- Используются моки и заглушки
### 2. Покрытие
- Тестируются основные функции
- Проверяются граничные случаи
- Тестируется обработка ошибок
### 3. Производительность
- Тесты должны выполняться быстро
- Проверяется производительность критических операций
- Устанавливаются временные лимиты
### 4. Читаемость
- Понятные названия тестов
- Описательные сообщения об ошибках
- Комментарии к сложной логике
## Добавление новых тестов
### 1. Создание теста
```rust
#[test]
async fn test_new_feature() {
// Подготовка
let test_data = create_test_data();
// Выполнение
let result = process_data(test_data);
// Проверка
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected_value);
}
```
### 2. Тестирование HTTP endpoints
```rust
#[actix_web::test]
async fn test_http_endpoint() {
let app = test::init_service(
App::new()
.route("/test", web::get().to(test_handler))
).await;
let req = test::TestRequest::get()
.uri("/test")
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
}
```
### 3. Тестирование производительности
```rust
#[test]
async fn test_performance() {
use std::time::Instant;
let start = Instant::now();
let iterations = 10000;
for _ in 0..iterations {
// Тестируемая операция
}
let duration = start.elapsed();
let avg_time = duration.as_micros() as f64 / iterations as f64;
assert!(avg_time < 100.0, "Operation too slow: {:.2} μs", avg_time);
}
```
## Лучшие практики
### 1. Именование
- Используйте описательные имена тестов
- Группируйте связанные тесты
- Используйте префиксы для типов тестов
### 2. Организация
- Разделяйте тесты на логические группы
- Используйте модули для организации
- Документируйте сложные тесты
### 3. Надежность
- Избегайте хрупких тестов
- Не полагайтесь на порядок выполнения
- Очищайте состояние после тестов
### 4. Производительность
- Минимизируйте время выполнения
- Используйте параллельное выполнение
- Оптимизируйте медленные тесты
## Отладка тестов
```bash
RUST_LOG=debug cargo test --tests -- --nocapture # Вывод отладочной информации
cargo test --tests -- --nocapture --test-threads=1 # Продолжение после ошибки
```
## Покрытие кода
```bash
cargo install cargo-tarpaulin # Установка cargo-tarpaulin
cargo tarpaulin --tests # Запуск анализа покрытия
```
## CI/CD интеграция
Тесты автоматически запускаются в CI/CD pipeline