Skip to content

Обзор платформы

Три списка админки: все команды, все люди, все агенты. Это ответ на вопрос, на который продукт раньше ответить не мог — что здесь вообще работает? — и они строго только для чтения.

Кто их видит: администратор платформы, роль отдельная от любой роли внутри команды. Как эта роль устроена и чем защищена — отдельная страница; эта описывает то, что она открывает.

Экран обзора платформы: итоги по командам, людям и агентам и две разбивкиСписок людей в админке: значок администратора платформы и команды каждой учётной записи

Три списка

экранэндпоинтчто говорит строка
КомандыGET admin/teamназвание, сколько людей, сколько агентов, когда создана, когда последняя активность
ЛюдиGET admin/userпочта, имя, все команды и роль в каждой, когда зарегистрирован
АгентыGET admin/agentназвание, владелец, команда, тип, статус, включён ли автозапуск, когда создан

Переходы между ними. admin/user и admin/agent принимают ?teamId=, поэтому строка команды ведёт к её людям и к её агентам, а каждая строка агента несёт свою команду — и ведёт обратно. Это вся модель навигации: один фильтр, а не второй набор экранов.

Любой список — это страница. ?page= и ?pageSize=, pageSize ограничен сотней. Режима «отдай все строки» нет, и это намеренно: команды и агенты — это ровно то, чего становится много.

Чего в списках нет и почему

Никакого содержимого. Администратор платформы видит факты о вещах — названия, счётчики, даты, статусы, роли — и никогда сами вещи. Ни текста сообщений. Ни памяти агентов. Ни SOUL.md, USER.md или HEARTBEAT.md. Ни значений секретов, ни хэшей паролей.

Владелец оставил эту развилку открытой («всё или всё, кроме переписок?»); её решили в сторону меньшего, потому что стороны стоят по-разному. Расширить видимость потом — это одно поле в одном ответе. Забрать обратно увиденную чужую переписку — ничто.

Отметка времени — это факт, поэтому «когда команда последний раз была активна» показывать можно, и берётся она из времени самого свежего сообщения. Само сообщение из базы не выходит.

Никаких действий. Ни на одном из этих экранов нельзя ничего выключить, удалить или войти под кем-то. Не потому, что этого никто не хочет, а потому что административное действие не оставляет следа, пока нет журнала, а действие без следа над чужим арендатором хуже отсутствующего. Действия придут вместе с журналом, отдельной задачей.

Нет «когда заходил». Списки показывают, когда учётная запись зарегистрирована, а не когда её владелец последний раз появлялся, потому что факт входа в системе никто не записывает: нет ни такой колонки, ни таблицы сессий, и сам вход не пишет ничего. Правильный дом для этого — журнал: вход это событие, и колонка отвечала бы только на «когда последний раз», а журнал отвечает ещё и на «сколько раз», «откуда» и «а неудачные попытки?».

Как это устроено

Тонкие эндпоинты поверх проверки роли, а не вторая копия продукта. В группе admin нет ни одной копии продуктового слайса. Это прямой урок 1.x, записанный в README самой группы: «2.0 = thin admin endpoints + a role guard, NOT duplicate modules».

Данные читаются напрямую через Prisma. Группа admin стоит ниже runtime и agent в порядке групп, а значит импортировать их не может — см. Слои-слайсы. Чтение таблиц арендаторов напрямую — это то, что держит порядок нетронутым, и это объявленный замысел, а не обход.

Защита — это префикс. Все маршруты живут под admin/, и одна гвардия, зарегистрированная на всю группу, покрывает всё, что под этим префиксом. Ни один контроллер слайса не пишет @UseGuards — и не должен: проверка, написанная в каждом контроллере, это проверка, которую однажды забудут написать.

Цена строки — не запрос

Испортить такой список проще всего так: получить строки, а потом задать базе ещё по одному вопросу на каждую. Все ответы правильные, поэтому ничего не выглядит сломанным — пока установка не станет настоящей. На этом здесь уже ловили.

Поэтому число запросов задано формой запроса, а не числом строк:

списокзапросов на 1 строкена 12 строкахна 20 строках
Команды666
Люди444
Агенты444

Это измерено, а не заявлено: тест прогоняет каждый список на одной команде и на двенадцати и требует, чтобы числа были равны. Запрос на строку такого сравнения не переживает, какой бы ни была константа.

Это сквозные числа на весь запрос, поэтому в каждое входит один поиск, который делает гвардия роли до того, как отработает обработчик.