Структура, доп модули, федерация, документация
This commit is contained in:
77
backend/src/Docs/admin_module_documentation.md
Normal file
77
backend/src/Docs/admin_module_documentation.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Документация модуля администрирования (Admin Module)
|
||||
|
||||
Модуль администрирования предназначен для управления глобальными настройками системы, пользователями и интеграциями мессенджера ForkMessager.
|
||||
|
||||
## Обзор архитектуры
|
||||
|
||||
Модуль построен на принципах чистой архитектуры и использует паттерн CQRS (через MediatR). Он взаимодействует с другими модулями системы для реализации сквозной функциональности, такой как полное удаление данных пользователя.
|
||||
|
||||
## Глобальные настройки системы
|
||||
|
||||
Настройки разделены на логические блоки для упрощения управления и соблюдения принципа разделения ответственности (ISP).
|
||||
|
||||
### 1. Системные настройки (System)
|
||||
* **DomainUrl**: Базовый URL домена сервера.
|
||||
* **EnableRegistration**: Переключатель возможности регистрации новых пользователей.
|
||||
* **AdminRoute**: Маршрут к панели администратора (по умолчанию `admin`).
|
||||
|
||||
### 2. Настройки историй (Stories)
|
||||
* **Enabled**: Общий переключатель функционала историй.
|
||||
* **MaxStoriesPerPeriod**: Максимальное количество историй за период жизни (1-10).
|
||||
* **StoryLifetimeHours**: Время жизни истории (4-48 часов).
|
||||
* **TextStoriesEnabled**: Разрешить текстовые истории.
|
||||
* **MaxMediaSizeBytes**: Лимит размера файлов для медиа-историй.
|
||||
|
||||
### 3. Настройки чатов (Chats)
|
||||
* **SupportGroups**: Включить поддержку групповых чатов.
|
||||
* **MaxGroupParticipants**: Лимит участников в одной группе.
|
||||
* **EnableFolders**: Включить функционал папок для группировки чатов.
|
||||
|
||||
### 4. Настройки сообщений (Messages)
|
||||
Администратор может гибко управлять правами пользователей на работу с контентом:
|
||||
* **AllowPolls**: Разрешить создание опросов (аналог Telegram).
|
||||
* **AllowMedia**: Разрешить отправку файлов/фото/видео.
|
||||
* **AllowVoiceMessages**: Разрешить отправку голосовых сообщений.
|
||||
* **AllowForwarding**: Разрешить пересылку сообщений в другие чаты.
|
||||
* **AllowReplies**: Разрешить ответы на сообщения.
|
||||
* **AllowQuoting**: Разрешить цитирование текста.
|
||||
* **AllowPinning**: Разрешить закрепление сообщений в чатах.
|
||||
* **AllowMessageDeletion**: Разрешить удаление сообщений.
|
||||
* **ForbidCopying**: При включении запрещает копирование текста и сохранение медиафайлов из чатов.
|
||||
* **AllowLinks**: Разрешить вставку активных гиперссылок.
|
||||
* **MaxMediaSizeBytes**: Глобальный лимит на размер вложения.
|
||||
|
||||
### 5. Настройки WebRTC (Голос/Видео)
|
||||
* **Enabled**: Общий переключатель звонков.
|
||||
* **Voice/Video Calls**: Разрешить аудио и видео звонки отдельно.
|
||||
* **ScreenSharing**: Разрешить демонстрацию экрана.
|
||||
* **TURN Configuration**: IP, логин и пароль для TURN-сервера (необходим для обхода NAT).
|
||||
|
||||
### 6. Интеграция Klipy
|
||||
* **Enabled**: Включить сервис GIF и стикеров Klipy.
|
||||
* **AppName / Token**: Параметры аутентификации в API Klipy.
|
||||
|
||||
### 7. Федерация (Confederation)
|
||||
* **Enabled**: Позволяет серверу взаимодействовать с другими серверами мессенджера.
|
||||
* **ServerDescription**: Описание сервера. **Валидация: от 20 до 120 символов.**
|
||||
* **AllowedDomains**: Список доверенных доменов для обмена сообщениями.
|
||||
|
||||
---
|
||||
|
||||
## Административные команды
|
||||
|
||||
### Полное удаление пользователя ([DeleteUserCommand](file:///e:/GIT/forkmessager/backend/src/Modules/Conversations/Application/Users/Commands/DeleteUser/DeleteUserCommand.cs#13-14))
|
||||
Операция, гарантирующая удаление персональных данных:
|
||||
1. **Очистка сообщений**: Удаляются сообщения пользователя из MongoDB.
|
||||
2. **Очистка файлов**: Система проверяет вложения. Файл удаляется из S3, если на него нет ссылок в других чатах.
|
||||
3. **Очистка профиля**: Удаление из PostgreSQL (Auth и Conversations).
|
||||
|
||||
### Сброс пароля (`ResetUserPasswordCommand`)
|
||||
Позволяет администратору принудительно изменить пароль любого пользователя.
|
||||
|
||||
---
|
||||
|
||||
## API Эндпоинты
|
||||
* `GET /api/admin/settings` — Получение текущих настроек.
|
||||
* `PUT /api/admin/settings` — Обновление конфигурации.
|
||||
* `DELETE /api/admin/users/{userId}` — Полное удаление пользователя.
|
||||
68
backend/src/Docs/auth_module_documentation.md
Normal file
68
backend/src/Docs/auth_module_documentation.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Документация модуля аутентификации (Auth Module)
|
||||
|
||||
Модуль Auth отвечает за идентификацию, аутентификацию и управление базовыми профилями пользователей в системе Knot Messager.
|
||||
|
||||
## Основные функции
|
||||
|
||||
* Регистрация новых пользователей (с контролем доступности).
|
||||
* Аутентификация (Login) с выдачей JWT токенов.
|
||||
* Управление сессиями и контекстом текущего пользователя.
|
||||
* Хранение базовой информации профиля (Username, PasswordHash, DisplayName).
|
||||
|
||||
## Доменная модель
|
||||
|
||||
### Пользователь (User)
|
||||
Сущность [User](file:///e:/GIT/forkmessager/backend/src/Modules/Auth/Domain/User.cs#20-30) является корнем агрегата и содержит следующие данные:
|
||||
* **Username**: Уникальное имя пользователя (логин).
|
||||
* **PasswordHash**: Хэшированный пароль (argon2/bcrypt).
|
||||
* **DisplayName**: Отображаемое имя.
|
||||
* **Email**: Электронная почта (опционально).
|
||||
* **Avatar**: URL аватара из хранилища.
|
||||
* **Bio**: Краткое описание профиля.
|
||||
* **Birthday**: Дата рождения (опционально).
|
||||
* **CreatedAt**: Дата регистрации.
|
||||
|
||||
## Процессы и команды
|
||||
|
||||
### 1. Регистрация ([RegisterUserCommand](file:///e:/GIT/forkmessager/backend/src/Modules/Auth/Application/Users/Register/RegisterUserCommandHandler.cs#14-20))
|
||||
Регистрация новых пользователей является управляемым процессом.
|
||||
|
||||
**Зависимость от модуля администрирования:**
|
||||
Перед началом процесса регистрации модуль Auth проверяет глобальную настройку `EnableRegistration` из модуля [Settings](file:///e:/GIT/forkmessager/backend/src/Modules/Settings/Application/Config/DTOs/PublicConfigDto.cs#17-87) (управляется через `Admin Module`).
|
||||
* Если регистрация **выключена** администратором, команда возвращает ошибку `Identity.RegistrationDisabled`.
|
||||
* Если регистрация **включена**, процесс продолжается:
|
||||
1. Проверка уникальности [Username](file:///e:/GIT/forkmessager/backend/src/Modules/Auth/Infrastructure/Persistence/UserRepository.cs#28-32).
|
||||
2. Хэширование пароля с использованием алгоритма BCrypt.
|
||||
3. Создание записи в PostgreSQL.
|
||||
4. Выдача JWT-токена для немедленного входа.
|
||||
|
||||
### 2. Вход в систему (`LoginUserCommand`)
|
||||
Процесс проверки учетных данных:
|
||||
1. Поиск пользователя по [Username](file:///e:/GIT/forkmessager/backend/src/Modules/Auth/Infrastructure/Persistence/UserRepository.cs#28-32).
|
||||
2. Верификация хэша пароля.
|
||||
3. Генерация JWT-токена, содержащего `sub` (UserId) и `unique_name` (Username).
|
||||
|
||||
### 3. Получение данных о себе (`GetMeQuery`)
|
||||
Возвращает данные текущего авторизованного пользователя на основе Token Claims. Используется для инициализации состояния клиента (профиля) после загрузки приложения.
|
||||
|
||||
## API Эндпоинты
|
||||
|
||||
Модуль использует **Minimal APIs** через Carter. Базовый путь: `/api/auth`
|
||||
|
||||
* `POST /api/auth/register` — Регистрация нового аккаунта. Возвращает ошибку 400, если регистрация закрыта администратором.
|
||||
* `POST /api/auth/login` — Вход и получение токена.
|
||||
* `GET /api/auth/me` — Получение данных своего профиля (требует Authorization Header).
|
||||
|
||||
## Безопасность
|
||||
|
||||
1. **JWT**: Используются токены с коротким временем жизни. Секретный ключ, Issuer и Audience настраиваются в переменных окружения.
|
||||
2. **Хэширование**: Пароли никогда не хранятся в открытом виде.
|
||||
3. **Контроль регистрации**: Позволяет администратору полностью закрыть сервер от новых регистраций в любой момент времени.
|
||||
4. **Изоляция**: Другие модули получают ID пользователя через `IUserContext`, который извлекает данные из JWT Claims без прямого обращения к БД Auth.
|
||||
|
||||
## Взаимодействие с другими модулями
|
||||
|
||||
* **Settings/Admin**: Предоставляет настройки доступности регистрации.
|
||||
* **Conversations**: Связывает чаты с UserId.
|
||||
* **Messaging**: Связывает сообщения с SenderId.
|
||||
* **Admin**: Позволяет администратору сбрасывать пароли пользователей.
|
||||
84
backend/src/Docs/conversations_module_documentation.md
Normal file
84
backend/src/Docs/conversations_module_documentation.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Документация модуля диалогов (Conversations Module)
|
||||
|
||||
Модуль Conversations является центральным узлом Knot Messager, обеспечивающим взаимодействие пользователей через чаты, управление сообщениями и организацию рабочего пространства с помощью папок.
|
||||
|
||||
## Основные сущности
|
||||
|
||||
### 1. Чат (Chat)
|
||||
Основная единица общения. Бывает трех типов:
|
||||
* **Personal**: Диалог между двумя пользователями.
|
||||
* **Group**: Групповой чат с неограниченным (или лимитированным админом) количеством участников.
|
||||
* **Favorites**: Спец-чат "Избранное" (заметки для самого себя).
|
||||
|
||||
### 2. Участник (Member)
|
||||
Связующее звено между пользователем и чатом. Хранит:
|
||||
* Роль (Admin, Moderator, Member).
|
||||
* **Cursors**: Указатели на последнее прочитанное и последнее доставленное сообщение для синхронизации статусов.
|
||||
|
||||
### 3. Папка (Folder)
|
||||
Инструмент группировки чатов.
|
||||
* **Системные папки**: "Все чаты", "Новые" (с непрочитанными), "Без звука".
|
||||
* **Пользовательские папки**: Создаются пользователем вручную по имени и иконке.
|
||||
* *Особенность*: Один чат может находиться одновременно в нескольких папках.
|
||||
|
||||
## Функциональные возможности
|
||||
|
||||
### Управление чатами
|
||||
* Создание личных и групповых чатов.
|
||||
* Редактирование названия, описания и аватара группы (с поддержкой обрезки изображения).
|
||||
* Управление участниками (добавление, удаление, выход из чата).
|
||||
* Закрепление чатов (Pin) для быстрого доступа.
|
||||
* Очистка истории сообщений для конкретного пользователя.
|
||||
|
||||
### Работа с сообщениями
|
||||
Модуль предоставляет высокоуровневый API для отправки контента:
|
||||
* **Текстовые сообщения**: С поддержкой цитирования и ответов.
|
||||
* **Медиа-сообщения**: Фото, видео, файлы, голосовые сообщения.
|
||||
* **Опросы**: Анонимные или публичные, с выбором одного или нескольких вариантов.
|
||||
* **Истории**: Ответы на истории и реакции (пересылаются в чат как спец-сообщения).
|
||||
* **Пересылка (Forwarding)**: Отправка существующих сообщений в другие чаты.
|
||||
|
||||
### Синхронизация и поиск
|
||||
* **Курсоры чтения**: Позволяют клиенту точно знать, какие сообщения новы для пользователя.
|
||||
* **Shared Media**: Быстрый доступ ко всем файлам, ссылкам или фото, когда-либо отправленным в конкретном чате.
|
||||
* **Глобальный поиск**: Поиск по тексту сообщений во всех доступных чатах.
|
||||
|
||||
---
|
||||
|
||||
## API Эндпоинты
|
||||
|
||||
### Чаты (`api/chats`)
|
||||
* `GET /` — Получение списка всех чатов пользователя.
|
||||
* `POST /personal` — Создание диалога с пользователем.
|
||||
* `POST /group` — Создание группы.
|
||||
* `PUT /{id}` — Обновление информации о чате.
|
||||
* `POST /{id}/pin` — Закрепить/открепить чат.
|
||||
* `POST /{id}/avatar` — Загрузка аватара группы.
|
||||
|
||||
## Зависимость от глобальных настроек (Admin Module)
|
||||
|
||||
Логика модуля Conversations напрямую зависит от конфигурации, установленной администратором в `Admin Module`:
|
||||
|
||||
1. **Медиа-сообщения**: Перед отправкой фото, видео или файлов проверяется флаг `AllowMedia`. Если он выключен, сервер возвращает ошибку `Media.Disabled`.
|
||||
2. **Опросы**: Создание опросов возможно только при включенном флаге `AllowPolls`. В противном случае возвращается `Polls.Disabled`.
|
||||
3. **Папки**: Функционал группировки чатов по папкам контролируется флагом `EnableFolders`. При его отключении команды добавления в папку ([AddToFolder](file:///e:/GIT/forkmessager/backend/src/Modules/Conversations/Domain/Folder.cs#67-71)) блокируются с ошибкой `Folders.Disabled`.
|
||||
4. **Групповые чаты**: Лимит на количество участников в группе (`MaxGroupParticipants`) проверяется при создании чата и добавлении новых членов.
|
||||
|
||||
## Сообщения (`api/messages`)
|
||||
* `POST /chat/{chatId}` — Отправка сообщения.
|
||||
* `GET /chat/{chatId}` — Получение истории (поддерживает пагинацию через курсоры).
|
||||
* `POST /upload` — Предварительная загрузка файла в хранилище.
|
||||
* `GET /search` — Поиск сообщений.
|
||||
|
||||
### Папки (`api/folders`)
|
||||
* `POST /add` — Добавление чата в папку.
|
||||
* `POST /remove` — Удаление из папки.
|
||||
|
||||
---
|
||||
|
||||
## Интеграция и Real-time
|
||||
|
||||
Модуль тесно интегрирован с:
|
||||
* **Messaging**: Для фактического хранения и доставки тел сообщений (в MongoDB).
|
||||
* **Storage**: Для обработки вложений и аватаров.
|
||||
* **SignalR (ChatHub)**: Для мгновенной доставки уведомлений о новых сообщениях, статусах прочтения (`message_read`) и обновлении состава участников.
|
||||
42
backend/src/Docs/federation_encryption_implementation.md
Normal file
42
backend/src/Docs/federation_encryption_implementation.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Спецификация Гибридного Шифрования (Hybrid Federation Encryption)
|
||||
|
||||
Для обеспечения безопасности переписки между серверами Knot Messager используется схема AES-RSA.
|
||||
|
||||
## 1. Структура Пакета (The Courier Packet)
|
||||
|
||||
```json
|
||||
{
|
||||
"senderDomain": "domain-a.com",
|
||||
"recipientDomain": "domain-b.com",
|
||||
"encryptedPayload": "base64-aes-data",
|
||||
"encryptedIV": "base64-rsa-encrypted-iv",
|
||||
"signature": "base64-rsa-signature",
|
||||
"metadata": {
|
||||
"chatId": "uuid",
|
||||
"senderId": "uuid",
|
||||
"messageType": "text|media|poll"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Алгоритм Отправки (Outgoing)
|
||||
|
||||
1. **AES-256 Encryption**: Локальный сервер генерирует случайный `IV` и шифрует текст сообщения с помощью `AES-256-CBC`.
|
||||
2. **RSA-Key Wrap**: Шифрует полученный `IV` с помощью **Public Key сервера-получателя** (полученного при Handshake).
|
||||
3. **RSA-Sign**: Подписывает весь пакет (собрав хеш из payload + metadata) своим **Private Key**.
|
||||
4. **Transport**: Отправляет POST-запрос на `/api/federation/v1/inbound`.
|
||||
|
||||
## 3. Алгоритм Приема (Inbound)
|
||||
|
||||
1. **Verify Signature**: Проверяет подпись пакета, используя **Public Key сервера-отправителя** (из белого списка).
|
||||
2. **RSA-Key Unwrap**: Расшифровывает `encryptedIV`, используя свой **локальный Private Key**.
|
||||
3. **AES-256 Decryption**: С помощью расшифрованного `IV` читает `encryptedPayload`.
|
||||
4. **Persistence**: Сохраняет сообщение в локальную БД и уведомляет пользователя через SignalR.
|
||||
|
||||
---
|
||||
|
||||
## Задачи на текущую итерацию:
|
||||
|
||||
1. **Shared Kernel**: Добавить методы `EncryptWithRsa` и `DecryptWithRsa` в вспомогательные службы.
|
||||
2. **Federation Application**: Реализовать `InboundFederationCommand` (логика приема и расшифровки).
|
||||
3. **Federation Infrastructure**: Реализовать `FederationHttpClient` для выполнения подписанных запросов.
|
||||
79
backend/src/Docs/federation_module_documentation.md
Normal file
79
backend/src/Docs/federation_module_documentation.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Модуль Федерации (Federation Module) — Knot Messager
|
||||
|
||||
Модуль федерации обеспечивает децентрализованное взаимодействие между независимыми узлами (серверами) Knot Messager. Он реализует безопасный обмен сообщениями, синхронизацию присутствия (Presence), проксирование медиафайлов и координацию системных политик.
|
||||
|
||||
---
|
||||
|
||||
## 🏗 Архитектура конфедерации
|
||||
|
||||
Федерация Knot Messager построена на принципах **Zero-Knowledge** и **Intersected Policies**. Ключевые аспекты:
|
||||
1. **Peer-to-Peer Trust**: Узлы доверяют друг другу на основе предварительного обмена публичными ключами (Handshake).
|
||||
2. **Domain Isolation**: Модуль Федерации полностью изолирован от других модулей. Взаимодействие происходит через **Domain Events** (MediatR).
|
||||
3. **No Data Duplication**: Файлы не копируются на чужие сервера, а стримятся через авторизованные прокси-каналы.
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Безопасность и Шифрование
|
||||
|
||||
Для защиты межсерверного трафика используется гибридная схема:
|
||||
* **Payload Encryption (AES-256)**: Содержимое пакетов шифруется на лету уникальным ключом AES.
|
||||
* **Key Wrapping (RSA-2048)**: Ключ AES и IV шифруются на публичный ключ сервера-получателя.
|
||||
* **Authentication (RSA Signature)**: Каждый входящий пакет подписывается приватным ключом отправителя. Подпись проверяется получателем по белому списку доменов.
|
||||
|
||||
---
|
||||
|
||||
## 📥 Входящие типы пакетов (Inbound Messages)
|
||||
|
||||
Модуль обрабатывает следующие типы федеративных транзакций через [InboundFederationCommand](file:///e:/GIT/forkmessager/backend/src/Modules/Federation/Application/Federation/Commands/InboundFederationCommand.cs#19-20):
|
||||
|
||||
| Тип сообщения | Описание | Действие |
|
||||
| :--- | :--- | :--- |
|
||||
| `text` | Обычное текстовое сообщение. | Сохранение в БД и уведомление локальных юзеров. |
|
||||
| `presence_update` | Обновление статуса (Online/Offline/LastSeen). | Обновление кэша `IsOnline` для внешнего контакта. |
|
||||
| `sync_capabilities` | Синхронизация системных настроек. | Обновление правил (Media, Polls, RTC) для партнера. |
|
||||
| `rtc_signal` | WebRTC сигнализация (Offer/ICE). | Проброс сигнала конечному пользователю для звонка. |
|
||||
| `message_edited` | Редактирование сообщения. | Обновление контента локальной копии сообщения. |
|
||||
| `message_deleted` | Удаление сообщения "у всех". | Перманентное удаление сообщения из локальной БД. |
|
||||
| `reaction_added` | Добавление эмодзи-реакции. | Синхронизация реакции внешнего пользователя. |
|
||||
|
||||
---
|
||||
|
||||
## 📂 Проксирование медиа (Storage Proxy)
|
||||
|
||||
Критически важный механизм, исключающий хранение чужих данных:
|
||||
1. **Outgoing Proxy**: `/api/federation/v1/proxy/{id}` — сервер отдает файл только авторизованным серверам-партнерам.
|
||||
2. **Incoming Proxy**: `/api/files/remote/{domain}/{id}` — ваш сервер выступает "транзитом", запрашивает файл у партнера и стримит его вашему клиенту.
|
||||
3. **Безопасность**: Ссылки на медиа внутри сообщений всегда указывают на локальный прокси, а не на оригинальный домен отправителя.
|
||||
|
||||
---
|
||||
|
||||
## ⚙ Настройка и Handshake
|
||||
|
||||
Администратор добавляет домен в белый список через `Admin Module`:
|
||||
1. Генерируется пара RSA-ключей для своего сервера (если нет).
|
||||
2. Выполняется запрос `/handshake` к удаленному серверу.
|
||||
3. Обмениваются `Public Keys` и [Capabilities](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#90-98) (возможности сервера).
|
||||
4. С этого момента домен считается **Trusted Node**.
|
||||
|
||||
---
|
||||
|
||||
## 📡 Реализованные API Эндпоинты
|
||||
|
||||
* `POST /api/federation/v1/handshake` — Установка связи.
|
||||
* `POST /api/federation/v1/inbound` — Прием зашифрованных пакетов.
|
||||
* `GET /api/federation/v1/resolve/{username}` — Поиск профиля по всей сети.
|
||||
* `GET /api/federation/v1/proxy/{id}` — Стриминг контента для партнеров.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Взаимодействие с другими модулями (MediatR Events)
|
||||
|
||||
* [MessageSentDomainEvent](file:///e:/GIT/forkmessager/backend/src/Modules/Messaging/Domain/MessageSentDomainEvent.cs#9-10) ➡ Триггерит рассылку сообщения внешним участникам.
|
||||
* [UserStatusChangedDomainEvent](file:///e:/GIT/forkmessager/backend/src/Modules/Auth/Domain/User.cs#5-6) ➡ Пушит статус пользователя всем серверам-контактам.
|
||||
* [SystemSettingsUpdatedDomainEvent](file:///e:/GIT/forkmessager/backend/src/Modules/Admin/Domain/Events/SystemSettingsUpdatedDomainEvent.cs#10-11) ➡ Синхронизирует возможности (Capabilities) с сетью.
|
||||
* `MessageEdited/DeletedDomainEvent` ➡ Транслирует действия с сообщениями.
|
||||
|
||||
---
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Модуль Федерации требует корректно настроенного `System:DomainUrl` в глобальном конфиге для формирования подписей.
|
||||
135
backend/src/Docs/federation_protocol_specification.md
Normal file
135
backend/src/Docs/federation_protocol_specification.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Архитектура и Протокол Конфедерации (Federation Protocol)
|
||||
|
||||
Спецификация описывает механизмы взаимодействия независимых узлов (серверов) Knot Messager для обмена сообщениями, соблюдения политик безопасности и проксирования медиаконтента.
|
||||
|
||||
---
|
||||
|
||||
## 1. Основные принципы (Design Principles)
|
||||
|
||||
1. **Делегированное Хранение (Remote Proxy Storage)**: Медиафайлы (фото, видео, голосовые) хранятся **только на сервере отправителя**. Сервер получателя хранит лишь "прокси-ссылку" на объект.
|
||||
2. **Сквозная Валидация (Cross-Server Policy)**: Если на сервере получателя отключены голосовые сообщения или опросы, сервер отправителя блокирует их отправку в этот федеративный чат на этапе валидации (до сетевого запроса).
|
||||
3. **Безопасный Handshake (RSA-2048)**: Узлы доверяют друг другу на основе обмена публичными ключами и подписи каждого входящего пакета (HMAC/RSA).
|
||||
4. **Zero-Knowledge Integrity**: Ключи шифрования вложений (IV) передаются получателю в зашифрованном виде (на его публичный ключ), чтобы обеспечивался E2EE даже при проксировании контента.
|
||||
|
||||
---
|
||||
|
||||
## 2. Протокол взаимодействия (Federation API)
|
||||
|
||||
Все запросы между серверами используют префикс `/api/federation/v1/*` и подписываются заголовком `X-Knot-Signature`.
|
||||
|
||||
### 2.1 Handshake (Установка связи)
|
||||
При добавлении домена в `Admin Module`, сервер инициирует [HandshakeRequest](file:///e:/GIT/forkmessager/backend/src/Modules/Federation/Application/Federation/Commands/HandshakeFederationCommand.cs#16-17):
|
||||
* **Передает**: Свой домен, публичный ключ, список поддерживаемых фич (AllowMedia, AllowPolls и т.д.).
|
||||
* **Получает**: Аналогичные данные от удаленного сервера.
|
||||
* **Результат**: Создается пара "Доверенный узел" с кешированной политикой возможностей.
|
||||
|
||||
### 2.2 Доставка Сообщений (`PushMessage`)
|
||||
Когда сообщение отправляется в чат с федеративным участником:
|
||||
1. Локальный сервер проверяет кэш политики удаленного сервера.
|
||||
2. Если тип сообщения разрешен удаленным сервером, формируется `FederationPacket`:
|
||||
```json
|
||||
{
|
||||
"sender": "user@domain-a.com",
|
||||
"chatId": "shared-uuid",
|
||||
"payload": { "type": "text", "content": "..." },
|
||||
"signature": "rsa-sig-body"
|
||||
}
|
||||
```
|
||||
3. Для медиа-сообщений в `payload` передается `ProxyUrl` вместо прямого вложения: `https://domain-a.com/api/federation/v1/proxy/{objectId}`.
|
||||
|
||||
### 2.3 Discovery & Health Check (Ping)
|
||||
Для поддержания актуального статуса сети используется фоновый мониторинг (Keep-Alive):
|
||||
* **Endpoint**: `GET /api/federation/v1/ping`.
|
||||
* **Механика**: Каждый узел раз в 2-5 минут опрашивает (пингует) известные ему домены из `AllowedDomains`.
|
||||
* **Статусы**:
|
||||
* `Online` — Сервер доступен.
|
||||
* `Maintenance` (503) — Временно недоступен (повтор через 10 мин).
|
||||
* `Disabled` (403) — Конфедерация на той стороне выключена принудительно.
|
||||
* **Последствия**: Если сервер `Offline`, исходящие сообщения в его сторону ставятся в локальную очередь `FederationOutbox`.
|
||||
|
||||
### 2.4 Global User Search (Поиск пользователей)
|
||||
Поиск осуществляется по формату `username@domain.com`:
|
||||
1. Если в строке поиска есть `@`, локальный сервер делает запрос к удаленному: `GET /api/federation/v1/users/search?q={username}`.
|
||||
2. Удаленный сервер возвращает публичные данные (DisplayName, AvatarProxyUrl).
|
||||
3. **Важно**: Поиск анонимен. Удаленный сервер отдает данные только если у пользователя в настройках разрешен публичный поиск через федерацию.
|
||||
|
||||
### 2.5 Federated Stories (Истории между серверами)
|
||||
Когда пользователь хочет посмотреть истории контакта с другого сервера:
|
||||
1. **Request**: Локальный сервер запрашивает актуальный список историй по ID пользователя: `GET /api/federation/v1/stories/user/{userId}`.
|
||||
2. **Response**: Удаленный сервер возвращает список метаданных историй (ID, Type, ProxyMediaUrl).
|
||||
3. **Proxying**: Сами файлы историй (видео/фото) проксируются через тот же механизм `Remote Proxy Storage`, что и вложения в сообщениях.
|
||||
4. **Views Logic**: Фиксация просмотра (`view_story`) также передается через федеративный пакет, чтобы автор на удаленном сервере видел счетчик просмотров правильно.
|
||||
|
||||
### 2.6 Remote Presence & Statuses (Синхронизация присутствия)
|
||||
1. **Status Sync**: При получении нового сообщения от контакта с другого сервера, поле `LastSeen` для этого контакта локально обновляется.
|
||||
2. **Explicit Query (Пинг статуса)**: При открытии чата с удаленным пользователем, локальный сервер может сделать запрос `GET /api/federation/v1/users/{id}/status`.
|
||||
3. **Response**: Возвращает `Online = true/false` и `LastSeen = timestamp`.
|
||||
4. **Privacy**: Если на удаленном сервере пользователь скрыл статус "Был в сети", сервер вернет пустой `LastSeen`.
|
||||
|
||||
### 2.7 Federated RTC (Звонки и Шаринг)
|
||||
WebRTC соединение между пользователями разных серверов требует координации:
|
||||
1. **Signaling Relay**: Серверы А и Б выступают промежуточными узлами (Relay) для обмена Offer/Answer/ICE кандидатами через федеративный канал.
|
||||
2. **Policy Check**: Перед инициацией звонка сервер А запрашивает у сервера Б: `CanCall(userB)`. Если у сервера Б выключены `VoiceCalls` или `ScreenSharing`, сервер А блокирует кнопку звонка в клиенте.
|
||||
3. **TURN Proxy**: Для обхода NAT в федерации рекомендуется использовать TURN-серверы, указанные в [WebRtcConfig](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#58-69).
|
||||
|
||||
### 2.8 Real-time & Interactivity (Реакции и Ответы)
|
||||
Любое действие с сообщением должно быть доставлено удаленному узлу:
|
||||
* **Реакции**: Пакет `MessageReactionChanged` с указанием `emoji` и `messageId`.
|
||||
* **Ответы на Истории**: Пакет `StoryResponse` с типом (Reply/Reaction) и метаданными истории.
|
||||
* **Удаление у всех (Delete for Everyone)**: Пакет `MessageDeleted`. Сервер получателя обязан удалить локальную копию сообщения при получении подписанного пакета от сервера-владельца.
|
||||
* **Редактирование (Edit)**: Пакет `MessageUpdated` с новым телом сообщения. Применяется локально на сервере получателя.
|
||||
|
||||
---
|
||||
|
||||
## 3. Remote Proxy Storage (Проксирование медиа)
|
||||
|
||||
## 3. Remote Proxy Storage (Проксирование медиа)
|
||||
|
||||
Это критическая часть системы, позволяющая не дублировать файлы на чужих серверах.
|
||||
|
||||
### Процесс скачивания файла пользователем Сервера B (отправленного Сервером A):
|
||||
1. Клиент на Сервере B запрашивает файл через локальный API: `GET /api/files/{remoteId}`.
|
||||
2. Модуль [Storage](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Storage/IFileStorageService.cs#6-26) сервера B видит, что файл является "внешним" (External).
|
||||
3. Сервер B делает внутренний авторизованный запрос к Серверу A: `GET /api/federation/v1/proxy/{objectId}`.
|
||||
4. Сервер A стримит байты зашифрованного файла Серверу B.
|
||||
5. Сервер B на лету расшифровывает контент (используя полученный ранее IV) и отдает клиенту.
|
||||
|
||||
**Важно:** Сервер B никогда не сохраняет файл локально на постоянной основе (только в In-Memory кэш для скорости раздачи).
|
||||
|
||||
---
|
||||
|
||||
## 4. Групповые чаты (Fan-out Architecture)
|
||||
|
||||
В Knot Messager групповые чаты децентрализованы.
|
||||
|
||||
1. **Рассылка (Fan-out)**: При отправке сообщения в группу, локальный сервер отправителя определяет список уникальных доменов участников и рассылает пакет каждому серверу один раз.
|
||||
2. **Автономность**: Если один из серверов-участников (например, Server C) уходит в оффлайн, остальные участники (Server A, B, D) продолжают общаться. Сообщения для Server C накапливаются в очередях на других серверах.
|
||||
3. **Медиа-матрица**: В одном групповом чате могут быть вложения, хранящиеся на 5 разных серверах одновременно. Каждый сервер проксирует файлы своих пользователей самостоятельно.
|
||||
|
||||
---
|
||||
|
||||
## 5. Валидация политик (Policy Enforcement)
|
||||
|
||||
Модуль Конфедерации Knot Messager работает по принципу **Intersected Policy** (Пересечение политик):
|
||||
|
||||
1. **Rule**: Действие разрешено только если оно разрешено ОБОИМИ серверами (отправителем и получателем).
|
||||
2. **Mapping**:
|
||||
* Если Сервер А разрешил `ScreenSharing`, а Сервер Б запретил — шаринг экрана в этом чате **невозможен**.
|
||||
* Если Сервер А запретил `Copying` (Для запрета копирования), Сервер Б **обязан** передать этот флаг своему клиенту, чтобы тот заблокировал контекстное меню для сообщений из этого чата.
|
||||
3. **Audit**: Все входящие федеративные пакеты проходят через `PolicyValidator`. Если сервер А пытается прислать Опрос на сервер Б, где опросы выключены, пакет будет отклонен с кодом `403 Forbidden`.
|
||||
|
||||
---
|
||||
|
||||
## 6. План реализации
|
||||
|
||||
### Этап 1: Инфраструктура
|
||||
* Обновить [FederationDomainConfig](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#82-87), добавив туда `PublicKey` и `RemoteCapabilities`.
|
||||
* Реализовать `FederationHttpClient` для подписанных запросов между серверами.
|
||||
|
||||
### Этап 2: Проксирование (Storage Proxy)
|
||||
* Добавить в `Storage Module` поддержку типа `ExternalFile`.
|
||||
* Реализовать эндпоинт прокси-стриминга в модуле [Federation](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Configuration/SystemSettingsDto.cs#88-94).
|
||||
|
||||
### Этап 3: Доставка сообщений
|
||||
* Реализовать `FederationOutbox` (очередь доставки через Celery/Hangfire или простой BackgroundService).
|
||||
* Интегрировать `ICommandHandler<SendMessageCommand>` с вызовом службы федерации.
|
||||
31
backend/src/Docs/federation_proxy_storage_implementation.md
Normal file
31
backend/src/Docs/federation_proxy_storage_implementation.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Архитектура Федеративного Прокси-Хранилища (Remote Proxy Storage)
|
||||
|
||||
Для экономии места и обеспечения суверенитета данных, медиафайлы в федерации Knot Messager **не дублируются** на сервере-получателе.
|
||||
|
||||
## 1. Механизм работы
|
||||
|
||||
1. **Отправка (Sender)**:
|
||||
* Пользователь на `Server A` загружает файл. Файл сохраняется в S3/Local Storage сервера А.
|
||||
* В федеративном пакете передается метаданные: `{ "fileId": "uuid", "proxyUrl": "https://server-a.com/api/federation/v1/proxy/uuid" }`.
|
||||
|
||||
2. **Прием (Recipient)**:
|
||||
* `Server B` получает сообщение. Он **не скачивает** файл себе.
|
||||
* Для своего клиента Server B генерирует внутреннюю ссылку: `https://server-b.com/api/files/remote/server-a.com/uuid`.
|
||||
|
||||
3. **Просмотр (Client-Recipient)**:
|
||||
* Когда клиент на Server B хочет посмотреть картинку, он делает запрос на свой `Server B`.
|
||||
* `Server B` выступает как **Streaming Proxy**: он делает запрос к `Server A` (подписав его системным ключом), получает поток байтов и стримит его клиенту.
|
||||
|
||||
## 2. Безопасность и Валидация
|
||||
|
||||
* **Auth Signature**: Прокси-запрос между серверами обязательно подписывается RSA. Без валидной подписи Server А не отдаст файл.
|
||||
* **Privacy**: Только серверы участников чата могут проксировать файлы.
|
||||
* **Zero-Knowledge**: Если файлы зашифрованы E2EE, проксирующий сервер все равно видит только поток зашифрованных байтов.
|
||||
|
||||
---
|
||||
|
||||
## Задачи на текущую итерацию:
|
||||
|
||||
1. **Federation Endpoints**: Добавить `GET /v1/proxy/{id}` (отдача своего файла другому серверу).
|
||||
2. **Storage Module**: Добавить `RemoteFileController`, который умеет запрашивать файл у другого сервера и стримить его.
|
||||
3. **Messaging Integration**: При получении MediaMessage сохранять ссылку на удаленный прокси-хост.
|
||||
59
backend/src/Docs/storage_module_documentation.md
Normal file
59
backend/src/Docs/storage_module_documentation.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Модуль Хранилища (Storage) - Техническая Документация
|
||||
|
||||
## 1. Обзор
|
||||
**Модуль Storage** отвечает за централизованное управление файлами на платформе (аватары, медиа в чатах, вложения в историях). Модуль выступает абстракцией над S3-совместимым объектным хранилищем (в данный момент реализовано через **MinIO**) и интегрируется с криптографическим ядром для обеспечения безопасности файлов.
|
||||
|
||||
Ключевая особенность: модуль реализует концепцию **Zero-Knowledge At-Rest Encryption** — ни один файл не попадает на жесткие диски S3-сервера в открытом виде.
|
||||
|
||||
## 2. Архитектура и Процесс Загрузки
|
||||
|
||||
Сервис [S3FileStorageService](file:///e:/GIT/forkmessager/backend/src/Modules/Storage/Infrastructure/Storage/S3FileStorageService.cs#21-27) (реализация [IFileStorageService](file:///e:/GIT/forkmessager/backend/src/Shared/Knot.Shared.Kernel/Storage/IFileStorageService.cs#6-26) из Shared Kernel) прогоняет файлы через строго определенный пайплайн:
|
||||
|
||||
### 1. Временное буферизирование
|
||||
Поскольку поток загрузки (HTTP Request Stream) зачастую нельзя читать дважды (отсутствует `Seek`), файл сначала сохраняется во временную директорию ОС.
|
||||
|
||||
### 2. Абсолютная дедупликация (SHA-256)
|
||||
Модуль вычисляет криптографический `SHA-256` хэш от содержимого загруженного файла.
|
||||
Именно этот хэш (вместе с расширением) становится именем файла (Object Key) в корзине (Bucket) S3.
|
||||
* **Результат:** Если тысяча пользователей перешлет друг другу один и тот же мем (файл), физически в S3 он будет загружен и сохранен **ровно один раз**, колоссально экономя место на серверах.
|
||||
|
||||
### 3. "На лету" Шифрование (E2EE/At-Rest)
|
||||
Далее применяется `IEncryptionService` (обычно AES-256-CBC).
|
||||
На основе временного файла открывается `CryptoStream`, который шифрует байты файла "на лету" во второй временный файл. Случайный Вектор Инициализации (Initalization Vector, `IV`) генерируется сервисом уникально для каждого файла.
|
||||
|
||||
### 4. Загрузка в S3 с Метаданными
|
||||
Зашифрованный бинарник улетает в MinIO. Чувствительная информация сохраняется **совершенно безопасно** в заголовках метаданных объекта (MinIO Object MetaData):
|
||||
* `ContentType`: Исходный MIME-тип (например, `image/png`).
|
||||
* `OriginalFileName`: Закодированное исходное имя файла (Urlescaped).
|
||||
* `IV`: Вектор инициализации (Base64), необходимый для последующей расшифровки. Без секретного мастер-ключа приложения этот IV бесполезен.
|
||||
* `EncryptionAlgorithm`: Маркер алгоритма (`AES-256-CBC`).
|
||||
|
||||
В конце цикла бэкенд зачищает локальные временные файлы.
|
||||
|
||||
## 3. Процесс Скачивания и Кэширование (API)
|
||||
|
||||
Модуль предоставляет минималистичный REST API интерфейс, реализованный через **Carter** ([FilesEndpoints](file:///e:/GIT/forkmessager/backend/src/Modules/Storage/Presentation/Endpoints/FilesEndpoints.cs#11-54)).
|
||||
|
||||
### Эндпоинты
|
||||
| Метод | Маршрут | Параметры | Описание |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `GET` | `/api/files/{id}` | `?download=true` | Отдает расшифрованный файл. Поддерживает `Range` запросы для стриминга видео и музыки. |
|
||||
|
||||
### Механизм In-Memory Caching (Производительность)
|
||||
Расшифровка файлов (особенно видео) в реальном времени и постоянный запрос их из S3 съедает процессорное время и сетевой канал бэкенда.
|
||||
К эндпоинту скачивания подключен `IMemoryCache`:
|
||||
1. При первом запросе файла (например, аватара популярного пользователя), он скачивается из MinIO во временный файл ОС на бэкенде.
|
||||
2. Файл прогоняется через `CryptoStream` (с использованием `IV` из метаданных корзины S3) для дешифровки и выгрузки в `MemoryStream`.
|
||||
3. Байты готового файла, его `ContentType` и исходное имя кэшируются в RAM бэкенда на **30 минут** (Sliding Expiration).
|
||||
4. Все последующие запросы отдают готовые байты прямо из оперативной памяти без сетевого похода в MinIO и без повторной расшифровки.
|
||||
|
||||
### Поддержка Range-запросов
|
||||
API использует метод `Results.File(..., enableRangeProcessing: true)`. Это означает, что плееры в браузере или мобильном клиенте могут запрашивать видео кусками (например, `Range: bytes=1000-2000`), и бэкенд корректно отдаст только нужный чанк (из кэша или после скачивания), что делает модуль пригодным для видео-стриминга прямо из коробки.
|
||||
|
||||
---
|
||||
|
||||
## Итог
|
||||
Модуль представляет собой пуленепробиваемый шлюз для работы с медиа:
|
||||
* Обеспечивает полную конфиденциальность (ни один файл не хранится открытым в корзине облака).
|
||||
* Защищен от дубликатов (переиспользование за счет SHA-256 хэшей).
|
||||
* Высоко оптимизирован для раздачи горячего контента (In-Memory кэширование и поддержка HTTP Range Streaming).
|
||||
Reference in New Issue
Block a user