Огляд платформи
Три списки адмінки: усі команди, усі люди, усі агенти. Це відповідь на питання, на яке продукт раніше відповісти не міг — що тут узагалі працює? — і вони суворо лише для читання.
Хто їх бачить: адміністратор платформи, роль окрема від будь-якої ролі всередині команди. Як ця роль влаштована і чим захищена — окрема сторінка; ця описує те, що вона відкриває.


Три списки
| екран | ендпоінт | що каже рядок |
|---|---|---|
| Команди | 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 рядках |
|---|---|---|---|
| Команди | 6 | 6 | 6 |
| Люди | 4 | 4 | 4 |
| Агенти | 4 | 4 | 4 |
Це виміряно, а не заявлено: тест проганяє кожен список на одній команді і на дванадцяти й вимагає, щоб числа були рівні. Запит на рядок такого порівняння не переживає, якою б не була константа.
Це наскрізні числа на весь запит, тому в кожне входить один пошук, який робить гвардія ролі перш ніж спрацює обробник.