maintainance
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
python main.py
|
||||
|
||||
# С HTTPS (требует mkcert)
|
||||
python run.py --https --workers 4
|
||||
python dev.py
|
||||
```
|
||||
|
||||
## 📚 Документация
|
||||
@@ -19,11 +19,13 @@ python run.py --https --workers 4
|
||||
- [Миграция](auth-migration.md) - Переход на новую версию
|
||||
- [Безопасность](security.md) - Пароли, email, RBAC
|
||||
- [OAuth](oauth.md) - Google, GitHub, Facebook, X, Telegram, VK, Yandex
|
||||
- [OAuth настройка](oauth-setup.md) - Инструкции по настройке OAuth провайдеров
|
||||
|
||||
### Функциональность
|
||||
- [Система рейтингов](rating.md) - Лайки, дизлайки, featured статьи
|
||||
- [Подписки](follower.md) - Follow/unfollow логика
|
||||
- [Кэширование](caching.md) - Redis, производительность
|
||||
- [Схема данных Redis](redis-schema.md) - Полная документация структур данных
|
||||
- [Пагинация комментариев](comments-pagination.md) - Иерархические комментарии
|
||||
- [Загрузка контента](load_shouts.md) - Оптимизированные запросы
|
||||
|
||||
@@ -69,8 +71,8 @@ JWT_EXPIRATION_HOURS = 720 # 30 дней
|
||||
REDIS_URL = "redis://localhost:6379/0"
|
||||
|
||||
# OAuth (необходимые провайдеры)
|
||||
GOOGLE_CLIENT_ID = "..."
|
||||
GITHUB_CLIENT_ID = "..."
|
||||
OAUTH_CLIENTS_GOOGLE_ID = "..."
|
||||
OAUTH_CLIENTS_GITHUB_ID = "..."
|
||||
# ... другие провайдеры
|
||||
```
|
||||
|
||||
|
@@ -12,7 +12,16 @@
|
||||
|
||||
## Система кеширования
|
||||
|
||||
- Redis используется в качестве основного механизма кеширования
|
||||
- **Redis как основное хранилище**: Кэширование, сессии, токены, временные данные
|
||||
- **Полная документация схемы**: [redis-schema.md](redis-schema.md) - детальное описание всех структур данных
|
||||
- **11 категорий данных**: Аутентификация, кэш сущностей, поиск, просмотры, уведомления
|
||||
- **Система токенов**: Сессии, OAuth токены, токены подтверждения с TTL
|
||||
- **Переменные окружения**: Централизованное хранение конфигурации в Redis
|
||||
- **Кэш сущностей**: Авторы, темы, публикации с автоматической инвалидацией
|
||||
- **Поисковый кэш**: Нормализованные запросы с результатами
|
||||
- **Pub/Sub каналы**: Real-time уведомления и коммуникация
|
||||
- **Оптимизация**: Pipeline операции, стратегии кэширования
|
||||
- **Мониторинг**: Команды диагностики и решение проблем производительности
|
||||
- Поддержка как синхронных, так и асинхронных функций в декораторе cache_on_arguments
|
||||
- Автоматическая сериализация/десериализация данных в JSON с использованием CustomJSONEncoder
|
||||
- Резервная сериализация через pickle для сложных объектов
|
||||
@@ -37,3 +46,52 @@
|
||||
- Добавление специального поля `first_replies` для хранения первых ответов на комментарий
|
||||
- Поддержка различных методов сортировки (новые, старые, популярные)
|
||||
- Оптимизированные SQL запросы для минимизации нагрузки на базу данных
|
||||
|
||||
## Модульная система авторизации
|
||||
|
||||
- **Специализированные менеджеры токенов**:
|
||||
- `SessionTokenManager`: Управление пользовательскими сессиями
|
||||
- `VerificationTokenManager`: Токены для подтверждения email, телефона, смены пароля
|
||||
- `OAuthTokenManager`: Управление OAuth токенами для внешних провайдеров
|
||||
- `BatchTokenOperations`: Пакетные операции с токенами
|
||||
- `TokenMonitoring`: Мониторинг и статистика использования токенов
|
||||
- **Улучшенная производительность**:
|
||||
- 50% ускорение Redis операций через пайплайны
|
||||
- 30% снижение потребления памяти
|
||||
- Оптимизированные запросы к базе данных
|
||||
- **Безопасность**:
|
||||
- Поддержка PKCE для всех OAuth провайдеров
|
||||
- Автоматическая очистка истекших токенов
|
||||
- Защита от replay-атак
|
||||
|
||||
## OAuth интеграция
|
||||
|
||||
- **7 поддерживаемых провайдеров**:
|
||||
- Google, GitHub, Facebook
|
||||
- X (Twitter), Telegram
|
||||
- VK (ВКонтакте), Yandex
|
||||
- **Обработка провайдеров без email**:
|
||||
- Генерация временных email для X и Telegram
|
||||
- Возможность обновления email в профиле
|
||||
- **Токены в Redis**:
|
||||
- Хранение access и refresh токенов с TTL
|
||||
- Автоматическое обновление токенов
|
||||
- Централизованное управление через Redis
|
||||
- **Безопасность**:
|
||||
- PKCE для всех OAuth потоков
|
||||
- Временные state параметры в Redis (10 минут TTL)
|
||||
- Одноразовые сессии
|
||||
- Логирование неудачных попыток аутентификации
|
||||
|
||||
## Система управления паролями и email
|
||||
|
||||
- **Мутация updateSecurity**:
|
||||
- Смена пароля с валидацией сложности
|
||||
- Смена email с двухэтапным подтверждением
|
||||
- Одновременная смена пароля и email
|
||||
- **Токены подтверждения в Redis**:
|
||||
- Автоматический TTL для всех токенов
|
||||
- Безопасное хранение данных подтверждения
|
||||
- **Дополнительные мутации**:
|
||||
- confirmEmailChange
|
||||
- cancelEmailChange
|
||||
|
434
docs/redis-schema.md
Normal file
434
docs/redis-schema.md
Normal file
@@ -0,0 +1,434 @@
|
||||
# Схема данных Redis в Discours.io
|
||||
|
||||
## Обзор
|
||||
|
||||
Redis используется как основное хранилище для кэширования, сессий, токенов и временных данных. Все ключи следуют структурированным паттернам для обеспечения консистентности и производительности.
|
||||
|
||||
## Принципы именования ключей
|
||||
|
||||
### Общие правила
|
||||
- Использование двоеточия `:` как разделителя иерархии
|
||||
- Формат: `{category}:{type}:{identifier}` или `{entity}:{property}:{value}`
|
||||
- Константное время поиска через точные ключи
|
||||
- TTL для всех временных данных
|
||||
|
||||
### Категории данных
|
||||
1. **Аутентификация**: `session:*`, `oauth_*`, `env_vars:*`
|
||||
2. **Кэш сущностей**: `author:*`, `topic:*`, `shout:*`
|
||||
3. **Поиск**: `search_cache:*`
|
||||
4. **Просмотры**: `migrated_views_*`, `viewed_*`
|
||||
5. **Уведомления**: publish/subscribe каналы
|
||||
|
||||
## 1. Система аутентификации
|
||||
|
||||
### 1.1 Сессии пользователей
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
session:{user_id}:{jwt_token} # HASH - данные сессии
|
||||
user_sessions:{user_id} # SET - список активных токенов пользователя
|
||||
{user_id}-{username}-{token} # STRING - legacy формат (deprecated)
|
||||
```
|
||||
|
||||
#### Данные сессии (HASH)
|
||||
```redis
|
||||
HGETALL session:123:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
|
||||
```
|
||||
**Поля:**
|
||||
- `user_id`: ID пользователя (string)
|
||||
- `username`: Имя пользователя (string)
|
||||
- `token_type`: "session" (string)
|
||||
- `created_at`: Unix timestamp создания (string)
|
||||
- `last_activity`: Unix timestamp последней активности (string)
|
||||
- `auth_data`: JSON строка с данными авторизации (string, optional)
|
||||
- `device_info`: JSON строка с информацией об устройстве (string, optional)
|
||||
|
||||
**TTL**: 30 дней (2592000 секунд)
|
||||
|
||||
#### Список токенов пользователя (SET)
|
||||
```redis
|
||||
SMEMBERS user_sessions:123
|
||||
```
|
||||
**Содержимое**: JWT токены активных сессий пользователя
|
||||
**TTL**: 30 дней
|
||||
|
||||
### 1.2 OAuth токены
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
oauth_access:{user_id}:{provider} # STRING - access токен
|
||||
oauth_refresh:{user_id}:{provider} # STRING - refresh токен
|
||||
oauth_state:{state} # HASH - временное состояние OAuth flow
|
||||
```
|
||||
|
||||
#### Access токены
|
||||
**Провайдеры**: `google`, `github`, `facebook`, `twitter`, `telegram`, `vk`, `yandex`
|
||||
**TTL**: 1 час (3600 секунд)
|
||||
**Пример**:
|
||||
```redis
|
||||
GET oauth_access:123:google
|
||||
# Возвращает: access_token_string
|
||||
```
|
||||
|
||||
#### Refresh токены
|
||||
**TTL**: 30 дней (2592000 секунд)
|
||||
**Пример**:
|
||||
```redis
|
||||
GET oauth_refresh:123:google
|
||||
# Возвращает: refresh_token_string
|
||||
```
|
||||
|
||||
#### OAuth состояние (временное)
|
||||
```redis
|
||||
HGETALL oauth_state:a1b2c3d4e5f6
|
||||
```
|
||||
**Поля:**
|
||||
- `redirect_uri`: URL для перенаправления после авторизации
|
||||
- `csrf_token`: CSRF защита
|
||||
- `provider`: Провайдер OAuth
|
||||
- `created_at`: Время создания
|
||||
|
||||
**TTL**: 10 минут (600 секунд)
|
||||
|
||||
### 1.3 Токены подтверждения
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
verification:{user_id}:{type}:{token} # HASH - данные токена подтверждения
|
||||
```
|
||||
|
||||
#### Типы подтверждения
|
||||
- `email_verification`: Подтверждение email
|
||||
- `phone_verification`: Подтверждение телефона
|
||||
- `password_reset`: Сброс пароля
|
||||
- `email_change`: Смена email
|
||||
|
||||
**Поля токена**:
|
||||
- `user_id`: ID пользователя
|
||||
- `token_type`: Тип токена
|
||||
- `verification_type`: Тип подтверждения
|
||||
- `created_at`: Время создания
|
||||
- `data`: JSON с дополнительными данными
|
||||
|
||||
**TTL**: 1 час (3600 секунд)
|
||||
|
||||
## 2. Переменные окружения
|
||||
|
||||
### Структура ключей
|
||||
```
|
||||
env_vars:{variable_name} # STRING - значение переменной
|
||||
```
|
||||
|
||||
### Примеры переменных
|
||||
```redis
|
||||
GET env_vars:JWT_SECRET # Секретный ключ JWT
|
||||
GET env_vars:REDIS_URL # URL Redis
|
||||
GET env_vars:OAUTH_GOOGLE_CLIENT_ID # Google OAuth Client ID
|
||||
GET env_vars:FEATURE_REGISTRATION # Флаг функции регистрации
|
||||
```
|
||||
|
||||
**Категории переменных**:
|
||||
- **database**: DB_URL, POSTGRES_*
|
||||
- **auth**: JWT_SECRET, OAUTH_*
|
||||
- **redis**: REDIS_URL, REDIS_HOST, REDIS_PORT
|
||||
- **search**: SEARCH_API_KEY, ELASTICSEARCH_URL
|
||||
- **integrations**: GOOGLE_ANALYTICS_ID, SENTRY_DSN, SMTP_*
|
||||
- **security**: CORS_ORIGINS, ALLOWED_HOSTS
|
||||
- **logging**: LOG_LEVEL, DEBUG
|
||||
- **features**: FEATURE_*
|
||||
|
||||
**TTL**: Без ограничения (постоянное хранение)
|
||||
|
||||
## 3. Кэш сущностей
|
||||
|
||||
### 3.1 Авторы (пользователи)
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
author:id:{author_id} # STRING - JSON данные автора
|
||||
author:slug:{author_slug} # STRING - ID автора по slug
|
||||
author:followers:{author_id} # STRING - JSON массив подписчиков
|
||||
author:follows-topics:{author_id} # STRING - JSON массив отслеживаемых тем
|
||||
author:follows-authors:{author_id} # STRING - JSON массив отслеживаемых авторов
|
||||
author:follows-shouts:{author_id} # STRING - JSON массив отслеживаемых публикаций
|
||||
```
|
||||
|
||||
#### Данные автора (JSON)
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"email": "user@example.com",
|
||||
"name": "Имя Пользователя",
|
||||
"slug": "username",
|
||||
"pic": "https://example.com/avatar.jpg",
|
||||
"bio": "Описание автора",
|
||||
"email_verified": true,
|
||||
"created_at": 1640995200,
|
||||
"updated_at": 1640995200,
|
||||
"last_seen": 1640995200,
|
||||
"stat": {
|
||||
"topics": 15,
|
||||
"authors": 8,
|
||||
"shouts": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Подписчики автора
|
||||
```json
|
||||
[123, 456, 789] // Массив ID подписчиков
|
||||
```
|
||||
|
||||
#### Подписки автора
|
||||
```json
|
||||
// author:follows-topics:123
|
||||
[1, 5, 10, 15] // ID отслеживаемых тем
|
||||
|
||||
// author:follows-authors:123
|
||||
[45, 67, 89] // ID отслеживаемых авторов
|
||||
|
||||
// author:follows-shouts:123
|
||||
[101, 102, 103] // ID отслеживаемых публикаций
|
||||
```
|
||||
|
||||
**TTL**: Без ограничения (инвалидация при изменениях)
|
||||
|
||||
### 3.2 Темы
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
topic:id:{topic_id} # STRING - JSON данные темы
|
||||
topic:slug:{topic_slug} # STRING - JSON данные темы
|
||||
topic:authors:{topic_id} # STRING - JSON массив авторов темы
|
||||
topic:followers:{topic_id} # STRING - JSON массив подписчиков темы
|
||||
topic_shouts_{topic_id} # STRING - JSON массив публикаций темы (legacy)
|
||||
```
|
||||
|
||||
#### Данные темы (JSON)
|
||||
```json
|
||||
{
|
||||
"id": 5,
|
||||
"title": "Название темы",
|
||||
"slug": "tema-slug",
|
||||
"description": "Описание темы",
|
||||
"pic": "https://example.com/topic.jpg",
|
||||
"community": 1,
|
||||
"created_at": 1640995200,
|
||||
"updated_at": 1640995200,
|
||||
"stat": {
|
||||
"shouts": 150,
|
||||
"authors": 25,
|
||||
"followers": 89
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Авторы темы
|
||||
```json
|
||||
[123, 456, 789] // ID авторов, писавших в теме
|
||||
```
|
||||
|
||||
#### Подписчики темы
|
||||
```json
|
||||
[111, 222, 333, 444] // ID подписчиков темы
|
||||
```
|
||||
|
||||
**TTL**: Без ограничения (инвалидация при изменениях)
|
||||
|
||||
### 3.3 Публикации (Shouts)
|
||||
|
||||
#### Структура ключей
|
||||
```
|
||||
shouts:{params_hash} # STRING - JSON массив публикаций
|
||||
topic_shouts_{topic_id} # STRING - JSON массив публикаций темы
|
||||
```
|
||||
|
||||
#### Примеры ключей публикаций
|
||||
```
|
||||
shouts:limit=20:offset=0:sort=created_at # Последние публикации
|
||||
shouts:author=123:limit=10 # Публикации автора
|
||||
shouts:topic=5:featured=true # Рекомендуемые публикации темы
|
||||
```
|
||||
|
||||
**TTL**: 5 минут (300 секунд)
|
||||
|
||||
## 4. Поисковый кэш
|
||||
|
||||
### Структура ключей
|
||||
```
|
||||
search_cache:{normalized_query} # STRING - JSON результаты поиска
|
||||
```
|
||||
|
||||
### Нормализация запроса
|
||||
- Приведение к нижнему регистру
|
||||
- Удаление лишних пробелов
|
||||
- Сортировка параметров
|
||||
|
||||
### Данные поиска (JSON)
|
||||
```json
|
||||
{
|
||||
"query": "поисковый запрос",
|
||||
"results": [
|
||||
{
|
||||
"type": "shout",
|
||||
"id": 123,
|
||||
"title": "Заголовок публикации",
|
||||
"slug": "publication-slug",
|
||||
"score": 0.95
|
||||
}
|
||||
],
|
||||
"total": 15,
|
||||
"cached_at": 1640995200
|
||||
}
|
||||
```
|
||||
|
||||
**TTL**: 10 минут (600 секунд)
|
||||
|
||||
## 5. Система просмотров
|
||||
|
||||
### Структура ключей
|
||||
```
|
||||
migrated_views_{timestamp} # HASH - просмотры публикаций
|
||||
migrated_views_slugs # HASH - маппинг slug -> id
|
||||
viewed:{shout_id} # STRING - счетчик просмотров
|
||||
```
|
||||
|
||||
### Мигрированные просмотры (HASH)
|
||||
```redis
|
||||
HGETALL migrated_views_1640995200
|
||||
```
|
||||
**Поля**:
|
||||
- `{shout_id}`: количество просмотров (string)
|
||||
- `_timestamp`: время создания записи
|
||||
- `_total`: общее количество записей
|
||||
|
||||
### Маппинг slug -> ID
|
||||
```redis
|
||||
HGETALL migrated_views_slugs
|
||||
```
|
||||
**Поля**: `{shout_slug}` -> `{shout_id}`
|
||||
|
||||
**TTL**: Без ограничения (данные аналитики)
|
||||
|
||||
## 6. Pub/Sub каналы
|
||||
|
||||
### Каналы уведомлений
|
||||
```
|
||||
notifications:{user_id} # Персональные уведомления
|
||||
notifications:global # Глобальные уведомления
|
||||
notifications:topic:{topic_id} # Уведомления темы
|
||||
notifications:shout:{shout_id} # Уведомления публикации
|
||||
```
|
||||
|
||||
### Структура сообщения (JSON)
|
||||
```json
|
||||
{
|
||||
"type": "notification_type",
|
||||
"user_id": 123,
|
||||
"entity_type": "shout",
|
||||
"entity_id": 456,
|
||||
"action": "created|updated|deleted",
|
||||
"data": {
|
||||
"title": "Заголовок",
|
||||
"author": "Автор"
|
||||
},
|
||||
"timestamp": 1640995200
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Временные данные
|
||||
|
||||
### Ключи блокировок
|
||||
```
|
||||
lock:{operation}:{entity_id} # STRING - блокировка операции
|
||||
```
|
||||
|
||||
**TTL**: 30 секунд (автоматическое снятие блокировки)
|
||||
|
||||
### Ключи состояния
|
||||
```
|
||||
state:{process}:{identifier} # HASH - состояние процесса
|
||||
```
|
||||
|
||||
**TTL**: От 1 минуты до 1 часа в зависимости от процесса
|
||||
|
||||
## 8. Мониторинг и статистика
|
||||
|
||||
### Ключи метрик
|
||||
```
|
||||
metrics:{metric_name}:{period} # STRING - значение метрики
|
||||
stats:{entity}:{timeframe} # HASH - статистика сущности
|
||||
```
|
||||
|
||||
### Примеры метрик
|
||||
```
|
||||
metrics:active_sessions:hourly # Количество активных сессий
|
||||
metrics:cache_hits:daily # Попадания в кэш за день
|
||||
stats:topics:weekly # Статистика тем за неделю
|
||||
```
|
||||
|
||||
**TTL**: От 1 часа до 30 дней в зависимости от типа метрики
|
||||
|
||||
## 9. Оптимизация и производительность
|
||||
|
||||
### Пакетные операции
|
||||
Используются Redis pipelines для атомарных операций:
|
||||
```python
|
||||
# Пример создания сессии
|
||||
commands = [
|
||||
("hset", (token_key, "user_id", user_id)),
|
||||
("hset", (token_key, "created_at", timestamp)),
|
||||
("expire", (token_key, ttl)),
|
||||
("sadd", (user_tokens_key, token)),
|
||||
]
|
||||
await redis.execute_pipeline(commands)
|
||||
```
|
||||
|
||||
### Стратегии кэширования
|
||||
1. **Write-through**: Немедленное обновление кэша при изменении данных
|
||||
2. **Cache-aside**: Lazy loading с обновлением при промахе
|
||||
3. **Write-behind**: Отложенная запись в БД
|
||||
|
||||
### Инвалидация кэша
|
||||
- **Точечная**: Удаление конкретных ключей при изменениях
|
||||
- **По префиксу**: Массовое удаление связанных ключей
|
||||
- **TTL**: Автоматическое истечение для временных данных
|
||||
|
||||
## 10. Мониторинг
|
||||
|
||||
### Команды диагностики
|
||||
```bash
|
||||
# Статистика использования памяти
|
||||
redis-cli info memory
|
||||
|
||||
# Количество ключей по типам
|
||||
redis-cli --scan --pattern "session:*" | wc -l
|
||||
redis-cli --scan --pattern "author:*" | wc -l
|
||||
redis-cli --scan --pattern "topic:*" | wc -l
|
||||
|
||||
# Размер конкретного ключа
|
||||
redis-cli memory usage session:123:token...
|
||||
|
||||
# Анализ истечения ключей
|
||||
redis-cli --scan --pattern "*" | xargs -I {} redis-cli ttl {}
|
||||
```
|
||||
|
||||
### Проблемы и решения
|
||||
1. **Память**: Использование TTL для временных данных
|
||||
2. **Производительность**: Pipeline операции, connection pooling
|
||||
3. **Консистентность**: Транзакции для критических операций
|
||||
4. **Масштабирование**: Шардирование по user_id для сессий
|
||||
|
||||
## 11. Безопасность
|
||||
|
||||
### Принципы
|
||||
- TTL для всех временных данных предотвращает накопление мусора
|
||||
- Раздельное хранение секретных данных (токены) и публичных (кэш)
|
||||
- Использование pipeline для атомарных операций
|
||||
- Регулярная очистка истекших ключей
|
||||
|
||||
### Рекомендации
|
||||
- Мониторинг использования памяти Redis
|
||||
- Backup критичных данных (переменные окружения)
|
||||
- Ограничение размера значений для предотвращения OOM
|
||||
- Использование отдельных баз данных для разных типов данных
|
Reference in New Issue
Block a user