Администрирование платформы
Простыми словами: это диспетчерская всей платформы, а не отдельной организации — шесть страниц ниже (Organizations, Users, Products, Platform Roles, Staff, Audit Log) позволяют собственной команде P4P управлять каждой компанией, использующей платформу, а не только своей. Это отдельная область от страниц «Workspace», которые видит обычный участник организации.
Всё под /platform/*. Доступно с точки входа Platform Administration на /dashboard, видно любому, у кого есть любая PlatformRole (middleware/platform-access.ts), не только SUPER_ADMIN — доступ к отдельным разделам дальше закрыт по правам, с единообразным разделением read/manage по всему модулю (просмотр без права :manage разрешён; изменяющее действие скрыто или заблокировано).
Все контроллеры здесь стоят за @UseGuards(PlatformRoleGuard) на уровне контроллера (самодостаточно, без зависимости от порядка с TenantGuard — в отличие от PermissionsGuard уровня организации, см. IAM). SUPER_ADMIN безусловно обходит PlatformRoleGuard.
Как сделать...
Сгруппировано так же, как технические разделы ниже — иконка помощи каждой страницы ведёт прямо в свою группу, а не во весь этот список.
Организации
Создать организацию со стороны платформы — «Organizations» → Новое рабочее пространство → Название/URL пространства/Тип → Создать. См. гайд по полям ниже.
Забрать управление зависшей организацией — открой организацию → Стать владельцем → укажи Причину → Продолжить → впиши название пространства для подтверждения. Только SUPER_ADMIN. См. гайд по полям ниже.
Дать или забрать у организации доступ к продукту — открой организацию → её раздел Products → Активировать/Деактивировать нужный продукт. (Сама страница «Products» редактирует только каталог — название/описание/активность — а не то, кто на что подписан.)
Создать организацию — гайд по полям
- Название (обязательно) — от 2 до 100 символов.
- URL пространства (обязательно) — от 2 до 50 символов, только строчные латинские буквы, цифры и дефисы. Автоматически заполняется из Названия по мере ввода (например, «Acme Corp» →
acme-corp) — начни редактировать его сама, и это автозаполнение отключается насовсем до конца работы с этим диалогом, даже если потом снова изменишь Название. Должен быть уникален по всей платформе; занятый вариант ловится только при отправке, не по ходу набора текста. - Тип — Company / Individual / Agency / Service provider / Platform / Personal. Только организации типа Personal создаются сразу Active; любой другой тип начинается со статуса Pending (значения статусов см. на собственной странице деталей организации).
Стать владельцем — гайд по полям
Эта кнопка появляется только рядом с тем, кто прямо сейчас держит роль OWNER, и только для SUPER_ADMIN, который сам этим владельцем не является. Она не даёт передать владение выбранному участнику — клик по ней всегда делает новым владельцем именно тебя, администратора, который нажал (при необходимости присоединяя тебя к организации, если ты ещё не её участник); прежний владелец понижается до обычного MEMBER, а не удаляется.
- Причина (шаг 1, необязательно) — свободный текст, чисто запись для того, кто потом читает журнал аудита; длина никак не проверяется, и заполнять не обязательно.
- Текст подтверждения (шаг 2, обязательно) — нужно ввести точное Название организации (с учётом регистра и пробелов после обрезки по краям), прежде чем финальная кнопка Стать владельцем перестанет быть неактивной. Никакой подсказки о частичном совпадении нет — ошибись на один символ, и кнопка просто останется неактивной без объяснения почему.
Пользователи
Удалить аккаунт пользователя — «Users» → строка → Удалить → подтвердить.
Убрать кого-то из персонала платформы — открой пользователя → Убрать из команды. Закрывает его членство в штате и все платформенные роли, но не удаляет сам аккаунт.
Дать кому-то платформенную роль или сделать новым владельцем платформы — открой пользователя → Роли платформы → отметь роли → Сохранить роли. Передача самой посевной роли Owner/ SUPER_ADMIN — отдельная кнопка на той же странице, Передать владение этому человеку.
Отправить сотрудника в отпуск или вернуть из него — этого действия здесь нет вообще; это меню «⋯» на его профиле в Staffing → Сотрудники, хотя оно и вызывает backend-роуты именно этого модуля.
Приглашения в персонал платформы
Пригласить кого-то во внутреннюю команду P4P — «Staff» → Пригласить в персонал платформы → Email, при желании заранее отметь Платформенные роли → Отправить приглашение. См. гайд по полям ниже.
Пригласить персонал платформы — гайд по полям
Это действие видит только SUPER_ADMIN или P4P Owner — оно не делегируется никаким обычным правом, в отличие от большинства других действий приглашения/управления в этом модуле.
- Email (обязательно) — единственная реальная проверка — «не пусто»; никакой проверки формата на клиенте вообще нет (явно некорректный адрес ловится только при реальной отправке, на сервере). Адрес, который уже есть в персонале платформы или уже имеет ожидающее приглашение, тоже ловится только при отправке.
- Платформенные роли (необязательно, чекбоксы) — оставить все пустыми совершенно нормально: приглашённый всё равно получит базовую роль «P4P Member», а не окажется вообще без доступа, так что это способ заранее назначить дополнительные роли, а не обязательный шаг.
SUPER_ADMINи Owner никогда не появляются в этом списке — их нельзя выдать приглашением, только через «Стать владельцем» или прямую передачу роли на странице пользователя. - Отправить приглашение ограничено 3 отправками в минуту — при превышении показывается отдельное сообщение «слишком много запросов», и кнопка ненадолго блокируется с видимым обратным отсчётом, а не обычной общей ошибкой.
Продукты и подписки
Отредактировать, как продукт выглядит в каталоге — «Products» → «⋯» строки → Редактировать → Название/Описание → Сохранить изменения. Включение/выключение продукта по всей платформе — отдельное действие из того же меню; выдача/отзыв доступа конкретной организации делается со страницы этой организации — см. Организации выше. См. гайд по полям ниже.
Отредактировать продукт — гайд по полям
- Название (обязательно) — от 2 до 100 символов.
- Описание (необязательно) — до 500 символов.
- Больше здесь ничего не редактируется — Код продукта (его стабильный внутренний идентификатор) и само его существование задаются один раз, в коде, при реальной сборке соответствующего приложения; этот диалог трогает только то, как продукт описан в каталоге.
- Активировать/деактивировать (по всей платформе) не часть этого диалога — это собственное действие-переключатель в строке, обратимое, без подтверждения. Выключение продукта здесь скрывает его сразу для всех организаций, независимо от статуса подписки каждой из них.
Роли
Создать собственную платформенную роль — «Roles» → Создать роль → назови, выбери права → сохранить. См. гайд по полям ниже.
Отредактировать, дублировать или удалить роль — меню ⋯ роли → Редактировать, Дублировать или Удалить. Назначение/снятие роли у пользователя происходит со страницы самого пользователя — см. Пользователи выше. Тот же гайд по полям ниже для Редактировать; Дублировать заранее заполняет ту же форму правами существующей роли, назвав её «Копия ⟨оригинал⟩» — ничего не сохраняется, пока не отправишь форму сама.
Создать/отредактировать платформенную роль — гайд по полям
5 посевных системных ролей (например, SUPER_ADMIN) показаны в том же списке, но их нельзя переименовать — поле Название у них заблокировано; Описание и Права остаются полностью редактируемыми, а удалить их нельзя никогда (только настоящие пользовательские роли).
- Название (обязательно) — должно быть уникальным среди всех неудалённых платформенных ролей. Сам диалог дополнительно отказывается принимать меньше 2 символов, хотя один только бэкенд принял бы и один символ — на практике этого разрыва никогда не увидишь, поскольку клиент блокирует раньше. До 100 символов.
- Описание (необязательно) — до 500 символов.
- Права (чекбоксы, сгруппированы по областям — Organizations/Users/Roles и т.д.) — минимума нет; роль без единого отмеченного права допустима и просто ничего не даёт.
- Сохранить/Создать активируется, только когда что-то реально отличается от загруженного — при создании это означает, что затронуто хотя бы одно поле от пустого состояния; при редактировании — реальное изменение текущего названия, описания или набора прав роли, а не просто повторное открытие и переотправка тех же значений.
Журнал аудита
Настраивать нечего — это только просмотр. Фильтруй по актору/действию/периоду прямо на странице; см. Журнал аудита ниже о том, что именно логируется и почему это отдельно от обычной админ-работы.
Организации (/platform/organizations, /platform/organizations/[id])
Простыми словами: все компании/агентства/частные лица, использующие платформу, с поиском и управлением из одного списка. Включает аварийное действие «взять владение» на случай, если реальный владелец организации недоступен.
admin-organizations.controller.ts, смонтирован на admin/organizations.
- Список / детали / участники (
GET) —platform:organizations:read. - Восстановление заархивированной организации (
POST :orgId/restore, переводитARCHIVED→ACTIVE, логируется какorg.restored) — подтверждено (2026-08-04, поиск поapps/portal): точки входа в интерфейсе нет. Единственный UI «restore», который вообще существует в портале — это несвязанная фича Shared Documents (восстановление мягко удалённого файла из корзины,POST /organizations/{id}/files/{fileId}/restore) — не путать эти два. Раз у архивации тоже нет UI, организация, ставшаяARCHIVED, сегодня вообще не имеет UI-пути обратно вACTIVE— только через API в обе стороны. - Force owner transfer (
POST :orgId/force-owner-transfer, «Take ownership» на странице карточки) —SuperAdminGuard, а не общее право:manage. Это явно «путь восстановления SUPER_ADMIN» (согласно комментарию в коде) для случая, когда организация застряла без доступного OWNER — обладательplatform:organizations:manage, не являющийсяSUPER_ADMIN, не может им воспользоваться.
Диалог «New workspace» на странице списка создаёт организацию напрямую со стороны платформы (тот же POST /organizations, что и при самостоятельном создании — см. IAM); его кнопка закрыта правом :manage, хотя сама страница требует только :read.
Исправлено, 2026-08-04: этот диалог раньше падал с 500 при каждой отправке — организация реально создавалась в базе (транзакция коммитится раньше, чем строится HTTP-ответ), но OrganizationsService.create() возвращал сырую строку Prisma вместо маппинга в OrganizationDto, а Organization.storageQuotaBytes/storageUsedBytes (BigInt-колонки, добавленные для docs/storage/ADR-001-file-storage.md) обрушивают JSON.stringify — так что админ видел ошибку и не мог узнать, что организация существует. getById() уже избегал этого, вручную формируя возвращаемое значение; теперь create() делает то же самое. Найдено через scripts/e2e/menu/platform-admin.mjs, который реально прогоняет этот диалог при каждом запуске.
Пользователи (/platform/users, /platform/users/[id])
Простыми словами: каждый человек с аккаунтом P4P, по всем организациям. Сегодня интерфейс позволяет только удалить аккаунт целиком — временно приостановить/заблокировать нужно напрямую через API (кнопки пока нет).
admin-users.controller.ts, смонтирован на admin/users.
- Список / список platform-staff / список platform-staff-invitations / детали (
GET) —platform:users:read. - Удаление аккаунта пользователя (
DELETE :id) —platform:users:manage, единственное действие со статусом аккаунта, которое реально есть в UI этой страницы (действие по строке на/platform/users). - Suspend / activate / lock / unlock (
POST :id/suspend|activate|lock|unlock) — подтверждено (2026-08-04, поиск поapps/portal): точки входа в интерфейсе нет ни для одного из четырёх, хотя все четыре полностью реализованы и закрыты правами на бэкенде. Если сегодня нужно приостановить или разблокировать аккаунт — это прямой API-вызов, кнопки нет. - Impersonate — действия «Impersonate» на этой странице не существует. Подтверждено 2026-08-04: Impersonation полностью серверная функция (
POST /auth/impersonate, толькоSUPER_ADMIN) — на этой странице нет подключённой к ней кнопки, хотя это естественное для неё место. - Убрать из platform staff (
DELETE :id/platform-staff) —platform:users:manage. ЗакрываетPlatformStaffMembershipчеловека и все назначенияPlatformRole; отличается от полной приостановки его аккаунтаUser. - Leave / return from leave (
POST :id/leave,POST :id/return-from-leave) —platform:users:manage. Статус внутреннего штата (в отпуске/активен), не приостановка аккаунта — влияет на доступность для назначения на задачи/проекты в модуле Staffing, не на возможность входа.
Приглашения в штат платформы (/platform-staff-invitations/[token])
Простыми словами: как кто-то присоединяется к собственной внутренней команде P4P (в отличие от членства в организации клиента). Отправляется с /platform/staff — отдельный процесс от приглашения кого-то в рабочее пространство компании.
platform-staff-invitations.controller.ts. Отправка приглашения (POST /platform-staff-invitations) закрыта SuperAdminOrOwnerGuard — не то же самое, что SuperAdminGuard, используемый для impersonation и force-owner-transfer. Он допускает SUPER_ADMIN или любого, кто занимает платформенную роль P4P Owner, намеренно: приглашение кого-то в штат платформы — это обычная работа по укомплектованию штата, которую Owner компании должен уметь делать напрямую, в отличие от действий восстановления безопасности, которые остаются только для SUPER_ADMIN. Приглашение никогда не может нести грант роли SUPER_ADMIN (проверяется на сервере, даже несмотря на то что сама отправка уже закрыта). Принятие (GET/POST :token[/accept], публично) создаёт запись PlatformStaffMembership — предпосылку для того, чтобы вообще занимать любую PlatformRole (см. Глоссарий).
Продукты и подписки (/platform/products)
Простыми словами: каталог продуктов, на которые могут быть подписаны организации, и кто на что подписан. Сами продукты добавляют инженеры, не с этой страницы — эта страница только редактирует, как существующие продукты отображаются, и выдаёт/отзывает доступ организации к ним.
admin-products.controller.ts, смонтирован на admin (роуты: products, products/:productId, organizations/:orgId/subscriptions, subscriptions/:subscriptionId).
- Список каталога продуктов —
platform:products:read; создание/редактирование/деактивация —platform:products:manage. Меню редактирования/активации на странице видно при:read, но доступно для использования только при:manage. - Подписки организации — список требует
platform:subscriptions:read, выдача/отзыв —platform:subscriptions:manage. Самостоятельного сценария подписки сегодня нет — организация не может подписаться на продукт сама; это делает платформенный админ.
Роли (/platform/roles)
Простыми словами: та же идея, что и Roles организации (IAM), но для собственной внутренней команды P4P, а не команды клиента — полностью отдельный набор ролей и прав, никогда не смешивается с ролями организации.
platform-roles.controller.ts, смонтирован на admin/platform-roles. Та же форма, что и у страницы Roles уровня организации (IAM), но для PlatformRole/PlatformPermission вместо Role/Permission — структурно отдельная пара таблиц, никогда не разделяемая с RBAC организации.
- Список ролей + список каталога прав —
platform:roles:read. - Передача посеянной роли
SUPER_ADMIN/эквивалента владельца (PATCH owner/transfer) — намеренно без декоратора@RequirePlatformPermission(см. комментарий в коде на строке 49): собственная логикаPlatformRoleGuardдля этого роута обеспечивает что-то более строгое, чем может выразить система прав. Не считай, что «нет декоратора» значит «нет защиты». - Создание / редактирование / удаление кастомной платформенной роли, назначение/снятие роли пользователю, добавление/удаление права у роли — всё требует
platform:roles:manage. Как описано в связанных заметках каталога в Администрирование платформы → Пользователи: обладательplatform:roles:manageвсё ещё может напрямую выдатьSUPER_ADMINуже активному сотруднику платформенного штата черезPOST :roleId/users/:userIdбез дополнительной защиты — известный, сейчас принятый пробел (см.docs/iam/PERMISSIONS_CATALOG.md, «Still open»).
Посеянные роли: SUPER_ADMIN (системная, полный обход), P4P Owner и P4P Super Admin (явно получают полный каталог прав, для отображения в UI — см. Глоссарий), P4P Admin (отобранное подмножество, исключает platform:roles:manage), P4P Member (грант по умолчанию для нового платформенного штата, включает platform:staffing:manage_own).
Журнал аудита (/platform/audit)
Простыми словами: просмотрщик, только для чтения, той же платформенной истории, что описана в IAM → Журнал аудита. Намеренно отделён от «P4P Admin», повседневной админской роли — чтение истории активности всех считается более чувствительным, чем обычная админская работа.
admin-audit.controller.ts, смонтирован на admin/audit-log, platform:audit:read. Просмотр только для чтения над AuditLog, доступным только на добавление (записывается со всей платформы, не только этим модулем — см. IAM → Журнал аудита). Намеренно исключён из удобной роли P4P Admin — «кто следит за следящими» уже, чем обычная админская работа, то же рассуждение, что держит platform:roles:manage вне этой роли.
Тестирование этого модуля
scripts/e2e/menu/platform-admin.mjs (10 проверок) прогоняет основные сценарии этого модуля от начала до конца и проставляет lastVerified выше. Не покрыто автоматизацией, стоит проверить руками — см. docs/MANUAL_TESTING.md: закрытие read/manage правами изменяющих элементов на каждой странице, блокировка роли SUPER_ADMIN в приглашении в штат, выдача/отзыв подписки на продукт, путь передачи владельческой платформенной роли и закрытие platform:audit:read на самой странице Audit Log.