diff --git a/EventHubBackSpec.md b/EventHubBackSpec.md index f45b0d9..44bfb8d 100644 --- a/EventHubBackSpec.md +++ b/EventHubBackSpec.md @@ -1,12 +1,12 @@ -ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB -Версия: 1.2 (расширенная гибридная модель + ролевая модель администрирования) +ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB +Версия: 1.3 (актуальная реализация: раздельная JWT-аутентификация, версионированное админ-API, расширенная инфраструктура) -1. ЦЕЛИ И НАЗНАЧЕНИЕ - EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. +## 1. ЦЕЛИ И НАЗНАЧЕНИЕ +EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. -2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ +## 2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ -2.1. Календари +### 2.1. Календари - CRUD календаря (название, описание, теги, владелец) - Расшаривание по ссылке (публичная/приватная) - Приглашение пользователей с правами "запись" или "администрирование" @@ -14,9 +14,9 @@ - Гибкое подтверждение заявок: auto (автоматически), manual (вручную), timeout (авто через N секунд) - Теги календаря, рейтинг (средняя оценка, количество голосов) -2.2. События (расширенная версия с повторяющимися событиями) +### 2.2. События (расширенная версия с повторяющимися событиями) -2.2.1. Типы событий и модель хранения +#### 2.2.1. Типы событий и модель хранения События делятся на два типа: - single — одиночное событие, имеет конкретные дату и время начала, длительность. - recurring — повторяющееся событие (серия), определяется мастер-записью и правилом повторения. @@ -24,21 +24,21 @@ Для эффективного хранения и быстрого поиска применяется гибридная модель с отложенной материализацией вхождений: - Каждая повторяющаяся серия представлена одной мастер-записью в таблице events с полем event_type = recurring и полем recurrence_rule, содержащим правило повторения в формате, совместимом с iCalendar RRULE (или упрощённый JSON-представление: freq, interval, byday, until и т.д.). - Конкретные вхождения (экземпляры) серии материализуются в таблице events только при необходимости: - * когда на данное вхождение записался хотя бы один участник; - * когда администратор вручную редактирует это вхождение (перенос времени, отмена, смена специалиста); - * (опционально) при наступлении даты вхождения для архивных целей. + * когда на данное вхождение записался хотя бы один участник; + * когда администратор вручную редактирует это вхождение (перенос времени, отмена, смена специалиста); + * (опционально) при наступлении даты вхождения для архивных целей. - Материализованное вхождение имеет: - * event_type = single (или специальный флаг is_instance = true), - * ссылку master_id на родительскую мастер-запись, - * собственные start_time и duration, которые могут отличаться от вычисленных по правилу (например, при переносе конкретного дня). + * event_type = single (или специальный флаг is_instance = true), + * ссылку master_id на родительскую мастер-запись, + * собственные start_time и duration, которые могут отличаться от вычисленных по правилу (например, при переносе конкретного дня). - Отменённые вхождения из серии (исключения) хранятся в отдельной таблице recurrence_exceptions с указанием master_id, original_start_time и action = cancel | reschedule. -2.2.2. Правила генерации вхождений при поиске +#### 2.2.2. Правила генерации вхождений при поиске При поиске событий на заданный интервал дат (например, /v1/events?from=...&to=...) система: 1. Выбирает все одиночные события (event_type = single), попадающие в интервал. 2. Выбирает все активные мастер-записи (event_type = recurring), у которых: - - дата первого вхождения ≤ конец интервала, - - правило повторения допускает вхождения внутри интервала (учёт until или ограничения по количеству повторений). + - дата первого вхождения ≤ конец интервала, + - правило повторения допускает вхождения внутри интервала (учёт until или ограничения по количеству повторений). 3. Для каждой мастер-записи генерирует список вхождений в пределах запрошенного интервала (используя встроенную функцию разбора RRULE или собственный алгоритм). 4. Из сгенерированного списка исключаются вхождения, присутствующие в таблице recurrence_exceptions с действием cancel. 5. Если для какого-либо вхождения уже существует материализованная запись в таблице events (по совпадению master_id и start_time), то используются данные материализованного экземпляра (специалист, длительность, статус) вместо вычисленных по шаблону. @@ -46,22 +46,21 @@ Ограничение: глубина генерации вхождений для одного запроса ограничена 1000 элементов; для длительных серий применяется пагинация по датам. -2.2.3. Материализация при записи участника +#### 2.2.3. Материализация при записи участника При попытке записаться на вхождение повторяющегося события (POST /v1/events/:event_id/join): - Если event_id ссылается на мастер-запись (event_type = recurring), то в теле запроса обязательно передаётся occurrence_start — конкретное время вхождения, на которое производится запись. - Система в одной транзакции Mnesia: - * Проверяет, существует ли материализованное вхождение с master_id = EventId и start_time = OccurrenceStart. - * Если нет — создаёт новую запись в таблице events (тип single, master_id = EventId), копируя общие атрибуты мастер-записи (название, описание, календарь, специалист). - * Создаёт бронирование (booking), привязанное к идентификатору материализованного вхождения. + * Проверяет, существует ли материализованное вхождение с master_id = EventId и start_time = OccurrenceStart. + * Если нет — создаёт новую запись в таблице events (тип single, master_id = EventId), копируя общие атрибуты мастер-записи (название, описание, календарь, специалист). + * Создаёт бронирование (booking), привязанное к идентификатору материализованного вхождения. - Дальнейшие действия по подтверждению заявки выполняются уже с материализованным экземпляром. -2.2.4. Изменение и удаление серий +#### 2.2.4. Изменение и удаление серий - При редактировании мастер-записи (изменение названия, описания, правила повторения) обновляется только сама мастер-запись. Уже существующие материализованные вхождения остаются неизменными (сохраняют старые значения атрибутов). Это поведение может быть изменено администратором через специальный флаг «применить ко всем будущим вхождениям». - При удалении мастер-записи все связанные материализованные вхождения также помечаются удалёнными (или удаляются каскадно), а бронирования на будущие вхождения аннулируются с уведомлением участников. -2.2.5. Структура записей (records.hrl) +#### 2.2.5. Структура записей (records.hrl) ```erlang -%% Событие (может быть мастером или экземпляром) -record(event, { id :: binary(), calendar_id :: binary(), @@ -81,7 +80,6 @@ updated_at :: calendar:datetime() }). -%% Исключение из повторений -record(recurrence_exception, { master_id :: binary(), original_start :: calendar:datetime(), @@ -89,115 +87,119 @@ new_start :: calendar:datetime() | undefined }). ``` - -## 2.2.6. Требования к реализации -- Разбор и генерация RRULE должны быть реализованы в отдельном модуле `logic_recurrence` без внешних зависимостей (чистый Erlang). +#### 2.2.6. Требования к реализации +- Разбор и генерация RRULE должны быть реализованы в отдельном модуле logic_recurrence без внешних зависимостей (чистый Erlang). - Все операции с материализацией и записью участников должны выполняться в транзакциях Mnesia для обеспечения консистентности. -- Для ускорения поиска материализованных вхождений по мастеру и дате создаётся составной индекс в Mnesia: `{master_id, start_time}` (через `mnesia:add_table_index/2` или хранение в ets с ключом `{master_id, start_time}`). +- Для ускорения поиска материализованных вхождений по мастеру и дате создаётся составной индекс в Mnesia: {master_id, start_time} (через mnesia:add_table_index/2 или хранение в ets с ключом {master_id, start_time}). -## 2.3. Запись участников и подтверждение +### 2.3. Запись участников и подтверждение - Запись через календарь - Подтверждение согласно политике календаря - Уведомления участника и владельца - Привязка события к специалисту (отдельная запись в календаре специалиста) -## 2.4. Отзывы и рейтинги +### 2.4. Отзывы и рейтинги - Только участники событий могут оставлять отзывы - Оценка 1-5, текстовый комментарий - Отзывы на событие или на календарь - Модерация отзывов (администраторы могут скрывать) -## 2.5. Поиск и фильтрация +### 2.5. Поиск и фильтрация - По тексту, по тегам, по гео-позиции (радиус), по дате/времени -## 2.6. Расширенные возможности +### 2.6. Расширенные возможности - Экспорт в Google Calendar (через OAuth2) - Экспорт в Apple Calendar (ICS-файл) - Геокодирование (адрес -> координаты) через внешний API (заглушка / Nominatim) -## 2.7. Модерация и безопасность +### 2.7. Модерация и безопасность - Жалобы на календари, события, отзывы - Автоматическая модерация по ключевым словам и по порогу жалоб - Ручная заморозка / разморозка администратором - Бан-лист слов - Аудит действий администраторов (детальная информация: кто, что, когда, IP, причина) -## 2.8. Баг-трекер (автоматический) +### 2.8. Баг-трекер (автоматический) - При ошибке создаётся тикет, группировка по хэшу ошибки - Учёт количества повторений - Уведомление пользователя при создании и при закрытии тикета - Доступ только у администраторов -## 2.9. Платная подписка -- Личное использование бесплатно (personal) -- Коммерческое использование платно (commercial) +### 2.9. Платная подписка +- Личное использование бесплатно (personal), коммерческое — платно (commercial) - Пробный период 30 дней -- Планы подписки: 1, 3, 6, 12 месяцев +- Планы подписки: monthly, quarterly, biannual, annual - Заглушка платежного шлюза (для тестирования) - Автоматическое истечение подписки +- Автоматическое понижение календарей до personal при истечении -## 2.10. Административная панель +### 2.10. Административная панель - Отдельный HTTPS-сервер (порт 8445) и отдельный WSS (порт 8446) - Управление календарями, событиями, отзывами, жалобами, тикетами, бан-словами - Статистика, информация о нодах кластера - WebSocket-уведомления администраторам -### 2.10.1. Ролевая модель администраторов -Вводится три встроенные роли администраторов с различным объёмом прав: +#### 2.10.1. Ролевая модель администраторов (реализована) +В проекте реализована трехуровневая ролевая модель: `superadmin`, `moderator`, `support`. Проверка ролей выполняется в каждом административном обработчике через `handler_auth:authenticate/1` и вспомогательную функцию `is_admin/1`. -| Роль | Идентификатор | Права | -|--------------|---------------|-------------------------------------------------------------------------------------------| -| Суперадмин | `superadmin` | Полный доступ ко всем модулям, управление ролями других админов, просмотр аудита, системные метрики. | -| Модератор | `moderator` | Модерация контента (события, жалобы, отзывы), блокировка/разблокировка пользователей, работа с баг-трекером. | -| Поддержка | `support` | Read-only доступ к пользователям и событиям, обработка жалоб, создание и обновление багов. | +#### 2.10.2. Эндпоинты для управления ролями и аудитом +- `GET /v1/admin/me` — получение текущей роли и разрешений администратора. +- `GET /v1/admin/admins` — список всех администраторов с ролями (только для `superadmin`). +- `PUT /v1/admin/admins/:id` — изменение роли администратора (только для `superadmin`). +- `POST /v1/admin/admins` — приглашение нового администратора с назначением роли (только для `superadmin`). +- `GET /v1/admin/audit` — журнал действий администраторов с фильтрацией по дате, пользователю, действию (только для `superadmin`). -**Примечание:** На этапе MVP поддерживается только роль `admin` (эквивалент `superadmin`). Полноценная ролевая модель будет внедрена в ближайших итерациях. - -### 2.10.2. Эндпоинты для управления ролями и аудитом -- `GET /admin/me` (или `GET /api/auth/me`) — получение текущей роли и разрешений администратора. -- `GET /admin/admins` — список всех администраторов с ролями (только для `superadmin`). -- `PUT /admin/admins/:id` — изменение роли администратора (только для `superadmin`). -- `POST /admin/admins` — приглашение нового администратора с назначением роли (только для `superadmin`). -- `GET /admin/audit` — журнал действий администраторов с фильтрацией по дате, пользователю, действию (только для `superadmin`). - -### 2.10.3. Статистика для дашборда с учётом ролей +#### 2.10.3. Статистика для дашборда с учётом ролей +Эндпоинт: `GET /v1/admin/stats`. Возвращает JSON, содержимое которого фильтруется в соответствии с ролью вызывающего. - `superadmin` — системные метрики: все пользователи, события, жалобы, баги за период, графики регистраций/событий по дням, активность администраторов. - `moderator` — собственные обработанные жалобы/события (количество, статусы, время реакции), общая статистика по модерации. - `support` — количество открытых багов и жалоб, назначенных на текущего сотрудника, персональные задачи. -Эндпоинт: `GET /admin/stats` (или `/api/stats` с авторизацией). Возвращает JSON, содержимое которого фильтруется в соответствии с ролью вызывающего. +### 2.11. Real-time уведомления (WebSocket) +- Пользователи: порт 8081, маршрут `/ws`, подписка на календарь, получение обновлений +- Администраторы: порт 8446, маршрут `/admin/ws`, подписка на глобальные уведомления (жалобы, авто-заморозки) -## 2.11. Real-time уведомления (WebSocket) -- Пользователи: подписка на календарь, получение обновлений -- Администраторы: подписка на глобальные уведомления (жалобы, авто-заморозки) +### 2.12. Инфраструктура развертывания (Docker Compose) +- Балансировщик Traefik с поддержкой HTTPS/WSS, WAF (Coraza), Rate Limiting и Failover +- Кластер из трех нод Erlang с автоматическим обнаружением (DNS-лукап) +- Мониторинг: Prometheus (метрики), Grafana (дашборды), LogLynx (аналитика логов) +- Ротация логов (logrotate) +- Административный SPA (EventHubFrontAdmin) как отдельный сервис +- Сервис-заглушка (Fallback) для отказоустойчивости -## 3.1. Производительность и масштабирование +## 3. НЕФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ + +### 3.1. Производительность и масштабирование - 100 000+ пользователей - Горизонтальное масштабирование (увеличение нод) - Mnesia с дисковыми копиями на нескольких нодах - Отдельные ноды для исторических данных (disc_only_copies) - Пагинация всех списков +- Автоматическое обнаружение узлов через DNS-лукап (libcluster в перспективе) -## 3.2. Надёжность +### 3.2. Надёжность - Супервизорное дерево OTP - Let it crash – быстрый перезапуск процессов - Автоматическое восстановление после падения ноды (Mnesia) -## 3.3. Безопасность -- JWT с ролью (user, admin; в будущем — superadmin, moderator, support) +### 3.3. Безопасность +- Раздельная JWT-аутентификация: + - Пользовательские токены: секрет `JWT_SECRET`, audience = `<<"user">>`, эндпоинт `/v1/login` + - Административные токены: секрет `ADMIN_JWT_SECRET`, audience = `<<"admin">>`, эндпоинт `/v1/admin/login` + - Проверка ролей: `superadmin`, `moderator`, `support` - HTTPS / WSS (самоподписанный сертификат для dev, реальный для prod) - Argon2 для хеширования паролей (erlang-argon2) +- Refresh token реализован (хранение в Mnesia, сессии) - OAuth2 для Google (опционально) -- Refresh token (запланирован) - При блокировке пользователя или отклонении сущности обязательно сохранять причину (поле `reason`) -## 3.4. Наблюдаемость +### 3.4. Наблюдаемость - Healthcheck эндпоинт (`GET /health`) - Логирование (JSON, ротация) -- Prometheus метрики (запланированы на уровне приложения, в настоящее время доступны через балансировщик) -- Аудит действий администраторов (обязателен, см. п. 2.10.2) +- Prometheus метрики (реализованы на уровне приложения через `prometheus_cowboy`) +- Аудит действий администраторов (запись `admin_audit`) -## 3.5. CI/CD +### 3.5. CI/CD - Drone CI (или GitLab CI / GitHub Actions) - EUnit + Common Test + Tsung - Сборка релиза @@ -210,9 +212,10 @@ - Сборка: rebar3 3.27.0 - HTTP/WebSocket: Cowboy 2.10.0 - JSON: jsx 3.1.0 -- JWT: jose (реализовано) -- Хеширование паролей: erlang-argon2 1.0.0 +- JWT: jose 1.11.10 +- Хеширование паролей: erlang-argon2 1.2.0 - Тестирование: meck 0.9.2, gun 2.0.0 +- Мониторинг: prometheus_cowboy 0.2.0 - Нагрузочное тестирование: Tsung 1.8.0 - CI/CD: Drone 2.0+ - Контейнеризация: Docker 20.10+ @@ -220,65 +223,77 @@ ## 5. ИЕРАРХИЧЕСКАЯ СТРУКТУРА КОДА ``` -src/ -├── infra/ # инфраструктура (приложение, супервизоры, аутентификация, подписки) -├── core/ # слой доступа к данным (DAO) и модели -├── logic/ # бизнес-логика (независимая от HTTP/WS) -├── services/ # внешние сервисы (заглушки/интеграции) -└── handlers/ # обработчики HTTP/WebSocket +src/ test/ +├── infra/ ├── unit/ # EUnit тесты +│ ├── eventhub_auth.erl # JWT └── api/ # Common Test интеграционные тесты +│ └── ... +├── core/ # слой доступа к данным (DAO) +├── logic/ # бизнес-логика +├── services/ # внешние сервисы +├── handlers/ # обработчики HTTP/WebSocket +│ ├── admin/ # административные обработчики +│ └── ... +└── middlewares/ # промежуточные слои (CORS, аутентификация) ``` **Правила:** - Модули `handlers/` не содержат бизнес-логики. - Модули `logic/` не знают о HTTP/WS. - Модули `core/` (DAO) работают только с Mnesia. +- Модуль `infra/eventhub_auth.erl` реализует раздельную JWT-аутентификацию (пользователи и администраторы). - Модули `services/` могут быть заменены на реальные реализации без изменения остального кода. - Заголовочный файл `include/records.hrl` содержит все записи таблиц Mnesia. ## 6. ОСНОВНЫЕ API (КРАТКО) ### Пользовательские (порт 8080) -- `POST /v1/register` -- `POST /v1/login` -- `GET /v1/user/me` -- `POST /v1/calendars` + CRUD -- `POST /v1/calendars/:id/events` + CRUD -- `POST /v1/events/:id/join` -- `POST /v1/events/:id/confirm/:user_id` -- `POST /v1/reviews` -- `POST /v1/reports` -- `POST /v1/subscription/activate` -- `POST /v1/events/:id/export` (Google) -- `GET /v1/events/:id/ical` (Apple) -- `GET /health` +**Публичные:** `GET /health`, `POST /v1/register`, `POST /v1/login`, `POST /v1/refresh` +**Авторизованные:** `GET /v1/user/me`, `GET /v1/user/bookings`, `GET /v1/user/reviews`, `GET /v1/search` +**Календари:** `POST /v1/calendars` + CRUD, `GET /v1/calendars/:id`, `PUT /v1/calendars/:id`, `DELETE /v1/calendars/:id` +**События:** `POST /v1/calendars/:calendar_id/events`, `GET /v1/events/:id`, `PUT /v1/events/:id`, `DELETE /v1/events/:id`, `GET /v1/events/:id/occurrences`, `POST /v1/events/:id/join`, `POST /v1/events/:id/confirm/:user_id`, `GET /v1/events/:id/bookings` +**Бронирования:** `GET /v1/bookings/:id`, `PUT /v1/bookings/:id`, `DELETE /v1/bookings/:id` +**Отзывы:** `POST /v1/reviews`, `GET /v1/reviews/:id`, `PUT /v1/reviews/:id`, `DELETE /v1/reviews/:id` +**Жалобы:** `POST /v1/reports` +**Тикеты:** `POST /v1/tickets`, `GET /v1/tickets`, `GET /v1/tickets/:id`, `PUT /v1/tickets/:id`, `DELETE /v1/tickets/:id` +**Подписки:** `GET /v1/subscription`, `POST /v1/subscription` +**Метрики:** `GET /metrics/[:registry]` -**WebSocket (порт 8081):** `ws://localhost:8081/ws?token=...` +WebSocket (порт 8081): `ws://localhost:8081/ws?token=` ### Административные (порт 8445) -- `GET /admin/stats/overview` (или `/admin/stats`) — статистика с учётом роли -- `GET /admin/nodes` -- `GET /admin/calendars`, `PUT /admin/calendars/:id/freeze`, `/unfreeze` -- `GET /admin/events`, `PUT /admin/events/:id/freeze`, `PATCH /admin/events/:id/approve` (модерация) -- `GET /admin/reviews`, `PUT /admin/reviews/:id/hide` -- `GET /admin/reports` (complaints), `PUT /admin/reports/:id/status` -- `GET /admin/tickets` (bugs), `POST /admin/tickets`, `PUT /admin/tickets/:id/status`, `DELETE /admin/tickets/:id` -- `GET /admin/banned_words`, `POST /admin/banned_words`, `DELETE /admin/banned_words` -- `GET /admin/admins` (только `superadmin`) -- `PUT /admin/admins/:id` (только `superadmin`) -- `POST /admin/admins` (только `superadmin`) -- `GET /admin/audit` (только `superadmin`) -- `GET /admin/me` (текущий админ, его роль и права) +**Версионирование:** Все административные эндпоинты имеют префикс `/v1/admin/`. -**Административный WebSocket (порт 8446):** `wss://localhost:8446/admin/ws?token=...` +**Базовые:** `GET /v1/admin/health`, `GET /v1/admin/stats`, `POST /v1/admin/login` +**Пользователи:** `GET /v1/admin/users`, `GET /v1/admin/users/:id`, `PUT /v1/admin/users/:id`, `DELETE /v1/admin/users/:id` +**Отчёты (жалобы):** `GET /v1/admin/reports`, `GET /v1/admin/reports/:id`, `PUT /v1/admin/reports/:id` +**Отзывы:** `GET /v1/admin/reviews/:id`, `PUT /v1/admin/reviews/:id` (hide/show) +**Бан-слова:** `GET /v1/admin/banned-words`, `POST /v1/admin/banned-words`, `DELETE /v1/admin/banned-words/:word` +**Тикеты:** `GET /v1/admin/tickets`, `POST /v1/admin/tickets`, `GET /v1/admin/tickets/:id`, `PUT /v1/admin/tickets/:id`, `DELETE /v1/admin/tickets/:id`, `GET /v1/admin/tickets/stats` +**Подписки:** `GET /v1/admin/subscriptions`, `POST /v1/admin/subscriptions`, `GET /v1/admin/subscriptions/:id`, `PUT /v1/admin/subscriptions/:id`, `DELETE /v1/admin/subscriptions/:id` +**Модерация:** `PUT /v1/admin/:target_type/:id` (target_type: calendar, event, review, user; action: freeze/unfreeze, hide/show, block/unblock) + +Административный WebSocket (порт 8446): `wss://localhost:8446/admin/ws?token=` + +## 6.1. Аутентификация и авторизация +Модуль `infra/eventhub_auth.erl` реализует раздельную JWT-аутентификацию: +- Пользовательские токены выпускаются через `POST /v1/login` с audience `<<"user">>` и секретом `JWT_SECRET`. Проверяются обработчиками через `handler_auth:authenticate/1` → `logic_auth:verify_jwt/1`. +- Административные токены выпускаются через `POST /v1/admin/login` с audience `<<"admin">>` и секретом `ADMIN_JWT_SECRET`. Проверяются обработчиками через `handler_auth:authenticate/1` с последующей проверкой роли `is_admin/1`. + +Обработчики порта 8445 используют единый middleware `handler_auth:authenticate/1`, который извлекает токен из заголовка `Authorization: Bearer ...`, верифицирует его через `logic_auth:verify_jwt/1` и возвращает `{ok, UserId, Req}` или `{error, Code, Message, Req}`. ## 7. ВЕРСИОНИРОВАНИЕ И СТАТУС -Текущая версия: **1.2** (MVP, альфа, включает гибридную модель повторяющихся событий и базовую ролевую модель администрирования). +Текущая версия: **1.3** (MVP, альфа). Включает: +- Гибридную модель повторяющихся событий +- Раздельную JWT-аутентификацию (пользователи/администраторы) +- Полноценную ролевую модель администрирования +- Версионированное админ-API (`/v1/admin/...`) +- Расширенную инфраструктуру (Traefik, WAF, мониторинг, failover) ## 8. ОГРАНИЧЕНИЯ И ДОПУЩЕНИЯ - В разработке используются самоподписанные SSL-сертификаты. - Для production требуется реальный сертификат и настройка OAuth2 для Google. - Заглушки внешних сервисов должны быть заменены перед запуском. -- Полноценное разграничение прав администраторов (`superadmin`, `moderator`, `support`) ожидается в следующем релизе; пока используется универсальная роль `admin`. +- Автоматическое обнаружение узлов через libcluster находится в перспективе; текущая реализация использует статический JOIN_NODES с возможностью подключения через DNS-лукап. ## 9. ТРЕБОВАНИЯ К ОКРУЖЕНИЮ - Erlang/OTP 28.2