diff --git a/EventHubBackSpec.md b/EventHubBackSpec.md index 44bfb8d..6d5b709 100644 --- a/EventHubBackSpec.md +++ b/EventHubBackSpec.md @@ -1,8 +1,10 @@ ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB -Версия: 1.3 (актуальная реализация: раздельная JWT-аутентификация, версионированное админ-API, расширенная инфраструктура) +Версия: 1.4 (актуальная реализация: раздельная JWT-аутентификация, версионированное админ-API, расширенная инфраструктура, аудит администраторов, расширенная статистика дашборда, улучшенная обработка ошибок) ## 1. ЦЕЛИ И НАЗНАЧЕНИЕ -EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. +EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. + +Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. ## 2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ @@ -62,29 +64,29 @@ EventHub — платформа для управления событиями #### 2.2.5. Структура записей (records.hrl) ```erlang -record(event, { - id :: binary(), - calendar_id :: binary(), - title :: binary(), - description :: binary(), - event_type :: single | recurring, - start_time :: calendar:datetime(), - duration :: integer(), %% минуты - recurrence_rule :: binary() | undefined, - master_id :: binary() | undefined, - is_instance :: boolean(), %% true для материализованных вхождений - specialist_id :: binary() | undefined, - location :: #location{} | undefined, - tags :: [binary()], - status :: active | cancelled | completed, - created_at :: calendar:datetime(), - updated_at :: calendar:datetime() + id :: binary(), + calendar_id :: binary(), + title :: binary(), + description :: binary(), + event_type :: single | recurring, + start_time :: calendar:datetime(), + duration :: integer(), %% минуты + recurrence_rule :: binary() | undefined, + master_id :: binary() | undefined, + is_instance :: boolean(), %% true для материализованных вхождений + specialist_id :: binary() | undefined, + location :: #location{} | undefined, + tags :: [binary()], + status :: active | cancelled | completed, + created_at :: calendar:datetime(), + updated_at :: calendar:datetime() }). -record(recurrence_exception, { - master_id :: binary(), - original_start :: calendar:datetime(), - action :: cancel | reschedule, - new_start :: calendar:datetime() | undefined + master_id :: binary(), + original_start :: calendar:datetime(), + action :: cancel | reschedule, + new_start :: calendar:datetime() | undefined }). ``` #### 2.2.6. Требования к реализации @@ -117,7 +119,16 @@ EventHub — платформа для управления событиями - Автоматическая модерация по ключевым словам и по порогу жалоб - Ручная заморозка / разморозка администратором - Бан-лист слов -- Аудит действий администраторов (детальная информация: кто, что, когда, IP, причина) +- **Аудит действий администраторов:** Ведётся детальный журнал действий всех администраторов (кто, что, когда, IP, причина). Модель аудита (admin_audit) включает: + - admin_id — идентификатор администратора, + - email — email администратора, + - role — роль (superadmin, moderator, support), + - action — действие (например, add_banned_word, delete_banned_word, update_role), + - entity_type — тип сущности (banned_word, admin и т.д.), + - entity_id — идентификатор сущности, + - ip — IP-адрес, + - reason — причина действия (опционально), + - timestamp — время действия. ### 2.8. Баг-трекер (автоматический) - При ошибке создаётся тикет, группировка по хэшу ошибки @@ -140,24 +151,28 @@ EventHub — платформа для управления событиями - WebSocket-уведомления администраторам #### 2.10.1. Ролевая модель администраторов (реализована) -В проекте реализована трехуровневая ролевая модель: `superadmin`, `moderator`, `support`. Проверка ролей выполняется в каждом административном обработчике через `handler_auth:authenticate/1` и вспомогательную функцию `is_admin/1`. +Реализована трехуровневая ролевая модель: superadmin, moderator, support. Проверка ролей выполняется в каждом административном обработчике через handler_auth:authenticate/1 и вспомогательную функцию is_admin/1. #### 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`). +- 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). #### 2.10.3. Статистика для дашборда с учётом ролей -Эндпоинт: `GET /v1/admin/stats`. Возвращает JSON, содержимое которого фильтруется в соответствии с ролью вызывающего. -- `superadmin` — системные метрики: все пользователи, события, жалобы, баги за период, графики регистраций/событий по дням, активность администраторов. -- `moderator` — собственные обработанные жалобы/события (количество, статусы, время реакции), общая статистика по модерации. -- `support` — количество открытых багов и жалоб, назначенных на текущего сотрудника, персональные задачи. +Эндпоинт: GET /v1/admin/stats. Возвращает JSON, содержимое которого фильтруется в соответствии с ролью вызывающего: +- superadmin — системные метрики: все пользователи, события, жалобы, баги за период, графики регистраций/событий по дням, активность администраторов. +- moderator — собственные обработанные жалобы/события (количество, статусы, время реакции), общая статистика по модерации. +- support — количество открытых багов и жалоб, назначенных на текущего сотрудника, персональные задачи. + +**Расширенная статистика (дополнение):** +- GET /v1/admin/statistics — агрегированная статистика по администраторам: общее количество действий за период, распределение по типам действий. +- Поддержка фильтрации: admin_id, action, date_from, date_to. ### 2.11. Real-time уведомления (WebSocket) -- Пользователи: порт 8081, маршрут `/ws`, подписка на календарь, получение обновлений -- Администраторы: порт 8446, маршрут `/admin/ws`, подписка на глобальные уведомления (жалобы, авто-заморозки) +- Пользователи: порт 8081, маршрут /ws, подписка на календарь, получение обновлений +- Администраторы: порт 8446, маршрут /admin/ws, подписка на глобальные уведомления (жалобы, авто-заморозки) ### 2.12. Инфраструктура развертывания (Docker Compose) - Балансировщик Traefik с поддержкой HTTPS/WSS, WAF (Coraza), Rate Limiting и Failover @@ -181,23 +196,29 @@ EventHub — платформа для управления событиями - Супервизорное дерево OTP - Let it crash – быстрый перезапуск процессов - Автоматическое восстановление после падения ноды (Mnesia) +- **Улучшенная обработка ошибок:** + - Валидация входных данных на всех критических эндпоинтах административного API. + - Единый формат ошибок: {"error": "код", "message": "описание"}. ### 3.3. Безопасность - Раздельная JWT-аутентификация: - - Пользовательские токены: секрет `JWT_SECRET`, audience = `<<"user">>`, эндпоинт `/v1/login` - - Административные токены: секрет `ADMIN_JWT_SECRET`, audience = `<<"admin">>`, эндпоинт `/v1/admin/login` - - Проверка ролей: `superadmin`, `moderator`, `support` + - Пользовательские токены: секрет 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 (опционально) -- При блокировке пользователя или отклонении сущности обязательно сохранять причину (поле `reason`) +- При блокировке пользователя или отклонении сущности обязательно сохранять причину (поле reason) +- **CORS:** Все ответы API включают заголовки: + - access-control-allow-origin: * + - access-control-expose-headers: Content-Range ### 3.4. Наблюдаемость -- Healthcheck эндпоинт (`GET /health`) +- Healthcheck эндпоинт (GET /health) - Логирование (JSON, ротация) -- Prometheus метрики (реализованы на уровне приложения через `prometheus_cowboy`) -- Аудит действий администраторов (запись `admin_audit`) +- Prometheus метрики (реализованы на уровне приложения через prometheus_cowboy) +- Аудит действий администраторов (запись admin_audit) ### 3.5. CI/CD - Drone CI (или GitLab CI / GitHub Actions) @@ -223,7 +244,8 @@ EventHub — платформа для управления событиями ## 5. ИЕРАРХИЧЕСКАЯ СТРУКТУРА КОДА ``` -src/ test/ +src/ +test/ ├── infra/ ├── unit/ # EUnit тесты │ ├── eventhub_auth.erl # JWT └── api/ # Common Test интеграционные тесты │ └── ... @@ -235,59 +257,59 @@ src/ test/ │ └── ... └── middlewares/ # промежуточные слои (CORS, аутентификация) ``` - **Правила:** -- Модули `handlers/` не содержат бизнес-логики. -- Модули `logic/` не знают о HTTP/WS. -- Модули `core/` (DAO) работают только с Mnesia. -- Модуль `infra/eventhub_auth.erl` реализует раздельную JWT-аутентификацию (пользователи и администраторы). -- Модули `services/` могут быть заменены на реальные реализации без изменения остального кода. -- Заголовочный файл `include/records.hrl` содержит все записи таблиц Mnesia. +- Модули handlers/ не содержат бизнес-логики. +- Модули logic/ не знают о HTTP/WS. +- Модули core/ (DAO) работают только с Mnesia. +- Модуль infra/eventhub_auth.erl реализует раздельную JWT-аутентификацию (пользователи и администраторы). +- Модули services/ могут быть заменены на реальные реализации без изменения остального кода. +- Заголовочный файл include/records.hrl содержит все записи таблиц Mnesia. ## 6. ОСНОВНЫЕ API (КРАТКО) ### Пользовательские (порт 8080) -**Публичные:** `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]` +- **Публичные:** 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) -**Версионирование:** Все административные эндпоинты имеют префикс `/v1/admin/`. +- **Версионирование:** Все административные эндпоинты имеют префикс /v1/admin/. +- **Базовые:** 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) -**Базовые:** `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=` +Административный 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}`. +Модуль 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.3** (MVP, альфа). Включает: +Текущая версия: 1.4 (MVP, альфа). Включает: - Гибридную модель повторяющихся событий - Раздельную JWT-аутентификацию (пользователи/администраторы) - Полноценную ролевую модель администрирования -- Версионированное админ-API (`/v1/admin/...`) +- Версионированное админ-API (/v1/admin/...) - Расширенную инфраструктуру (Traefik, WAF, мониторинг, failover) +- Аудит действий администраторов +- Расширенную статистику для дашборда +- Улучшенную обработку ошибок и валидацию ## 8. ОГРАНИЧЕНИЯ И ДОПУЩЕНИЯ - В разработке используются самоподписанные SSL-сертификаты.