1124 lines
38 KiB
Markdown
1124 lines
38 KiB
Markdown
BASE_THINK.md
|
||
|
||
Да, тут лучше сразу зафиксировать не как “страничку с плитками”, а как **отдельный Control Plane / Launcher NODE.DC**: единая точка входа, витрина приложений, управление клиентами, выдача доступов, связка с Authentik и дальше — прокидывание пользователей в Plane/Task Manager, NodeDC и будущие сервисы.
|
||
|
||
Я бы раскладывал так.
|
||
|
||
---
|
||
|
||
## 1. Главная мысль по продукту
|
||
|
||
Launcher — это **не часть NodeDC** и **не часть Task Manager**. Это отдельное React-приложение, которое управляет всей экосистемой.
|
||
|
||
Функционально он отвечает за:
|
||
|
||
1. вход пользователя через Authentik / OIDC;
|
||
2. определение, к какому клиенту / компании относится пользователь;
|
||
3. показ только доступных сервисов;
|
||
4. управление клиентами, пользователями, группами и сервисами;
|
||
5. выдачу доступов на уровне клиента, группы или конкретного участника;
|
||
6. передачу пользователя в нужный сервис по SSO;
|
||
7. хранение каталога приложений: название, описание, URL, slug, статус, превью, порядок отображения;
|
||
8. в будущем — синхронизацию доступов с Authentik, Plane, NodeDC и другими сервисами.
|
||
|
||
Это хорошо совпадает с базовым документом бэкендеров: там уже выделены сущности **Клиент**, **Участник**, **Группа**, **Сервис** и **Доступ**, а доступ может выдаваться всему клиенту, группе или отдельному участнику.
|
||
|
||
---
|
||
|
||
## 2. Важное архитектурное решение: Authentik не должен быть всей бизнес-логикой
|
||
|
||
Authentik лучше использовать как **Identity Provider и SSO-слой**, а не как единственное место хранения бизнес-иерархии.
|
||
|
||
То есть:
|
||
|
||
| Слой | За что отвечает |
|
||
| -------------------- | ----------------------------------------------------------------------------------------------------- |
|
||
| Authentik | логин, пароль, OIDC, группы, claims, SSO, политики входа |
|
||
| Launcher Backend | клиенты, компании, участники, сервисы, доступы, исключения, демо-периоды, статусы, каталог приложений |
|
||
| Plane / Task Manager | workspace, проекты, роли внутри workspace, доски, задачи |
|
||
| NodeDC | workflows, sharing workflow, агентные сценарии, runtime-доступы |
|
||
| Launcher Frontend | витрина, админка, доступы, запуск сервисов |
|
||
|
||
Почему так: в Authentik действительно есть пользователи, группы, роли, приложения, политики, application bindings и entitlements. Группы — это коллекции пользователей, группы могут иметь parent groups, роли могут назначаться группам и наследоваться вниз по иерархии. ([authentik][1]) Но бизнес-сущности типа “ООО Ромашка купила Task Manager до 01.09”, “у Васи исключение из доступа”, “этому клиенту доступен демо-модуль”, “у сервиса вот такое превью” — лучше держать в собственной модели Launcher.
|
||
|
||
Authentik должен получать из Launcher только то, что нужно для SSO и доступа: группы, application bindings, claims, entitlements или синхронизированные атрибуты. Scope mappings в Authentik как раз позволяют передавать данные пользователя и групп в OAuth/OIDC claims. ([authentik][2])
|
||
|
||
---
|
||
|
||
## 3. Доменная модель Launcher
|
||
|
||
Я бы зафиксировал такие сущности.
|
||
|
||
### 3.1. Client / Клиент
|
||
|
||
Клиент — владелец доступа. Это может быть компания или частное лицо.
|
||
|
||
Поля:
|
||
|
||
```ts
|
||
Client {
|
||
id: string;
|
||
type: "company" | "person";
|
||
name: string;
|
||
legalName?: string;
|
||
status: "active" | "suspended" | "demo" | "expired";
|
||
demoEndsAt?: string;
|
||
contactName?: string;
|
||
contactEmail?: string;
|
||
notes?: string;
|
||
createdAt: string;
|
||
updatedAt: string;
|
||
}
|
||
```
|
||
|
||
Пример:
|
||
|
||
```ts
|
||
{
|
||
id: "client_romashka",
|
||
type: "company",
|
||
name: "ООО Ромашка",
|
||
status: "active",
|
||
demoEndsAt: "2026-06-01"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2. User / Участник
|
||
|
||
Пользователь берётся из Authentik, но в Launcher хранится его бизнес-привязка.
|
||
|
||
```ts
|
||
LauncherUser {
|
||
id: string;
|
||
authentikUserId: string;
|
||
email: string;
|
||
name: string;
|
||
avatarUrl?: string;
|
||
globalStatus: "invited" | "active" | "blocked";
|
||
createdAt: string;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3.3. ClientMembership / Членство в клиенте
|
||
|
||
Один пользователь потенциально может быть в нескольких клиентах. Например, интегратор, внешний подрядчик или ваш внутренний супер-админ.
|
||
|
||
```ts
|
||
ClientMembership {
|
||
id: string;
|
||
clientId: string;
|
||
userId: string;
|
||
role: "client_owner" | "client_admin" | "member";
|
||
status: "active" | "disabled";
|
||
}
|
||
```
|
||
|
||
Роли:
|
||
|
||
| Роль | Что может |
|
||
| --------------- | ----------------------------------------- |
|
||
| `root_admin` | ваш главный NODE.DC-админ, видит всё |
|
||
| `support_admin` | ваш внутренний оператор, можно ограничить |
|
||
| `client_owner` | главный админ клиента |
|
||
| `client_admin` | админ клиента |
|
||
| `member` | обычный пользователь |
|
||
|
||
Важно: **root_admin Launcher** не обязательно должен быть тем же самым, что **super-user Authentik**. В Authentik super-user даётся через группу с super-user правами, и эти права наследуются descendant-группами, поэтому с этим нужно аккуратно. ([authentik][1])
|
||
|
||
---
|
||
|
||
### 3.4. ClientGroup / Группа внутри клиента
|
||
|
||
Это не обязательно один-в-один Authentik group. Это бизнес-группа внутри клиента.
|
||
|
||
```ts
|
||
ClientGroup {
|
||
id: string;
|
||
clientId: string;
|
||
name: string;
|
||
description?: string;
|
||
memberIds: string[];
|
||
}
|
||
```
|
||
|
||
Примеры:
|
||
|
||
* Руководство
|
||
* Менеджеры
|
||
* Бухгалтерия
|
||
* Демо-команда
|
||
* IT-администраторы
|
||
|
||
Эта модель уже есть в документе бэкендеров: группы создаются внутри клиента, туда добавляются участники, и сервис может подключаться группе.
|
||
|
||
---
|
||
|
||
### 3.5. Service / Сервис
|
||
|
||
Сервис — это приложение в экосистеме.
|
||
|
||
```ts
|
||
Service {
|
||
id: string;
|
||
slug: string;
|
||
title: string;
|
||
subtitle?: string;
|
||
description: string;
|
||
url: string;
|
||
launchUrl?: string;
|
||
iconUrl?: string;
|
||
coverImageUrl?: string;
|
||
previewVideoUrl?: string;
|
||
ambientVideoUrl?: string;
|
||
accentColor?: string;
|
||
status: "active" | "maintenance" | "hidden" | "disabled";
|
||
order: number;
|
||
authentikApplicationSlug?: string;
|
||
authentikGroupName?: string;
|
||
}
|
||
```
|
||
|
||
Примеры сервисов:
|
||
|
||
* NodeDC Agent Platform
|
||
* NODE.DC Task Manager
|
||
* Tender Agent
|
||
* 1C Assistant
|
||
* Digital Twin Viewer
|
||
* Demo Gallery
|
||
* Internal Tools
|
||
|
||
В базовом документе уже есть поля сервиса: название, описание, иконка, URL, технический slug, статус, связанная группа Authentik, порядок отображения.
|
||
|
||
---
|
||
|
||
### 3.6. ServiceAccess / Доступ к сервису
|
||
|
||
Доступ должен быть трёхуровневым:
|
||
|
||
```ts
|
||
ServiceGrant {
|
||
id: string;
|
||
serviceId: string;
|
||
targetType: "client" | "group" | "user";
|
||
targetId: string;
|
||
appRole: "viewer" | "member" | "admin" | "owner";
|
||
status: "active" | "disabled";
|
||
}
|
||
```
|
||
|
||
Отдельно нужны исключения:
|
||
|
||
```ts
|
||
ServiceAccessException {
|
||
id: string;
|
||
serviceId: string;
|
||
userId: string;
|
||
type: "deny" | "allow";
|
||
reason?: string;
|
||
}
|
||
```
|
||
|
||
Правило вычисления:
|
||
|
||
```txt
|
||
effectiveAccess =
|
||
client is active
|
||
AND service is active
|
||
AND user is active
|
||
AND user has grant via client/group/user
|
||
AND user does not have deny exception
|
||
```
|
||
|
||
Это прямо нужно, потому что в документе бэкендеров есть сценарий: сервис подключен всему клиенту, но конкретному участнику его надо убрать — это должно отображаться как исключение.
|
||
|
||
---
|
||
|
||
## 4. Иерархия прав в интерфейсе
|
||
|
||
### 4.1. Root Admin NODE.DC
|
||
|
||
Видит всё:
|
||
|
||
* все клиенты;
|
||
* всех пользователей из Authentik / Launcher;
|
||
* все сервисы;
|
||
* все доступы;
|
||
* все исключения;
|
||
* статусы синхронизации;
|
||
* ошибки provisioning;
|
||
* аудит действий;
|
||
* может банить, блокировать, приостанавливать клиента;
|
||
* может создавать сервисы и редактировать каталог;
|
||
* может назначать клиента, админов клиента и доступы к приложениям.
|
||
|
||
---
|
||
|
||
### 4.2. Client Owner / Client Admin
|
||
|
||
Видит только свою компанию.
|
||
|
||
Может:
|
||
|
||
* приглашать пользователей;
|
||
* удалять / деактивировать пользователей внутри своей компании;
|
||
* создавать группы внутри клиента;
|
||
* добавлять участников в группы;
|
||
* выдавать доступы только к тем сервисам, которые уже разрешены клиенту;
|
||
* назначать других админов внутри клиента;
|
||
* смотреть, кто к чему имеет доступ.
|
||
|
||
Не может:
|
||
|
||
* видеть других клиентов;
|
||
* создавать глобальные сервисы;
|
||
* менять URL сервисов;
|
||
* выдавать доступ к сервису, который клиенту не куплен / не разрешён;
|
||
* менять Authentik-группы напрямую;
|
||
* видеть системные ошибки других клиентов.
|
||
|
||
---
|
||
|
||
### 4.3. Member
|
||
|
||
Видит только пользовательский Launcher:
|
||
|
||
* доступные сервисы;
|
||
* карточку профиля;
|
||
* выход из аккаунта;
|
||
* возможно — выбор компании, если он состоит в нескольких клиентах.
|
||
|
||
Никаких глобальных настроек.
|
||
|
||
---
|
||
|
||
## 5. UX-структура приложения
|
||
|
||
Ты правильно смотришь в сторону референса. Его можно взять не буквально, а как композиционный принцип:
|
||
|
||
> полноэкранный медиа-фон + верхний минимальный бар + нижняя карусель сервисов + glass-detail выбранного сервиса + отдельный админский слой.
|
||
|
||
### 5.1. Пользовательский экран
|
||
|
||
Композиция:
|
||
|
||
```txt
|
||
┌─────────────────────────────────────────────┐
|
||
│ Logo Search/Profile/⚙ │
|
||
│ │
|
||
│ │
|
||
│ Ambient preview выбранного сервиса │
|
||
│ / абстрактная анимация / видео │
|
||
│ │
|
||
│ Glass Detail Card │
|
||
│ Название │
|
||
│ Описание │
|
||
│ Статус │
|
||
│ [Открыть] │
|
||
│ │
|
||
│ │
|
||
│ [NodeDC] [Task Manager] [1C] [Demo] │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
Внизу — не “проекты” в смысле Plane Projects, а лучше назвать:
|
||
|
||
* **Контуры**
|
||
* **Сервисы**
|
||
* **Приложения**
|
||
* **Рабочие среды**
|
||
|
||
Я бы выбрал **“Сервисы”** или **“Контуры”**, потому что “Проекты” уже есть внутри Plane.
|
||
|
||
---
|
||
|
||
### 5.2. Поведение выбора сервиса
|
||
|
||
При клике на карточку в нижней панели:
|
||
|
||
1. центральный фон меняется на `ambientVideoUrl` / `previewVideoUrl` / `coverImageUrl`;
|
||
2. поверх появляется большая glass-карточка;
|
||
3. справа или по центру показывается описание;
|
||
4. кнопка “Открыть” ведёт на SSO launch URL;
|
||
5. если сервис на техработах — кнопка disabled, статус “Техработы”;
|
||
6. если сервис скрыт — обычный пользователь его не видит;
|
||
7. root admin может видеть скрытые сервисы с бейджем.
|
||
|
||
---
|
||
|
||
### 5.3. Верхняя панель
|
||
|
||
Минимальная:
|
||
|
||
* логотип NODE.DC / H&H DC;
|
||
* выбранный клиент, если у пользователя несколько компаний;
|
||
* кнопка “Администрирование”, если есть права;
|
||
* профиль пользователя;
|
||
* выход.
|
||
|
||
По дизайн-коду верхние панели должны держать единую горизонтальную ось, одинаковую высоту кнопок и нормальные радиусы.
|
||
|
||
---
|
||
|
||
## 6. Админка: не отдельная страница, а слой поверх Launcher
|
||
|
||
Так как приложение одностраничное, админку лучше делать не как `/admin`, а как **режим / overlay**:
|
||
|
||
```txt
|
||
[Launcher View]
|
||
↓
|
||
[Admin Command Center overlay / drawer]
|
||
```
|
||
|
||
Варианты:
|
||
|
||
1. правая большая glass-панель;
|
||
2. полноэкранный matte glass overlay;
|
||
3. split view: слева список, справа детали;
|
||
4. command-center в стиле системной панели.
|
||
|
||
Для MVP я бы делал **полноэкранный админский overlay** с внутренними вкладками.
|
||
|
||
---
|
||
|
||
## 7. Разделы админки
|
||
|
||
### Для root admin
|
||
|
||
```txt
|
||
Администрирование
|
||
├── Клиенты
|
||
├── Участники
|
||
├── Группы
|
||
├── Каталог сервисов
|
||
├── Доступы
|
||
├── Инвайты
|
||
├── Синхронизация
|
||
└── Аудит
|
||
```
|
||
|
||
Это расширяет базовый документ, где уже есть разделы “Клиенты”, “Участники”, “Группы внутри клиента”, “Каталог сервисов”, “Доступы” и “Статус Authentik”.
|
||
|
||
---
|
||
|
||
### Для client admin
|
||
|
||
```txt
|
||
Администрирование компании
|
||
├── Участники
|
||
├── Группы
|
||
├── Доступы к сервисам
|
||
├── Инвайты
|
||
└── Профиль компании
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Экран “Клиенты”
|
||
|
||
Список клиентов:
|
||
|
||
| Клиент | Тип | Статус | Участники | Сервисы | Демо до | Контакт |
|
||
| ----------- | ------------ | ------- | --------- | ------- | ------- | ------- |
|
||
| ООО Ромашка | Компания | Активен | 18 | 2 | — | Иван |
|
||
| Иван Петров | Частное лицо | Демо | 2 | 1 | 01.06 | Иван |
|
||
|
||
Детальная карточка клиента:
|
||
|
||
* название;
|
||
* тип;
|
||
* статус;
|
||
* демо-доступ;
|
||
* контактное лицо;
|
||
* заметки;
|
||
* участники;
|
||
* группы;
|
||
* подключенные сервисы;
|
||
* история действий;
|
||
* синхронизация.
|
||
|
||
---
|
||
|
||
## 9. Экран “Каталог сервисов”
|
||
|
||
Сервис — это настраиваемая витрина.
|
||
|
||
Поля в форме:
|
||
|
||
```txt
|
||
Название
|
||
Slug
|
||
Краткое описание
|
||
Полное описание
|
||
URL
|
||
Launch URL
|
||
Статус: активен / скрыт / техработы / отключен
|
||
Иконка
|
||
Обложка
|
||
Превью-видео
|
||
Ambient-видео
|
||
Цветовой акцент
|
||
Порядок отображения
|
||
Связанная группа Authentik
|
||
Связанное приложение Authentik
|
||
Доступен всем новым клиентам: да/нет
|
||
```
|
||
|
||
Очень важно: медиа нужно сразу предусмотреть мультиформатно:
|
||
|
||
```ts
|
||
ServiceMedia {
|
||
icon?: AssetRef;
|
||
thumbnail?: AssetRef;
|
||
coverImage?: AssetRef;
|
||
previewVideo?: AssetRef;
|
||
ambientVideo?: AssetRef;
|
||
fallbackGradient?: string;
|
||
}
|
||
```
|
||
|
||
Поддержать:
|
||
|
||
* `.png`
|
||
* `.jpg`
|
||
* `.webp`
|
||
* `.gif`
|
||
* `.mp4`
|
||
* `.webm`
|
||
|
||
На фронте:
|
||
|
||
```ts
|
||
type MediaKind = "image" | "video" | "gif" | "gradient";
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Экран “Доступы”
|
||
|
||
Это ключевой экран.
|
||
|
||
Я бы делал не просто таблицу, а **матрицу доступа**:
|
||
|
||
```txt
|
||
Клиент: ООО Ромашка
|
||
|
||
Участник Task Manager NodeDC 1C Assistant
|
||
---------------------------------------------------------
|
||
Иван Admin Admin —
|
||
Вася Member — —
|
||
Лена Member Member Deny
|
||
Бухгалтерия Member — Member
|
||
```
|
||
|
||
И рядом объяснение effective access:
|
||
|
||
```txt
|
||
Лена / NodeDC:
|
||
Нет доступа, потому что есть индивидуальное исключение deny.
|
||
|
||
Вася / Task Manager:
|
||
Есть доступ, потому что сервис подключен всему клиенту.
|
||
|
||
Пётр / 1C Assistant:
|
||
Есть доступ, потому что состоит в группе “Бухгалтерия”.
|
||
```
|
||
|
||
Это прямо соответствует требованию из PDF: надо отображать итоговый доступ пользователя и объяснять, почему он есть или отсутствует.
|
||
|
||
---
|
||
|
||
## 11. Как это ложится на Plane / Task Manager
|
||
|
||
Здесь лучше зафиксировать жёсткое правило:
|
||
|
||
> Один клиент в Launcher = один workspace в Task Manager.
|
||
|
||
Например:
|
||
|
||
```txt
|
||
Client: ООО Ромашка
|
||
Plane Workspace: romashka
|
||
```
|
||
|
||
Дальше:
|
||
|
||
1. root admin создаёт клиента;
|
||
2. клиенту подключается сервис `task-manager`;
|
||
3. backend создаёт / связывает Plane workspace;
|
||
4. все пользователи клиента, которым выдан доступ к Task Manager, автоматически появляются в этом workspace;
|
||
5. внутри Plane админ клиента уже создаёт проекты;
|
||
6. распределение людей по проектам остаётся штатной логикой Plane.
|
||
|
||
То есть Launcher **не должен управлять каждым проектом Plane**. Он управляет только:
|
||
|
||
* доступом к приложению;
|
||
* принадлежностью пользователя к клиенту;
|
||
* provisioning в workspace.
|
||
|
||
А уже Plane управляет:
|
||
|
||
* проектами;
|
||
* досками;
|
||
* задачами;
|
||
* ролями внутри workspace;
|
||
* project-level access.
|
||
|
||
Это правильная граница, потому что вы не ломаете внутреннюю модель Plane.
|
||
|
||
---
|
||
|
||
## 12. Как это ложится на NodeDC
|
||
|
||
Для NodeDC логика похожая, но глубже:
|
||
|
||
```txt
|
||
Client: ООО Ромашка
|
||
NodeDC Tenant / Workspace: romashka
|
||
NodeDC Workflows: выдаются через NodeDC sharing
|
||
```
|
||
|
||
Launcher отвечает за:
|
||
|
||
* доступ к приложению NodeDC;
|
||
* роль пользователя в NodeDC: member/admin;
|
||
* привязку пользователя к клиентскому пространству.
|
||
|
||
NodeDC отвечает за:
|
||
|
||
* workflow;
|
||
* sharing workflow;
|
||
* runtime-права;
|
||
* агенты;
|
||
* запуск / мониторинг;
|
||
* доступ к конкретным сценариям.
|
||
|
||
То есть в MVP Launcher не должен пытаться управлять каждым workflow. Он должен только открыть пользователю вход в NodeDC и передать идентичность / клиент / роль.
|
||
|
||
---
|
||
|
||
## 13. Authentik: как лучше состыковать
|
||
|
||
В Authentik есть Applications, Providers, Groups, Roles, Policies, Application bindings и Entitlements. Для приложений важно помнить: если на приложение не задано bindings, то по умолчанию все пользователи могут получить к нему доступ, поэтому для ваших сервисов лучше всегда задавать bindings явно. ([authentik][3])
|
||
|
||
Рекомендуемая схема:
|
||
|
||
```txt
|
||
Authentik
|
||
├── Application: launcher
|
||
├── Application: task-manager
|
||
├── Application: nodedc
|
||
├── Group: nodedc-root-admins
|
||
├── Group: client-romashka-users
|
||
├── Group: client-romashka-admins
|
||
├── Group: service-task-manager-romashka
|
||
└── Group: service-nodedc-romashka
|
||
```
|
||
|
||
Но бизнес-правило всё равно должно жить в Launcher.
|
||
|
||
Пример flow:
|
||
|
||
```txt
|
||
Launcher Backend:
|
||
user Вася имеет доступ к Task Manager
|
||
↓
|
||
создать / обновить группу в Authentik
|
||
↓
|
||
добавить Васю в group service-task-manager-romashka
|
||
↓
|
||
Authentik application binding разрешает вход в Task Manager
|
||
↓
|
||
Task Manager получает пользователя через OIDC
|
||
↓
|
||
Task Manager backend проверяет / создаёт пользователя в workspace romashka
|
||
```
|
||
|
||
OIDC scopes в Authentik уже включают `openid`, `profile`, `email`, `entitlements`, `offline_access`; `profile` включает базовую информацию пользователя и group membership, а entitlements можно использовать для application-level прав. ([authentik][4])
|
||
|
||
---
|
||
|
||
## 14. Что фронту делать сейчас без настоящего Authentik
|
||
|
||
Фронт сейчас надо делать так, будто Authentik уже есть, но через mock contract.
|
||
|
||
### Mock endpoint `GET /api/me`
|
||
|
||
```ts
|
||
type MeResponse = {
|
||
user: {
|
||
id: string;
|
||
name: string;
|
||
email: string;
|
||
avatarUrl?: string;
|
||
};
|
||
launcherRole: "root_admin" | "support_admin" | "client_owner" | "client_admin" | "member";
|
||
memberships: {
|
||
clientId: string;
|
||
clientName: string;
|
||
role: "client_owner" | "client_admin" | "member";
|
||
}[];
|
||
activeClientId: string;
|
||
permissions: {
|
||
canOpenAdmin: boolean;
|
||
canManageClients: boolean;
|
||
canManageOwnClient: boolean;
|
||
canManageServiceCatalog: boolean;
|
||
canInviteUsers: boolean;
|
||
};
|
||
};
|
||
```
|
||
|
||
### Mock endpoint `GET /api/launcher/services`
|
||
|
||
```ts
|
||
type LauncherServiceView = {
|
||
id: string;
|
||
slug: string;
|
||
title: string;
|
||
description: string;
|
||
status: "active" | "maintenance" | "hidden";
|
||
userAccess: "allowed" | "denied";
|
||
appRole?: "viewer" | "member" | "admin" | "owner";
|
||
openUrl?: string;
|
||
media: {
|
||
icon?: string;
|
||
thumbnail?: string;
|
||
coverImage?: string;
|
||
previewVideo?: string;
|
||
ambientVideo?: string;
|
||
};
|
||
};
|
||
```
|
||
|
||
### Mock endpoint `GET /api/admin/clients`
|
||
|
||
Для root admin.
|
||
|
||
### Mock endpoint `GET /api/admin/client/:id/access-matrix`
|
||
|
||
Для матрицы доступов.
|
||
|
||
---
|
||
|
||
## 15. Frontend state-модель
|
||
|
||
Я бы делал так:
|
||
|
||
```ts
|
||
LauncherState {
|
||
mode: "user" | "admin";
|
||
activeClientId: string;
|
||
selectedServiceId?: string;
|
||
serviceRail: LauncherServiceView[];
|
||
me: MeResponse;
|
||
}
|
||
```
|
||
|
||
Для админки:
|
||
|
||
```ts
|
||
AdminState {
|
||
activeSection:
|
||
| "clients"
|
||
| "users"
|
||
| "groups"
|
||
| "services"
|
||
| "access"
|
||
| "invites"
|
||
| "sync"
|
||
| "audit";
|
||
|
||
selectedClientId?: string;
|
||
selectedUserId?: string;
|
||
selectedServiceId?: string;
|
||
}
|
||
```
|
||
|
||
Технически:
|
||
|
||
* React;
|
||
* TypeScript;
|
||
* Tailwind;
|
||
* Zustand или Redux Toolkit для UI state;
|
||
* React Query / TanStack Query для API;
|
||
* mock API adapter, который потом меняется на реальный backend;
|
||
* без реального Authentik на первом этапе.
|
||
|
||
---
|
||
|
||
## 16. UI-компоненты
|
||
|
||
### Базовые
|
||
|
||
```txt
|
||
AppShell
|
||
TopBar
|
||
ProfileMenu
|
||
ClientSwitcher
|
||
ServiceRail
|
||
ServiceTile
|
||
ServiceStage
|
||
ServiceDetailGlassCard
|
||
LaunchButton
|
||
StatusBadge
|
||
AdminOverlay
|
||
AdminSidebar
|
||
AdminSectionHeader
|
||
GlassTable
|
||
AccessMatrix
|
||
UserPicker
|
||
GroupPicker
|
||
ServiceEditor
|
||
InviteModal
|
||
ConfirmModal
|
||
ActionDropdown
|
||
MediaPreviewField
|
||
```
|
||
|
||
### Shared design components
|
||
|
||
```txt
|
||
GlassSurface
|
||
GlassCard
|
||
RoundIconButton
|
||
AccentButton
|
||
SecondaryButton
|
||
DangerButton
|
||
Checker
|
||
Chip
|
||
PortalDropdown
|
||
SearchInput
|
||
SegmentedControl
|
||
```
|
||
|
||
Это важно, потому что в вашем дизайн-коде прямо написано: новый экран или popup не стилизуется локально “на глаз”; сначала используется shared-class или shared-component, а если shared-слоя нет — создаётся reusable-компонент.
|
||
|
||
---
|
||
|
||
## 17. Дизайн-канон для Launcher
|
||
|
||
Из текущего HDESIGN-CODE надо взять:
|
||
|
||
1. matte black glass для popup, dropdown, modal, sidebar overlays и settings-карточек;
|
||
2. blur / backdrop-filter;
|
||
3. мягкую стеклянную границу;
|
||
4. большие радиусы;
|
||
5. отсутствие жёстких outline;
|
||
6. круглые action-кнопки;
|
||
7. акцентные CTA с правильным контрастом текста;
|
||
8. dropdown только через portal, если он внутри scroll/detail/card/sticky header.
|
||
|
||
Радиусы можно взять напрямую:
|
||
|
||
```css
|
||
--launcher-radius-xl: 1.75rem;
|
||
--launcher-radius-card: 1.35rem;
|
||
--launcher-radius-control: 1.25rem;
|
||
--launcher-radius-circle: 999px;
|
||
```
|
||
|
||
Из дизайн-кода:
|
||
|
||
* большие surface-контейнеры: `1.75rem`;
|
||
* glass-карточки: `1.35rem`;
|
||
* поля, селекты, кнопки, chip-кнопки: `1.25rem`;
|
||
* малые круглые action-кнопки: `999px`.
|
||
|
||
---
|
||
|
||
## 18. Как адаптировать референс
|
||
|
||
Референс даёт правильную композицию:
|
||
|
||
* большой экран;
|
||
* затемнённый / заблюренный фон;
|
||
* центральная медиа-зона;
|
||
* мягкая glass-панель;
|
||
* нижняя timeline/rail-панель;
|
||
* плавающий правый assistant-like модуль.
|
||
|
||
Для Launcher это можно перевести так:
|
||
|
||
```txt
|
||
Reference Video Editor → Launcher
|
||
|
||
Storyboard → список сервисов / контуров
|
||
AI Assistant card → detail выбранного сервиса / админ-панель
|
||
Moodboard → медиа-превью / related apps
|
||
Timeline bottom → service rail
|
||
Main preview → ambient preview выбранного сервиса
|
||
```
|
||
|
||
То есть визуально это будет не “админка”, а **входная операционная панель экосистемы**.
|
||
|
||
---
|
||
|
||
## 19. MVP-границы
|
||
|
||
На первом этапе не надо делать всё.
|
||
|
||
### MVP 1 — пользовательский Launcher
|
||
|
||
* mock login state;
|
||
* верхний бар;
|
||
* профиль;
|
||
* список доступных сервисов;
|
||
* нижняя карусель;
|
||
* выбранный сервис;
|
||
* glass-detail;
|
||
* кнопка “Открыть”;
|
||
* статусы `active / maintenance / hidden`;
|
||
* mock media assets.
|
||
|
||
### MVP 2 — root admin shell
|
||
|
||
* кнопка “Администрирование”;
|
||
* overlay;
|
||
* разделы:
|
||
|
||
* Клиенты;
|
||
* Участники;
|
||
* Каталог сервисов;
|
||
* Доступы;
|
||
* CRUD на mock data;
|
||
* access matrix;
|
||
* service editor.
|
||
|
||
### MVP 3 — client admin shell
|
||
|
||
* ограниченная админка;
|
||
* участники своей компании;
|
||
* группы своей компании;
|
||
* инвайты;
|
||
* выдача доступов только к разрешённым сервисам.
|
||
|
||
### MVP 4 — backend contract ready
|
||
|
||
* заменить mock adapter на API;
|
||
* оставить те же UI-компоненты;
|
||
* добавить loading / error / empty states;
|
||
* добавить sync-status;
|
||
* добавить audit log.
|
||
|
||
---
|
||
|
||
## 20. Инвайты и регистрация
|
||
|
||
Лучше сейчас зафиксировать:
|
||
|
||
> MVP работает только по invite-only модели.
|
||
|
||
Сценарий:
|
||
|
||
```txt
|
||
1. Root admin создаёт клиента.
|
||
2. Root admin создаёт client_owner.
|
||
3. Client owner заходит в Launcher.
|
||
4. Client owner создаёт invite.
|
||
5. Пользователь переходит по одноразовой ссылке.
|
||
6. Authentik создаёт / активирует пользователя.
|
||
7. Launcher связывает пользователя с clientId.
|
||
8. Пользователь видит только сервисы, доступные ему.
|
||
```
|
||
|
||
Если позже появится свободная регистрация, то новый пользователь должен попадать в состояние:
|
||
|
||
```txt
|
||
unassigned / pending / no services
|
||
```
|
||
|
||
То есть он вошёл, но ничего не видит, пока root admin не привяжет его к клиенту.
|
||
|
||
---
|
||
|
||
## 21. Синхронизация
|
||
|
||
В админке надо сразу предусмотреть статус:
|
||
|
||
```ts
|
||
SyncStatus {
|
||
target: "authentik" | "task-manager" | "nodedc";
|
||
status: "synced" | "pending" | "error";
|
||
lastSyncAt?: string;
|
||
errorMessage?: string;
|
||
}
|
||
```
|
||
|
||
В PDF уже есть статусы Authentik: “синхронизировано”, “ожидает синхронизации”, “ошибка синхронизации”, а также действия “повторить” и “открыть детали ошибки”.
|
||
|
||
В UI это нужно показывать не техническим логом, а понятной плашкой:
|
||
|
||
```txt
|
||
Authentik: синхронизировано
|
||
Task Manager: ожидает создания workspace
|
||
NodeDC: ошибка синхронизации — открыть детали
|
||
```
|
||
|
||
---
|
||
|
||
## 22. Главные риски
|
||
|
||
### Риск 1. Смешать роли Launcher и роли приложений
|
||
|
||
Нельзя делать одну роль “admin” на всё.
|
||
|
||
Нужно разделить:
|
||
|
||
```txt
|
||
launcherRole: root_admin / client_admin / member
|
||
taskManagerRole: owner / admin / member
|
||
nodeDcRole: admin / editor / viewer
|
||
```
|
||
|
||
---
|
||
|
||
### Риск 2. Назвать сервисы “проектами”
|
||
|
||
В Plane уже есть Projects. В NodeDC есть Workflows. В Launcher лучше использовать “Сервисы” или “Контуры”.
|
||
|
||
---
|
||
|
||
### Риск 3. Полагаться только на Authentik groups
|
||
|
||
Authentik groups хороши для SSO и грубой авторизации, но ваша коммерческая модель доступа должна жить в Launcher.
|
||
|
||
---
|
||
|
||
### Риск 4. Не сделать deny exceptions
|
||
|
||
Без исключений будет больно: “всему клиенту доступ дали, но Васе нельзя”.
|
||
|
||
---
|
||
|
||
### Риск 5. Сделать админку как обычный скучный CRUD
|
||
|
||
У вас визуальная система сильная. Админка должна быть такой же glass/control-plane, но с нормальной читаемостью таблиц.
|
||
|
||
---
|
||
|
||
## 23. ТЗ для Codex — первый проход
|
||
|
||
Можно дать Кодексу так:
|
||
|
||
```md
|
||
# ТЗ: NODE.DC Launcher Frontend MVP
|
||
|
||
## Цель
|
||
Создать отдельное одностраничное React-приложение Launcher для входа в экосистему NODE.DC. Приложение должно работать без реального Authentik на первом этапе, но иметь mock API-контракт, совместимый с будущей OIDC/Authentik-интеграцией.
|
||
|
||
## Основные режимы
|
||
1. User Launcher View — витрина доступных сервисов.
|
||
2. Root Admin View — глобальная админка NODE.DC.
|
||
3. Client Admin View — админка конкретного клиента.
|
||
|
||
## Дизайн
|
||
Использовать NODE.DC glass-canon:
|
||
- matte black glass;
|
||
- backdrop blur;
|
||
- крупные радиусы;
|
||
- без browser outline;
|
||
- круглые action-кнопки;
|
||
- portal dropdown;
|
||
- русскоязычный UI;
|
||
- reusable shared components, без локальной стилизации “на глаз”.
|
||
|
||
## User Launcher View
|
||
Сделать:
|
||
- fullscreen shell;
|
||
- top bar с логотипом, выбранным клиентом, профилем;
|
||
- центральную media-stage зону;
|
||
- нижнюю service rail панель;
|
||
- service tile карточки;
|
||
- detail glass-card выбранного сервиса;
|
||
- кнопку “Открыть”;
|
||
- статусы сервиса: доступен / техработы / скрыт;
|
||
- поддержку image / gif / mp4 / webm preview.
|
||
|
||
## Admin View
|
||
Сделать overlay-панель администрирования:
|
||
- Клиенты;
|
||
- Участники;
|
||
- Группы;
|
||
- Каталог сервисов;
|
||
- Доступы;
|
||
- Инвайты;
|
||
- Синхронизация.
|
||
|
||
## Mock Data
|
||
Добавить mock:
|
||
- ООО Ромашка;
|
||
- ООО Рога и Копыта;
|
||
- NodeDC Agent Platform;
|
||
- NODE.DC Task Manager;
|
||
- 1C Assistant;
|
||
- несколько пользователей с разными ролями.
|
||
|
||
## Access Engine
|
||
Реализовать функцию computeEffectiveAccess:
|
||
- учитывать client status;
|
||
- service status;
|
||
- grants на client/group/user;
|
||
- deny exceptions;
|
||
- user status.
|
||
|
||
## Acceptance Criteria
|
||
- member видит только доступные сервисы;
|
||
- client_admin видит админку только своей компании;
|
||
- root_admin видит все разделы;
|
||
- сервис на техработах виден, но кнопка “Открыть” disabled;
|
||
- hidden-сервис не виден member-у;
|
||
- deny exception перекрывает client-level grant;
|
||
- dropdown/menu рендерятся через portal;
|
||
- UI не содержит смешанных английских подписей.
|
||
```
|
||
|
||
---
|
||
|
||
## 24. Мой вердикт по структуре
|
||
|
||
Делать надо так:
|
||
|
||
```txt
|
||
Launcher
|
||
├── витрина сервисов
|
||
├── клиентская админка
|
||
├── root-админка
|
||
├── каталог сервисов
|
||
├── access matrix
|
||
├── invite flow
|
||
├── sync status
|
||
└── SSO launch layer
|
||
```
|
||
|
||
А не так:
|
||
|
||
```txt
|
||
просто React-страница с кнопками на разные сайты
|
||
```
|
||
|
||
Потому что у вас уже не набор сайтов, а экосистема:
|
||
|
||
```txt
|
||
Информационный сайт
|
||
↓
|
||
Launcher / Auth
|
||
↓
|
||
NodeDC Agent Platform
|
||
↓
|
||
Task Manager / Plane
|
||
↓
|
||
дальше 1C, тендеры, digital twins, демки, внутренние сервисы
|
||
```
|
||
|
||
И Launcher становится тем самым верхним слоем, который делает из отдельных приложений единый веб-суперапп.
|
||
|
||
[1]: https://docs.goauthentik.io/users-sources/groups/manage_groups/ "Manage groups | authentik"
|
||
[2]: https://docs.goauthentik.io/add-secure-apps/providers/property-mappings/ "Provider property mappings | authentik"
|
||
[3]: https://docs.goauthentik.io/add-secure-apps/applications/manage_apps/ "Manage applications | authentik"
|
||
[4]: https://docs.goauthentik.io/add-secure-apps/providers/oauth2/ "OAuth 2.0 provider | authentik"
|