This commit is contained in:
2026-04-28 13:19:46 +03:00
parent 4db15443a1
commit 59a2e5a397

View File

@@ -1,12 +1,12 @@
ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB
Версия: 1.2 (расширенная гибридная модель + ролевая модель администрирования) Версия: 1.3 (актуальная реализация: раздельная JWT-аутентификация, версионированное админ-API, расширенная инфраструктура)
1. ЦЕЛИ И НАЗНАЧЕНИЕ ## 1. ЦЕЛИ И НАЗНАЧЕНИЕ
EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы.
2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ ## 2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ
2.1. Календари ### 2.1. Календари
- CRUD календаря (название, описание, теги, владелец) - CRUD календаря (название, описание, теги, владелец)
- Расшаривание по ссылке (публичная/приватная) - Расшаривание по ссылке (публичная/приватная)
- Приглашение пользователей с правами "запись" или "администрирование" - Приглашение пользователей с правами "запись" или "администрирование"
@@ -14,9 +14,9 @@
- Гибкое подтверждение заявок: auto (автоматически), manual (вручную), timeout (авто через N секунд) - Гибкое подтверждение заявок: auto (автоматически), manual (вручную), timeout (авто через N секунд)
- Теги календаря, рейтинг (средняя оценка, количество голосов) - Теги календаря, рейтинг (средняя оценка, количество голосов)
2.2. События (расширенная версия с повторяющимися событиями) ### 2.2. События (расширенная версия с повторяющимися событиями)
2.2.1. Типы событий и модель хранения #### 2.2.1. Типы событий и модель хранения
События делятся на два типа: События делятся на два типа:
- single — одиночное событие, имеет конкретные дату и время начала, длительность. - single — одиночное событие, имеет конкретные дату и время начала, длительность.
- recurring — повторяющееся событие (серия), определяется мастер-записью и правилом повторения. - recurring — повторяющееся событие (серия), определяется мастер-записью и правилом повторения.
@@ -33,7 +33,7 @@
* собственные start_time и duration, которые могут отличаться от вычисленных по правилу (например, при переносе конкретного дня). * собственные start_time и duration, которые могут отличаться от вычисленных по правилу (например, при переносе конкретного дня).
- Отменённые вхождения из серии (исключения) хранятся в отдельной таблице recurrence_exceptions с указанием master_id, original_start_time и action = cancel | reschedule. - Отменённые вхождения из серии (исключения) хранятся в отдельной таблице recurrence_exceptions с указанием master_id, original_start_time и action = cancel | reschedule.
2.2.2. Правила генерации вхождений при поиске #### 2.2.2. Правила генерации вхождений при поиске
При поиске событий на заданный интервал дат (например, /v1/events?from=...&to=...) система: При поиске событий на заданный интервал дат (например, /v1/events?from=...&to=...) система:
1. Выбирает все одиночные события (event_type = single), попадающие в интервал. 1. Выбирает все одиночные события (event_type = single), попадающие в интервал.
2. Выбирает все активные мастер-записи (event_type = recurring), у которых: 2. Выбирает все активные мастер-записи (event_type = recurring), у которых:
@@ -46,7 +46,7 @@
Ограничение: глубина генерации вхождений для одного запроса ограничена 1000 элементов; для длительных серий применяется пагинация по датам. Ограничение: глубина генерации вхождений для одного запроса ограничена 1000 элементов; для длительных серий применяется пагинация по датам.
2.2.3. Материализация при записи участника #### 2.2.3. Материализация при записи участника
При попытке записаться на вхождение повторяющегося события (POST /v1/events/:event_id/join): При попытке записаться на вхождение повторяющегося события (POST /v1/events/:event_id/join):
- Если event_id ссылается на мастер-запись (event_type = recurring), то в теле запроса обязательно передаётся occurrence_start — конкретное время вхождения, на которое производится запись. - Если event_id ссылается на мастер-запись (event_type = recurring), то в теле запроса обязательно передаётся occurrence_start — конкретное время вхождения, на которое производится запись.
- Система в одной транзакции Mnesia: - Система в одной транзакции Mnesia:
@@ -55,13 +55,12 @@
* Создаёт бронирование (booking), привязанное к идентификатору материализованного вхождения. * Создаёт бронирование (booking), привязанное к идентификатору материализованного вхождения.
- Дальнейшие действия по подтверждению заявки выполняются уже с материализованным экземпляром. - Дальнейшие действия по подтверждению заявки выполняются уже с материализованным экземпляром.
2.2.4. Изменение и удаление серий #### 2.2.4. Изменение и удаление серий
- При редактировании мастер-записи (изменение названия, описания, правила повторения) обновляется только сама мастер-запись. Уже существующие материализованные вхождения остаются неизменными (сохраняют старые значения атрибутов). Это поведение может быть изменено администратором через специальный флаг «применить ко всем будущим вхождениям». - При редактировании мастер-записи (изменение названия, описания, правила повторения) обновляется только сама мастер-запись. Уже существующие материализованные вхождения остаются неизменными (сохраняют старые значения атрибутов). Это поведение может быть изменено администратором через специальный флаг «применить ко всем будущим вхождениям».
- При удалении мастер-записи все связанные материализованные вхождения также помечаются удалёнными (или удаляются каскадно), а бронирования на будущие вхождения аннулируются с уведомлением участников. - При удалении мастер-записи все связанные материализованные вхождения также помечаются удалёнными (или удаляются каскадно), а бронирования на будущие вхождения аннулируются с уведомлением участников.
2.2.5. Структура записей (records.hrl) #### 2.2.5. Структура записей (records.hrl)
```erlang ```erlang
%% Событие (может быть мастером или экземпляром)
-record(event, { -record(event, {
id :: binary(), id :: binary(),
calendar_id :: binary(), calendar_id :: binary(),
@@ -81,7 +80,6 @@
updated_at :: calendar:datetime() updated_at :: calendar:datetime()
}). }).
%% Исключение из повторений
-record(recurrence_exception, { -record(recurrence_exception, {
master_id :: binary(), master_id :: binary(),
original_start :: calendar:datetime(), original_start :: calendar:datetime(),
@@ -89,115 +87,119 @@
new_start :: calendar:datetime() | undefined new_start :: calendar:datetime() | undefined
}). }).
``` ```
#### 2.2.6. Требования к реализации
## 2.2.6. Требования к реализации - Разбор и генерация RRULE должны быть реализованы в отдельном модуле logic_recurrence без внешних зависимостей (чистый Erlang).
- Разбор и генерация RRULE должны быть реализованы в отдельном модуле `logic_recurrence` без внешних зависимостей (чистый Erlang).
- Все операции с материализацией и записью участников должны выполняться в транзакциях Mnesia для обеспечения консистентности. - Все операции с материализацией и записью участников должны выполняться в транзакциях 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, текстовый комментарий - Оценка 1-5, текстовый комментарий
- Отзывы на событие или на календарь - Отзывы на событие или на календарь
- Модерация отзывов (администраторы могут скрывать) - Модерация отзывов (администраторы могут скрывать)
## 2.5. Поиск и фильтрация ### 2.5. Поиск и фильтрация
- По тексту, по тегам, по гео-позиции (радиус), по дате/времени - По тексту, по тегам, по гео-позиции (радиус), по дате/времени
## 2.6. Расширенные возможности ### 2.6. Расширенные возможности
- Экспорт в Google Calendar (через OAuth2) - Экспорт в Google Calendar (через OAuth2)
- Экспорт в Apple Calendar (ICS-файл) - Экспорт в Apple Calendar (ICS-файл)
- Геокодирование (адрес -> координаты) через внешний API (заглушка / Nominatim) - Геокодирование (адрес -> координаты) через внешний API (заглушка / Nominatim)
## 2.7. Модерация и безопасность ### 2.7. Модерация и безопасность
- Жалобы на календари, события, отзывы - Жалобы на календари, события, отзывы
- Автоматическая модерация по ключевым словам и по порогу жалоб - Автоматическая модерация по ключевым словам и по порогу жалоб
- Ручная заморозка / разморозка администратором - Ручная заморозка / разморозка администратором
- Бан-лист слов - Бан-лист слов
- Аудит действий администраторов (детальная информация: кто, что, когда, IP, причина) - Аудит действий администраторов (детальная информация: кто, что, когда, IP, причина)
## 2.8. Баг-трекер (автоматический) ### 2.8. Баг-трекер (автоматический)
- При ошибке создаётся тикет, группировка по хэшу ошибки - При ошибке создаётся тикет, группировка по хэшу ошибки
- Учёт количества повторений - Учёт количества повторений
- Уведомление пользователя при создании и при закрытии тикета - Уведомление пользователя при создании и при закрытии тикета
- Доступ только у администраторов - Доступ только у администраторов
## 2.9. Платная подписка ### 2.9. Платная подписка
- Личное использование бесплатно (personal) - Личное использование бесплатно (personal), коммерческое — платно (commercial)
- Коммерческое использование платно (commercial)
- Пробный период 30 дней - Пробный период 30 дней
- Планы подписки: 1, 3, 6, 12 месяцев - Планы подписки: monthly, quarterly, biannual, annual
- Заглушка платежного шлюза (для тестирования) - Заглушка платежного шлюза (для тестирования)
- Автоматическое истечение подписки - Автоматическое истечение подписки
- Автоматическое понижение календарей до personal при истечении
## 2.10. Административная панель ### 2.10. Административная панель
- Отдельный HTTPS-сервер (порт 8445) и отдельный WSS (порт 8446) - Отдельный HTTPS-сервер (порт 8445) и отдельный WSS (порт 8446)
- Управление календарями, событиями, отзывами, жалобами, тикетами, бан-словами - Управление календарями, событиями, отзывами, жалобами, тикетами, бан-словами
- Статистика, информация о нодах кластера - Статистика, информация о нодах кластера
- WebSocket-уведомления администраторам - WebSocket-уведомления администраторам
### 2.10.1. Ролевая модель администраторов #### 2.10.1. Ролевая модель администраторов (реализована)
Вводится три встроенные роли администраторов с различным объёмом прав: В проекте реализована трехуровневая ролевая модель: `superadmin`, `moderator`, `support`. Проверка ролей выполняется в каждом административном обработчике через `handler_auth:authenticate/1` и вспомогательную функцию `is_admin/1`.
| Роль | Идентификатор | Права | #### 2.10.2. Эндпоинты для управления ролями и аудитом
|--------------|---------------|-------------------------------------------------------------------------------------------| - `GET /v1/admin/me` — получение текущей роли и разрешений администратора.
| Суперадмин | `superadmin` | Полный доступ ко всем модулям, управление ролями других админов, просмотр аудита, системные метрики. | - `GET /v1/admin/admins` — список всех администраторов с ролями (только для `superadmin`).
| Модератор | `moderator` | Модерация контента (события, жалобы, отзывы), блокировка/разблокировка пользователей, работа с баг-трекером. | - `PUT /v1/admin/admins/:id` — изменение роли администратора (только для `superadmin`).
| Поддержка | `support` | Read-only доступ к пользователям и событиям, обработка жалоб, создание и обновление багов. | - `POST /v1/admin/admins` — приглашение нового администратора с назначением роли (только для `superadmin`).
- `GET /v1/admin/audit` — журнал действий администраторов с фильтрацией по дате, пользователю, действию (только для `superadmin`).
**Примечание:** На этапе MVP поддерживается только роль `admin` (эквивалент `superadmin`). Полноценная ролевая модель будет внедрена в ближайших итерациях. #### 2.10.3. Статистика для дашборда с учётом ролей
Эндпоинт: `GET /v1/admin/stats`. Возвращает JSON, содержимое которого фильтруется в соответствии с ролью вызывающего.
### 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. Статистика для дашборда с учётом ролей
- `superadmin` — системные метрики: все пользователи, события, жалобы, баги за период, графики регистраций/событий по дням, активность администраторов. - `superadmin` — системные метрики: все пользователи, события, жалобы, баги за период, графики регистраций/событий по дням, активность администраторов.
- `moderator` — собственные обработанные жалобы/события (количество, статусы, время реакции), общая статистика по модерации. - `moderator` — собственные обработанные жалобы/события (количество, статусы, время реакции), общая статистика по модерации.
- `support` — количество открытых багов и жалоб, назначенных на текущего сотрудника, персональные задачи. - `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+ пользователей - 100 000+ пользователей
- Горизонтальное масштабирование (увеличение нод) - Горизонтальное масштабирование (увеличение нод)
- Mnesia с дисковыми копиями на нескольких нодах - Mnesia с дисковыми копиями на нескольких нодах
- Отдельные ноды для исторических данных (disc_only_copies) - Отдельные ноды для исторических данных (disc_only_copies)
- Пагинация всех списков - Пагинация всех списков
- Автоматическое обнаружение узлов через DNS-лукап (libcluster в перспективе)
## 3.2. Надёжность ### 3.2. Надёжность
- Супервизорное дерево OTP - Супервизорное дерево OTP
- Let it crash быстрый перезапуск процессов - Let it crash быстрый перезапуск процессов
- Автоматическое восстановление после падения ноды (Mnesia) - Автоматическое восстановление после падения ноды (Mnesia)
## 3.3. Безопасность ### 3.3. Безопасность
- JWT с ролью (user, admin; в будущем — superadmin, moderator, support) - Раздельная JWT-аутентификация:
- Пользовательские токены: секрет `JWT_SECRET`, audience = `<<"user">>`, эндпоинт `/v1/login`
- Административные токены: секрет `ADMIN_JWT_SECRET`, audience = `<<"admin">>`, эндпоинт `/v1/admin/login`
- Проверка ролей: `superadmin`, `moderator`, `support`
- HTTPS / WSS (самоподписанный сертификат для dev, реальный для prod) - HTTPS / WSS (самоподписанный сертификат для dev, реальный для prod)
- Argon2 для хеширования паролей (erlang-argon2) - Argon2 для хеширования паролей (erlang-argon2)
- Refresh token реализован (хранение в Mnesia, сессии)
- OAuth2 для Google (опционально) - OAuth2 для Google (опционально)
- Refresh token (запланирован)
- При блокировке пользователя или отклонении сущности обязательно сохранять причину (поле `reason`) - При блокировке пользователя или отклонении сущности обязательно сохранять причину (поле `reason`)
## 3.4. Наблюдаемость ### 3.4. Наблюдаемость
- Healthcheck эндпоинт (`GET /health`) - Healthcheck эндпоинт (`GET /health`)
- Логирование (JSON, ротация) - Логирование (JSON, ротация)
- Prometheus метрики (запланированы на уровне приложения, в настоящее время доступны через балансировщик) - Prometheus метрики (реализованы на уровне приложения через `prometheus_cowboy`)
- Аудит действий администраторов (обязателен, см. п. 2.10.2) - Аудит действий администраторов (запись `admin_audit`)
## 3.5. CI/CD ### 3.5. CI/CD
- Drone CI (или GitLab CI / GitHub Actions) - Drone CI (или GitLab CI / GitHub Actions)
- EUnit + Common Test + Tsung - EUnit + Common Test + Tsung
- Сборка релиза - Сборка релиза
@@ -210,9 +212,10 @@
- Сборка: rebar3 3.27.0 - Сборка: rebar3 3.27.0
- HTTP/WebSocket: Cowboy 2.10.0 - HTTP/WebSocket: Cowboy 2.10.0
- JSON: jsx 3.1.0 - JSON: jsx 3.1.0
- JWT: jose (реализовано) - JWT: jose 1.11.10
- Хеширование паролей: erlang-argon2 1.0.0 - Хеширование паролей: erlang-argon2 1.2.0
- Тестирование: meck 0.9.2, gun 2.0.0 - Тестирование: meck 0.9.2, gun 2.0.0
- Мониторинг: prometheus_cowboy 0.2.0
- Нагрузочное тестирование: Tsung 1.8.0 - Нагрузочное тестирование: Tsung 1.8.0
- CI/CD: Drone 2.0+ - CI/CD: Drone 2.0+
- Контейнеризация: Docker 20.10+ - Контейнеризация: Docker 20.10+
@@ -220,65 +223,77 @@
## 5. ИЕРАРХИЧЕСКАЯ СТРУКТУРА КОДА ## 5. ИЕРАРХИЧЕСКАЯ СТРУКТУРА КОДА
``` ```
src/ src/ test/
├── infra/ # инфраструктура (приложение, супервизоры, аутентификация, подписки) ├── infra/ ├── unit/ # EUnit тесты
├── core/ # слой доступа к данным (DAO) и модели ├── eventhub_auth.erl # JWT └── api/ # Common Test интеграционные тесты
├── logic/ # бизнес-логика (независимая от HTTP/WS) │ └── ...
├── services/ # внешние сервисы (заглушки/интеграции) ├── core/ # слой доступа к данным (DAO)
── handlers/ # обработчики HTTP/WebSocket ── logic/ # бизнес-логика
├── services/ # внешние сервисы
├── handlers/ # обработчики HTTP/WebSocket
│ ├── admin/ # административные обработчики
│ └── ...
└── middlewares/ # промежуточные слои (CORS, аутентификация)
``` ```
**Правила:** **Правила:**
- Модули `handlers/` не содержат бизнес-логики. - Модули `handlers/` не содержат бизнес-логики.
- Модули `logic/` не знают о HTTP/WS. - Модули `logic/` не знают о HTTP/WS.
- Модули `core/` (DAO) работают только с Mnesia. - Модули `core/` (DAO) работают только с Mnesia.
- Модуль `infra/eventhub_auth.erl` реализует раздельную JWT-аутентификацию (пользователи и администраторы).
- Модули `services/` могут быть заменены на реальные реализации без изменения остального кода. - Модули `services/` могут быть заменены на реальные реализации без изменения остального кода.
- Заголовочный файл `include/records.hrl` содержит все записи таблиц Mnesia. - Заголовочный файл `include/records.hrl` содержит все записи таблиц Mnesia.
## 6. ОСНОВНЫЕ API (КРАТКО) ## 6. ОСНОВНЫЕ API (КРАТКО)
### Пользовательские (порт 8080) ### Пользовательские (порт 8080)
- `POST /v1/register` **Публичные:** `GET /health`, `POST /v1/register`, `POST /v1/login`, `POST /v1/refresh`
- `POST /v1/login` **Авторизованные:** `GET /v1/user/me`, `GET /v1/user/bookings`, `GET /v1/user/reviews`, `GET /v1/search`
- `GET /v1/user/me` **Календари:** `POST /v1/calendars` + CRUD, `GET /v1/calendars/:id`, `PUT /v1/calendars/:id`, `DELETE /v1/calendars/:id`
- `POST /v1/calendars` + CRUD **События:** `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`
- `POST /v1/calendars/:id/events` + CRUD **Бронирования:** `GET /v1/bookings/:id`, `PUT /v1/bookings/:id`, `DELETE /v1/bookings/:id`
- `POST /v1/events/:id/join` **Отзывы:** `POST /v1/reviews`, `GET /v1/reviews/:id`, `PUT /v1/reviews/:id`, `DELETE /v1/reviews/:id`
- `POST /v1/events/:id/confirm/:user_id` **Жалобы:** `POST /v1/reports`
- `POST /v1/reviews` **Тикеты:** `POST /v1/tickets`, `GET /v1/tickets`, `GET /v1/tickets/:id`, `PUT /v1/tickets/:id`, `DELETE /v1/tickets/:id`
- `POST /v1/reports` **Подписки:** `GET /v1/subscription`, `POST /v1/subscription`
- `POST /v1/subscription/activate` **Метрики:** `GET /metrics/[:registry]`
- `POST /v1/events/:id/export` (Google)
- `GET /v1/events/:id/ical` (Apple)
- `GET /health`
**WebSocket (порт 8081):** `ws://localhost:8081/ws?token=...` WebSocket (порт 8081): `ws://localhost:8081/ws?token=<jwt>`
### Административные (порт 8445) ### Административные (порт 8445)
- `GET /admin/stats/overview` (или `/admin/stats`) — статистика с учётом роли **Версионирование:** Все административные эндпоинты имеют префикс `/v1/admin/`.
- `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` (текущий админ, его роль и права)
**Административный 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=<admin_jwt>`
## 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. ВЕРСИОНИРОВАНИЕ И СТАТУС ## 7. ВЕРСИОНИРОВАНИЕ И СТАТУС
Текущая версия: **1.2** (MVP, альфа, включает гибридную модель повторяющихся событий и базовую ролевую модель администрирования). Текущая версия: **1.3** (MVP, альфа). Включает:
- Гибридную модель повторяющихся событий
- Раздельную JWT-аутентификацию (пользователи/администраторы)
- Полноценную ролевую модель администрирования
- Версионированное админ-API (`/v1/admin/...`)
- Расширенную инфраструктуру (Traefik, WAF, мониторинг, failover)
## 8. ОГРАНИЧЕНИЯ И ДОПУЩЕНИЯ ## 8. ОГРАНИЧЕНИЯ И ДОПУЩЕНИЯ
- В разработке используются самоподписанные SSL-сертификаты. - В разработке используются самоподписанные SSL-сертификаты.
- Для production требуется реальный сертификат и настройка OAuth2 для Google. - Для production требуется реальный сертификат и настройка OAuth2 для Google.
- Заглушки внешних сервисов должны быть заменены перед запуском. - Заглушки внешних сервисов должны быть заменены перед запуском.
- Полноценное разграничение прав администраторов (`superadmin`, `moderator`, `support`) ожидается в следующем релизе; пока используется универсальная роль `admin`. - Автоматическое обнаружение узлов через libcluster находится в перспективе; текущая реализация использует статический JOIN_NODES с возможностью подключения через DNS-лукап.
## 9. ТРЕБОВАНИЯ К ОКРУЖЕНИЮ ## 9. ТРЕБОВАНИЯ К ОКРУЖЕНИЮ
- Erlang/OTP 28.2 - Erlang/OTP 28.2