Сборка бэк

This commit is contained in:
Халимов Рустам
2026-03-27 15:45:34 +03:00
parent 16978c423c
commit 11f2b232a3
135 changed files with 2162 additions and 725 deletions

View File

@@ -0,0 +1,105 @@
# Документация модуля Profiles (Профили пользователей)
## Введение
Модуль `Knot.Modules.Profiles` отвечает за управление публичными данными пользователей, их аватарами и настройками отображения в мессенджере Knot Messager.
### Разделение ответственности (Auth vs Profiles)
В соответствии с принципами **DDD (Domain-Driven Design)** и **Чистой Архитектуры**, модуль Profiles отделен от модуля Auth:
* **Auth (Postgres):** Занимается идентификацией (логин, пароль, токены, email).
* **Profiles (MongoDB + S3):** Занимается представлением пользователя (имя, био, аватар, настройки видимости).
Это разделение позволяет изменять структуру профиля (добавлять новые социальные ссылки, поля "О себе") без выполнения миграций в основной реляционной БД.
---
## Архитектура хранения данных (Гибридный подход)
Модуль использует **гибридную схему хранения**, оптимизированную для производительности и гибкости:
1. **Metadata (MongoDB):** Документ `ProfileDocument` содержит все текстовые данные профиля. Использование NoSQL позволяет легко расширять схему данных.
2. **Media (S3/MinIO):** Файлы аватаров хранятся во внешнем объектном хранилище. В MongoDB хранится только `AvatarUrl` (ссылка на файл).
3. **Identity Link (UserId):** Поле `Id` в MongoDB-документе совпадает с `UserId` из PostgreSQL. Это единственный ключ для связи модулей.
---
## Доменная модель
### ProfileDocument (Aggregate Root)
Основная сущность в модуле. Находится в слое `Domain`.
* **Username:** Уникальный хендл пользователя.
* **DisplayName:** Публичное имя (может меняться).
* **Bio:** Короткая биографическая справка.
* **AvatarUrl:** Путь к файлу в формате `/api/files/{fileId}`.
* **HideStoryViews:** Флаг приватности (скрывать просмотр сторис от других).
### Доменные события
Модуль генерирует и слушает события для обеспечения согласованности:
* `UserRegisteredDomainEvent`: Слушается модулем Profiles (от Auth). При получении создается начальный документ профиля в MongoDB.
* `ProfileAvatarChangedDomainEvent`: Генерируется при смене аватара (для потенциальной очистки кэша).
---
## Ключевые возможности
### 1. Управление аватарами
Модуль предоставляет эндпоинты для загрузки и удаления аватаров:
* **Автоматическое кадрирование:** При вызове `/avatar/crop` используется библиотека `ImageSharp` для вырезания области изображения и ресайза до `400x400` пикселей в формате JPEG.
* **Авто-очистка:** При загрузке нового аватара модуль автоматически удаляет старый файл из S3, предотвращая появление "файлов-сирот".
### 2. Поиск пользователей
Реализован через `ProfileRepository` с использованием регулярных выражений MongoDB (case-insensitive) по полям `Username` и `DisplayName`.
### 3. Настройки приватности
Пользователь может управлять отображением своей активности (например, `HideStoryViews`), что сохраняется непосредственно в документе профиля.
---
## Структура проекта
```text
Knot.Modules.Profiles/
├── Application/
│ ├── Abstractions/ # Интерфейсы IProfileRepository, IAvatarStorageService
│ ├── Profiles/ # Case-обработчики (Handlers)
│ │ ├── Avatar/ # Загрузка, обрезка, удаление аватара
│ │ ├── GetUser/ # Получение данных профиля
│ │ ├── Search/ # Поиск по базе профилей
│ │ └── UpdateProfile/ # Обновление текстовых данных
│ └── Integration/ # Обработчики событий от других модулей
├── Domain/
│ ├── ProfileDocument.cs # Корень агрегата (Mongo Document)
│ └── Events/ # Доменные события
├── Infrastructure/
│ └── Database/ # Реализация репозиториев для MongoDB и S3
├── Presentation/
│ └── Endpoints/ # Carter-модули (API эндпоинты)
└── DependencyInjection.cs # Регистрация сервисов и MongoDB клиента
```
---
## Технические детали реализации
### Интеграция с S3 (MinIO)
В модуле реализован адаптер `AvatarStorageService`, который инкапсулирует работу с `IFileStorageService`. Он отвечает за:
1. Генерацию уникальных ключей файлов.
2. Шифрование контента (через Shared Infrastructure).
3. Очистку путей (преобразование URL в ID файла для удаления).
### Настройка MongoDB
Репозиторий `ProfileRepository` использует типизированную коллекцию `IMongoCollection<ProfileDocument>`. Первичный ключ `Id` мапится как строка (BsonType.String) для совместимости с `Guid` из .NET.
---
## API Эндпоинты (Примеры)
| Метод | Путь | Описание |
| :--- | :--- | :--- |
| `GET` | `/api/profiles/{id}` | Получить данные профиля пользователя. |
| `GET` | `/api/profiles/search?q=text` | Найти пользователей по имени или никнейму. |
| `PUT` | `/api/profiles/profile` | Обновить DisplayName, Bio, Birthday. |
| `POST` | `/api/profiles/avatar` | Простая загрузка аватара (FormFile). |
| `POST` | `/api/profiles/avatar/crop` | Загрузка с указанием координат обрезки (x, y, w, h). |
| `DELETE` | `/api/profiles/avatar` | Удаление текущего аватара и файла из S3. |
| `PUT` | `/api/profiles/settings` | Обновить настройки приватности профиля. |

