Техническое задание: Сервис администрации
1. Контекст и окружение
Заголовок раздела «1. Контекст и окружение»Существующий стек
Заголовок раздела «Существующий стек»- Монорепозиторий: NX 22.0
- Backend: NestJS 11.0 (микросервисная архитектура:
auth-api,user-api,project-api,core-api,ai-agent-service) - ORM: Prisma 6.18
- База данных: PostgreSQL
- Кэш: Redis 5.9
- Авторизация: Passport.js + JWT (access + refresh), раздельные конфигурации для Customer и Employee
- Email: Nodemailer (
@nestjs-modules/mailer) через SMTP - Верификация email: 6-символьный буквенно-цифровой код, хранение в Redis с TTL 900 сек (15 мин), cooldown на повторную отправку 120 сек (2 мин)
- Очереди: RabbitMQ
- Шаред-библиотеки:
/libs/apis/— configs, providers, shared (guards, decorators, helpers)
Текущая ролевая модель
Заголовок раздела «Текущая ролевая модель»- Таблица
rolesуже существует с enumRoleType(значения:CUSTOMER,EMPLOYEE). - Связь users ↔ roles реализована через junction-таблицу
users_on_roles(many-to-many). - Необходимо добавить значение
ADMINв enumRoleType. - Админ — это тот же
User, но с привязанной рольюADMINчерезusers_on_roles. - Все админы равнозначны, иерархии нет.
Текущая auth-архитектура
Заголовок раздела «Текущая auth-архитектура»- В системе уже есть раздельные JWT-конфигурации:
JWT_CUSTOMER_*иJWT_EMPLOYEE_*. - Каждый тип пользователя имеет свои Passport-стратегии (
CustomerAccessTokenStrategy,EmployeeAccessTokenStrategyи т.д.) и Guard’ы (CustomerAccessTokenGuard,EmployeeAccessTokenGuard). - Для админа необходимо создать аналогичную инфраструктуру: JWT-конфигурацию, стратегии, guard’ы.
2. Регистрация админа
Заголовок раздела «2. Регистрация админа»2.1. Проверка домена корпоративной почты
Заголовок раздела «2.1. Проверка домена корпоративной почты»- Список разрешённых доменов хранится в
.env(переменная:ADMIN_ALLOWED_DOMAINS=company.com,company.io). - При попытке регистрации система проверяет, что домен email-адреса входит в список разрешённых.
- Если домен не совпадает — отказ с понятным сообщением об ошибке.
2.2. Флоу регистрации
Заголовок раздела «2.2. Флоу регистрации»- Админ вводит корпоративный email.
- Система проверяет домен email по списку разрешённых.
- Система проверяет, что пользователь с таким email ещё не зарегистрирован.
- На email отправляется код подтверждения — переиспользовать существующий механизм:
- Генерация 6-символьного кода через
generateRandomCode()изrandom-code.helper.ts. - Хранение в Redis с ключом
ADMIN_VERIFICATION_CODE:email={email}и TTL 900 сек. - Cooldown на повторную отправку через ключ
ADMIN_VERIFICATION_RESEND_CODE:email={email}с TTL 120 сек. - Отправка через
EmailSenderService(добавить новый шаблонadmin_email_verification).
- Генерация 6-символьного кода через
- Админ вводит полученный код.
- Система верифицирует код (проверка в Redis, удаление после использования).
- При успешной верификации:
- Создаётся запись
User(без пароля — поляpasswordHash/passwordSaltnull или пустые). - Создаётся связь через
UserOnRoleс рольюADMIN. - Создаётся
Sessionс fingerprint устройства (аналогично customer auth). - Создаются
Tokenзаписи в БД.
- Создаётся запись
- Возвращается пара JWT-токенов (access + refresh).
2.3. Что НЕ нужно
Заголовок раздела «2.3. Что НЕ нужно»- Пароль не используется. Вся аутентификация — passwordless (email + код).
- Не нужно создавать записи в таблицах
customersилиemployees— админ не является ни тем, ни другим.
3. Аутентификация админа (логин)
Заголовок раздела «3. Аутентификация админа (логин)»3.1. Флоу входа
Заголовок раздела «3.1. Флоу входа»- Админ вводит свой корпоративный email.
- Система проверяет, что пользователь с таким email существует и имеет роль
ADMIN(черезusers_on_roles). - На email отправляется код подтверждения (тот же механизм, что при регистрации).
- Админ вводит код.
- При успешной верификации:
- Создаётся/обновляется
Session(по fingerprint из заголовкаX-Fingerprint). - Генерируются JWT-токены и сохраняются в таблицу
tokens. - Возвращается пара JWT-токенов.
- Создаётся/обновляется
3.2. JWT-токены
Заголовок раздела «3.2. JWT-токены»- Создать отдельную JWT-конфигурацию для админа (по аналогии с
JwtCustomerConfig/JwtEmployeeConfig):JWT_ADMIN_ACCESS_SECRET_KEYJWT_ADMIN_ACCESS_EXPIRES_IN(default: 3600)JWT_ADMIN_REFRESH_SECRET_KEYJWT_ADMIN_REFRESH_EXPIRES_IN(default: 7200)
- Payload токена (по аналогии с
TokenPayload):{userId: stringroles: ['ADMIN']iat: numberexp: numberjti: string // UUID, уникальный идентификатор токена} - Refresh-токен — стандартный флоу через
POST /admin/auth/refreshс заголовкамиX-FingerprintиX-Session-Id.
4. Авторизация и защита эндпоинтов
Заголовок раздела «4. Авторизация и защита эндпоинтов»4.1. Guard’ы и стратегии
Заголовок раздела «4.1. Guard’ы и стратегии»Создать по аналогии с существующей инфраструктурой (customer/employee):
AdminAccessTokenStrategy— Passport-стратегия для валидации access-токена.AdminRefreshTokenStrategy— Passport-стратегия для валидации refresh-токена.AdminAccessTokenGuard— Guard для защиты эндпоинтов (использует стратегию).AdminRefreshTokenGuard— Guard для эндпоинта обновления токена.
Расположение:
- Стратегии:
/libs/apis/providers/auth-api/features/admin-auth/src/lib/strategies/ - Guard’ы:
/libs/apis/shared/src/lib/guards/
4.2. Разделение доступа
Заголовок раздела «4.2. Разделение доступа»- Обычные пользователи (Customer/Employee) НЕ имеют доступа к админским эндпоинтам.
- Админы имеют доступ ко всем админским эндпоинтам на равных правах (без иерархии).
- Guard проверяет JWT-подпись отдельным секретом (
JWT_ADMIN_ACCESS_SECRET_KEY), что делает токены Customer/Employee невалидными для админских эндпоинтов.
5. Функциональность админа
Заголовок раздела «5. Функциональность админа»5.1. Просмотр проектов
Заголовок раздела «5.1. Просмотр проектов»- Админ может видеть ВСЕ проекты всех пользователей.
GET /admin/projects— список всех проектов с пагинацией (переиспользоватьgetPaginate()/toPaginateResponse()изpaginate.helper.ts), фильтрацией поstatus(ProjectStatusenum) иcategory(ProjectCategoryTypeenum), поиском поtitle.GET /admin/projects/:id— детальная информация о проекте.- В ответе должна содержаться информация о владельце (customer → user: id, email).
5.2. Редактирование проектов
Заголовок раздела «5.2. Редактирование проектов»- Админ может редактировать любой проект любого пользователя.
PATCH /admin/projects/:id— обновление полей проекта.- Редактируемые поля:
title,description,category,status(с учётом допустимых переходов изPROJECT_STATUS_TRANSITIONS).
5.3. Удаление проектов
Заголовок раздела «5.3. Удаление проектов»- Админ может удалить любой проект.
DELETE /admin/projects/:id.- Реализовать soft delete: добавить поле
deletedAt(DateTime, nullable) в модельProject. При удалении проставлять текущую дату. Все запросы на выборку должны фильтроватьdeletedAt = null.
5.4. Блокировка пользователей
Заголовок раздела «5.4. Блокировка пользователей»- Админ может заблокировать (забанить) пользователя.
PATCH /admin/users/:id/block— заблокировать пользователя.PATCH /admin/users/:id/unblock— разблокировать пользователя.- Добавить поле
isBlocked(Boolean, default: false) в модельUser. - Заблокированный пользователь не может авторизоваться — при попытке входа возвращать понятное сообщение об ошибке.
- Проверку
isBlockedдобавить в существующие guard’ы/стратегии (CustomerAccessTokenStrategy,EmployeeAccessTokenStrategy) — при каждом запросе проверять флаг в БД. - Нельзя блокировать других админов.
5.5. Просмотр пользователей
Заголовок раздела «5.5. Просмотр пользователей»GET /admin/users— список всех пользователей с пагинацией (переиспользоватьgetPaginate()), фильтрацией по роли (RoleType), статусу блокировки (isBlocked), поиском по email.GET /admin/users/:id— детальная информация о пользователе (profile, роли черезusers_on_roles,isBlocked, список проектов).
6. API-эндпоинты (сводка)
Заголовок раздела «6. API-эндпоинты (сводка)»Аутентификация админа
Заголовок раздела «Аутентификация админа»| Метод | Путь | Описание |
|---|---|---|
| POST | /admin/auth/register | Начать регистрацию (email → проверка домена → отправка кода) |
| POST | /admin/auth/register/verify | Подтвердить код → создание аккаунта → JWT |
| POST | /admin/auth/login | Начать вход (email → проверка существования + роль ADMIN → отправка кода) |
| POST | /admin/auth/login/verify | Подтвердить код входа → JWT |
| POST | /admin/auth/refresh | Обновить access-токен (заголовки: X-Fingerprint, X-Session-Id) |
Управление проектами
Заголовок раздела «Управление проектами»| Метод | Путь | Описание |
|---|---|---|
| GET | /admin/projects | Список всех проектов (пагинация: page, perPage; фильтры: status, category; поиск: search) |
| GET | /admin/projects/:id | Детали проекта (включая владельца) |
| PATCH | /admin/projects/:id | Редактирование проекта |
| DELETE | /admin/projects/:id | Soft delete проекта |
Управление пользователями
Заголовок раздела «Управление пользователями»| Метод | Путь | Описание |
|---|---|---|
| GET | /admin/users | Список пользователей (пагинация, фильтры: role, isBlocked; поиск по email) |
| GET | /admin/users/:id | Детали пользователя (профиль, роли, проекты) |
| PATCH | /admin/users/:id/block | Заблокировать пользователя |
| PATCH | /admin/users/:id/unblock | Разблокировать пользователя |
7. Модель данных (изменения в Prisma-схеме)
Заголовок раздела «7. Модель данных (изменения в Prisma-схеме)»Файл: /apps/core-api/prisma/schema.prisma
Enum RoleType — добавить значение
Заголовок раздела «Enum RoleType — добавить значение»enum RoleType { CUSTOMER EMPLOYEE ADMIN // <-- добавить}Модель User — добавить поле блокировки
Заголовок раздела «Модель User — добавить поле блокировки»model User { // ... существующие поля isBlocked Boolean @default(false) @map("is_blocked") // passwordHash и passwordSalt должны стать nullable (для passwordless admin) passwordHash String? @map("password_hash") passwordSalt String? @map("password_salt")}Модель Project — добавить soft delete
Заголовок раздела «Модель Project — добавить soft delete»model Project { // ... существующие поля deletedAt DateTime? @map("deleted_at")}Redis-ключи (новые константы)
Заголовок раздела «Redis-ключи (новые константы)»Добавить в /libs/apis/shared/src/lib/constants/:
ADMIN_VERIFICATION_CODE = 'ADMIN_VERIFICATION_CODE:email={email}'ADMIN_VERIFICATION_RESEND_CODE = 'ADMIN_VERIFICATION_RESEND_CODE:email={email}'TTL: код — 900 сек, cooldown — 120 сек (аналогично customer).
8. Структура кода (новые модули/библиотеки)
Заголовок раздела «8. Структура кода (новые модули/библиотеки)»Следуя архитектуре монорепозитория:
Новое приложение (опционально) или модуль в auth-api
Заголовок раздела «Новое приложение (опционально) или модуль в auth-api»Рекомендуется добавить admin auth как часть существующего auth-api, по аналогии с customer-auth и employee-auth:
libs/apis/├── configs/auth-api/│ └── jwt-admin/ # JWT конфигурация для админа├── providers/auth-api/│ └── features/│ └── admin-auth/ # Регистрация, логин, верификация, refresh│ └── src/lib/│ ├── strategies/│ │ ├── admin-access-token.strategy.ts│ │ └── admin-refresh-token.strategy.ts│ ├── admin-auth.controller.ts│ ├── admin-auth.service.ts│ └── admin-auth.module.ts├── providers/admin-api/ # Админские CRUD-эндпоинты│ └── features/│ ├── admin-project/ # GET/PATCH/DELETE /admin/projects│ └── admin-user/ # GET/PATCH /admin/users└── shared/src/lib/ └── guards/ ├── admin-access-token.guard.ts └── admin-refresh-token.guard.tsНовое приложение admin-api (или контроллеры в auth-api / отдельный app)
Заголовок раздела «Новое приложение admin-api (или контроллеры в auth-api / отдельный app)»Для CRUD-эндпоинтов (projects, users) рекомендуется создать отдельное NX-приложение admin-api со своим портом, либо добавить контроллеры в существующий auth-api под префиксом /admin.
9. Переменные окружения (новые)
Заголовок раздела «9. Переменные окружения (новые)»Добавить в .env и .env.example:
# Admin JWTJWT_ADMIN_ACCESS_SECRET_KEY=JWT_ADMIN_ACCESS_EXPIRES_IN=3600JWT_ADMIN_REFRESH_SECRET_KEY=JWT_ADMIN_REFRESH_EXPIRES_IN=7200
# Admin allowed email domainsADMIN_ALLOWED_DOMAINS=company.com,company.io10. Безопасность
Заголовок раздела «10. Безопасность»- Все админские эндпоинты защищены
AdminAccessTokenGuard— без валидного JWT с рольюADMINдоступ запрещён. - JWT для админа подписывается отдельным секретом (
JWT_ADMIN_ACCESS_SECRET_KEY), что изолирует админские токены от customer/employee токенов. - Проверка
isBlockedпри каждом запросе авторизованного пользователя (добавить в существующие Passport-стратегии). - Rate limiting на эндпоинты отправки кода подтверждения (переиспользовать существующие cooldown-механизмы через Redis).
- Код подтверждения — одноразовый, удаляется из Redis после успешного использования.
- Админ не может заблокировать другого админа.
- Логирование действий админа — рекомендуется для аудита (вне скоупа MVP).
11. Что переиспользовать из существующего кода
Заголовок раздела «11. Что переиспользовать из существующего кода»| Что | Откуда |
|---|---|
| Генерация кода верификации | libs/apis/shared/src/lib/helpers/random-code.helper.ts — generateRandomCode() |
| Redis TTL-константы | libs/apis/shared/src/lib/constants/ — добавить новые по аналогии |
| Email-отправка | libs/apis/providers/email-sender-api/ — EmailSenderService |
| JWT генерация/валидация | libs/apis/providers/auth-api/features/token/ — TokenCustomerService как образец |
| Passport-стратегии | libs/apis/providers/auth-api/features/customer-auth/src/lib/strategies/ — как образец |
| Guard’ы | libs/apis/shared/src/lib/guards/ — как образец |
| Пагинация | libs/apis/shared/src/lib/helpers/paginate.helper.ts — getPaginate(), toPaginateResponse() |
| DTO-маппинг | libs/apis/shared/src/lib/services/mapper.service.ts |
| Декораторы | @GetTokenPayload(), @HeaderFingerprint(), @HeaderSessionId() |
| Сессии | libs/apis/providers/auth-api/features/session/ |
12. Миграции
Заголовок раздела «12. Миграции»Создать Prisma-миграцию, которая:
- Добавляет
ADMINв enumRoleType. - Добавляет колонку
is_blocked(boolean, default false) в таблицуusers. - Делает
password_hashиpassword_saltnullable в таблицеusers. - Добавляет колонку
deleted_at(timestamp, nullable) в таблицуprojects. - Вставляет запись роли
ADMINв таблицуroles.
Файл миграции: /apps/core-api/prisma/migrations/<timestamp>_add_admin_role_and_user_blocking/
13. Вне скоупа (не реализуется сейчас)
Заголовок раздела «13. Вне скоупа (не реализуется сейчас)»- Фронтенд / админ-панель (только API).
- Иерархия админов (суперадмин).
- Блокировка отдельных проектов (только бан пользователя целиком).
- Управление списком разрешённых доменов через API (хранится в
.env). - Логирование действий админа (аудит-лог).
- Создание отдельного
admin-apiмикросервиса (решение принимается при реализации).