View File

@@ -0,0 +1,129 @@
# Документация модуля Settings (Настройки)
## Введение
Модуль `Knot.Modules.Settings` является централизованным компонентом для управления глобальными конфигурациями и параметрами системы в архитектуре модульного монолита (Modular Monolith) Knot Messager. Он обеспечивает единый источник истины (Single Source of Truth) для настроек всех других модулей (WebRTC, Federation, Messages, Chats, Admin и др.).
## Основные принципы (DDD и Clean Architecture)
- **Независимость и Инкапсуляция:** Модуль выступает как самостоятельная единица и не зависит от реализаций других модулей. Другие модули ссылаются на `Settings` только через абстракции или контракты.
- **Единственный Источник Истины:** Любое изменение системной конфигурации (включение/отключение фич, лимиты, домены) происходит через этот модуль.
- **Событийная Модель (Domain Events):** При изменении настроек инфраструктура Settings генерирует доменное событие `SystemSettingsUpdatedDomainEvent`, позволяя другим модулям (например, `Federation` для отправки новых Capabilities) реагировать на изменения асинхронно, не создавая жестких связей.
## Структура модуля
Модуль спроектирован по принципам Чистой Архитектуры и разделен на слои:
```text
Knot.Modules.Settings/
├── Application/ # Слой Приложения (Application Layer)
│ ├── Settings/
│ │ ├── Abstractions/ # Определение интерфейсов доступа к каждому разделу настроек
│ │ │ ├── ISettingsService.cs
│ │ │ ├── IMessagesSettings.cs
│ │ │ ├── IWebRtcSettings.cs
│ │ │ └── ...
│ │ ├── DTOs/ # Структуры данных конфигураций
│ │ ├── SystemSettingsDto.cs
│ │ ├── PublicConfigDto.cs
│ │ ├── KlipyConfig.cs
│ │ └── ...
├── Domain/ # Доменный Слой (Domain Layer)
│ ├── Events/
│ │ └── SystemSettingsUpdatedDomainEvent.cs # Событие об изменении настроек
├── Infrastructure/ # Слой Инфраструктуры (Infrastructure Layer)
│ ├── Configuration/
│ │ └── SettingsService.cs # Реализация логики кэширования и обновления
├── DependencyInjection.cs # Регистрация модуля в DI (AddSettingsModule)
```
## Как это работает?
### 1. Интерфейсы сегрегации (Interface Segregation Principle - ISP)
Чтобы модули не зависели от огромного объекта настройки всей системы `SystemSettingsDto`, в слое `Application/Settings/Abstractions/ISettingsService.cs` мы разделили конфигурации на интерфейсы по специализациям:
- `ISystemSettings` — Системные и общие параметры.
- `IWebRtcSettings` — Настройки аудио/видео вызовов (WebRTC, TURN).
- `IMessagesSettings` — Настройки сообщений (файлы, ограничения).
- `IFederationSettings` — Настройки ActivityPub/Федерации.
- `IKlipySettings` — Настройки интеграции внешней библиотеки гифок (Klipy).
### 2. Доступ к настройкам из других модулей
Если, например, модулю бесед (Conversations) требуется проверить, включены ли интеграции Klipy, он инжектит специфичный интерфейс `IKlipySettings`, не получая доступа к настройкам админки или WebRTC:
```csharp
public class CreateStoryCommandHandler
{
private readonly IKlipySettings _klipySettings;
public CreateStoryCommandHandler(IKlipySettings klipySettings)
{
_klipySettings = klipySettings;
}
public async Task Handle(...)
{
if (!_klipySettings.Current.Enabled) {
// Отбросить логику
}
}
}
```
### 3. Хранение и шифрование данных
Реализация `SettingsService` хранит данные централизованно (в таблице `SystemSettings` БД через `SystemDbContext` в `Knot.Shared.Infrastructure`) в формате JSON.
Для безопасности вся JSON-строка **зашифрована** с использованием `IEncryptionService`.
Особенности хранения:
- **Кэширование**: `SettingsService` зарегистрирован как `Singleton`. При запуске сервера конфигурация загружается один раз из базы данных (или кеша/файла) и сохраняется в памяти.
- **Обновление**: Метод `UpdateSettingsAsync` сохраняет новый стейт в БД и обновляет объект в памяти (`_current`), предотвращая лишние запросы к базе данных при обычной работе мессенджера.
## Жизненный цикл обновления настроек (Управление)
Администратор изменяет настройки через UI (или API), после чего происходит следующий процесс:
1. Вызывается эндпоинт в модуле **Admin**: `PUT /api/admin/settings`.
2. Команда `UpdateSettingsCommand` валидирует входные данные.
3. Команда обращается к `ISettingsService.UpdateSettingsAsync(newSettings)`.
4. `SettingsService` шифрует новые настройки, обновляет запись в БД, и заменяет кэш в оперативной памяти.
5. После успешного сохранения, диспетчер MediatR (в `Admin` модуле) публикует `SystemSettingsUpdatedDomainEvent`.
6. Событие отлавливается через `INotificationHandler<SystemSettingsUpdatedDomainEvent>` независимыми слушателями, например:
- Модулем `Federation` для рассылки всем "соседним" инстансам новых возможностей `Capabilities`.
## Регистрация модуля
При старте приложения в `Host/Program.cs` модуль инициализируется:
```csharp
// Регистрация сервисов модуля настроек
builder.Services.AddSettingsModule(builder.Configuration);
// Дополнительно можно вызывать Initialize(...) чтобы загрузить кэш из БД при старте
```
Под капотом `DependencyInjection.cs`:
```csharp
public static IServiceCollection AddSettingsModule(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<SettingsService>();
// Привязываем конкретные абстракции к одному Singleton инстансу
services.AddSingleton<ISettingsService>(sp => sp.GetRequiredService<SettingsService>());
services.AddSingleton<IMessagesSettings>(sp => sp.GetRequiredService<SettingsService>());
// ... и остальные
}
```
## Публичная конфигурация клиента (PublicConfig)
Для клиентской части (Frontend) предусмотрен специальный DTO объект `PublicConfigDto`.
Это subset (урезанная часть) от `SystemSettingsDto`, которая не содержит чувствительной информации о сервере и ключах:
- Доступные лимиты медиа файлов.
- Включены ли аудио/видео звонки.
- Включена ли федерация для поиска глобальных пользователей.
Таким образом, клиент может запросить `GET /api/app/config`, а бэкенд отдаст безопасный набор доступных разрешений через маппинг `SettingsService.Current`.