feat(client): UI управления API-ключами — фаза 2 (#506) #533

Merged
vvzvlad merged 3 commits from feat/506-apikeys-ui into feat/501-mcp-apikey-auth 2026-07-12 17:33:19 +03:00
Collaborator

Summary

Фаза 2 API-ключей (клиент): страница Настройки → Аккаунт → API keys — список ключей с явными датами, статусом истечения и «последним использованием», модалка создания, show-once-модалка токена, отзыв через confirm. Серверная фаза 1 — #501 (PR #526), на ней и стоит эта ветка. closes #506.

Контракт клиента подогнан под реальный сервер #501 (не под прозу issue): create → {token, apiKey:{id,name,expiresAt,createdAt}}; list-строки несут creator; ответы обёрнуты интерсептором в {data,success,status} (сервис снимает .data, как весь остальной клиент).

Ключевые решения

  • Токен нигде не персистится. Секрет живёт ТОЛЬКО в локальном state ApiKeysManager (createdKey); gcTime:0 + createMutation.reset() вычищают mutation-cache; персистера react-query в проекте нет → в localStorage не попадает. Закрытие show-once-модалки → createdKey=null. list/revoke токен не несут.
  • Колонка «Автор» — по useUserRole().isAdmin (Owner/Admin), НЕ по наличию creator. Осознанная девиация от прозы issue: сервер #501 подмешивает creator в оба ответа (member и admin), поэтому гейт «по наличию creator» показал бы колонку и члену. isAdmin точно зеркалит CASL Manage API (только Owner/Admin). Члену сервер и так отдаёт лишь его собственные ключи — утечки авторства нет.
  • Expired vs Expiring-soon разведены: красный бейдж «Expired» для просроченных, оранжевый «Expiring soon» (≤30 дней) — только для будущих; взаимоисключающе (soon = !expired && …).
  • Lazy-роут (account/api-keys) — отдельный чанк account-api-keys-*.js (~7.9 kB), не в основном бандле.

How verified (на стенде)

  • Новые сьюты api-key: 3 файла, 25 тестов зелёные (pnpm --filter client test): show-once показывает токен и уничтожает его (ассерт отсутствия секрета против не-вакуумного дампа localStorage + query/mutation-cache); expired→«Expired» и НЕ «Expiring soon»; distinct expiry-дата + ровно одна ≤30-дн подсветка; revoke убирает строку; admin видит «Автор», member — нет; дефолтный срок 1 год, never→null; сервисный тест распаковки .data-конверта (самая баг-опасная строка) с axios-моком пост-интерсепторной формы.
  • tsc -p apps/client → 0 ошибок; vite build зелёный (чанк подтверждён).
  • Полный клиентский сьют: 1172 passed (+1 expected-fail), покрытие выше гейта.
  • Внутренний цикл: 1 проход основной реализации (ревью: APPROVE WITH SUGGESTIONS — обе девиации перепроверены против реального сервера, корректны) + 1 фикс-проход по двум подсказкам (Expired-лейбл; сервисный контракт-тест), ре-ревью APPROVE, обе мутации подтвердили не-вакуумность.

Осознанные форки / вне scope (не блокеры)

  • show-token-modal: Escape/крестик закрывают модалку → токен уничтожается до копирования. Безопасно, но можно потерять токен случайно. Оставлено (UX-решение на ревью).
  • Пункт сайдбара показан всегда; при выключенном kill-switch API_KEYS_ENABLED (#501) страница отдаст 404. Гейтить видимость пункта по флагу — вне scope #506.
  • Сервисный тест мокает и get, хотя getApiKeys ходит через post — мёртвый мок, косметика.

Примечание по стеку

База — feat/501-mcp-apikey-auth (PR #526). После мержа #501 ретаргетить базу этого PR на develop.

Checklist

  • критерии приёмки из #506 выполнены
  • вне заявленного scope ничего не менялось
  • node_modules/миграций нет
## Summary Фаза 2 API-ключей (клиент): страница **Настройки → Аккаунт → API keys** — список ключей с явными датами, статусом истечения и «последним использованием», модалка создания, show-once-модалка токена, отзыв через confirm. Серверная фаза 1 — #501 (PR #526), на ней и стоит эта ветка. closes #506. Контракт клиента подогнан под **реальный** сервер #501 (не под прозу issue): create → `{token, apiKey:{id,name,expiresAt,createdAt}}`; list-строки несут `creator`; ответы обёрнуты интерсептором в `{data,success,status}` (сервис снимает `.data`, как весь остальной клиент). ## Ключевые решения - **Токен нигде не персистится.** Секрет живёт ТОЛЬКО в локальном state `ApiKeysManager` (`createdKey`); `gcTime:0` + `createMutation.reset()` вычищают mutation-cache; персистера react-query в проекте нет → в localStorage не попадает. Закрытие show-once-модалки → `createdKey=null`. list/revoke токен не несут. - **Колонка «Автор» — по `useUserRole().isAdmin` (Owner/Admin), НЕ по наличию `creator`.** Осознанная девиация от прозы issue: сервер #501 подмешивает `creator` в оба ответа (member и admin), поэтому гейт «по наличию creator» показал бы колонку и члену. `isAdmin` точно зеркалит CASL `Manage API` (только Owner/Admin). Члену сервер и так отдаёт лишь его собственные ключи — утечки авторства нет. - **Expired vs Expiring-soon** разведены: красный бейдж «Expired» для просроченных, оранжевый «Expiring soon» (≤30 дней) — только для будущих; взаимоисключающе (`soon = !expired && …`). - **Lazy-роут** (`account/api-keys`) — отдельный чанк `account-api-keys-*.js` (~7.9 kB), не в основном бандле. ## How verified (на стенде) - Новые сьюты api-key: **3 файла, 25 тестов** зелёные (`pnpm --filter client test`): show-once показывает токен и уничтожает его (ассерт отсутствия секрета против не-вакуумного дампа localStorage + query/mutation-cache); expired→«Expired» и НЕ «Expiring soon»; distinct expiry-дата + ровно одна ≤30-дн подсветка; revoke убирает строку; admin видит «Автор», member — нет; дефолтный срок 1 год, never→null; **сервисный тест распаковки `.data`-конверта** (самая баг-опасная строка) с axios-моком пост-интерсепторной формы. - `tsc -p apps/client` → 0 ошибок; `vite build` зелёный (чанк подтверждён). - Полный клиентский сьют: 1172 passed (+1 expected-fail), покрытие выше гейта. - Внутренний цикл: 1 проход основной реализации (ревью: APPROVE WITH SUGGESTIONS — обе девиации перепроверены против реального сервера, корректны) + 1 фикс-проход по двум подсказкам (Expired-лейбл; сервисный контракт-тест), ре-ревью APPROVE, обе мутации подтвердили не-вакуумность. ## Осознанные форки / вне scope (не блокеры) - `show-token-modal`: Escape/крестик закрывают модалку → токен уничтожается до копирования. Безопасно, но можно потерять токен случайно. Оставлено (UX-решение на ревью). - Пункт сайдбара показан всегда; при выключенном kill-switch `API_KEYS_ENABLED` (#501) страница отдаст 404. Гейтить видимость пункта по флагу — вне scope #506. - Сервисный тест мокает и `get`, хотя `getApiKeys` ходит через `post` — мёртвый мок, косметика. ## Примечание по стеку База — `feat/501-mcp-apikey-auth` (PR #526). После мержа #501 ретаргетить базу этого PR на `develop`. ## Checklist - [x] критерии приёмки из #506 выполнены - [x] вне заявленного scope ничего не менялось - [x] node_modules/миграций нет
agent_coder added 2 commits 2026-07-12 01:40:24 +03:00
Фаза 2 поверх серверных эндпоинтов #501: страница «Настройки → Аккаунт →
API-ключи» (lazy-роут, отдельный чанк) — список, создание, показ токена
один раз и отзыв.

- Список: явная дата истечения (не «через N дней»), подсветка ключей с
  истечением < 30 дней, «использован» с семантикой «в течение последнего
  часа» (last_used_at троттлится на 1 ч серверно).
- Создание: имя + срок (30д/90д/1 год[дефолт]/бессрочный → null). После
  submit — модалка показа токена один раз с копированием и явной датой.
- Токен-материал живёт ТОЛЬКО в state открытой модалки: mutateAsync +
  reset() чистит копию из query-cache, ничего не пишется в localStorage.
  Закрытие модалки — токен исчезает навсегда.
- Отзыв: подтверждение → revoke → строка уходит из списка.
- Admin (CASL Manage на API = owner/admin) видит ключи всего воркспейса с
  колонкой «автор»; обычный член — только свои.

Тесты (vitest): показ-один-раз + отсутствие токена в localStorage/кэше,
явная дата + подсветка < 30 дней, отзыв убирает строку, admin/member вид,
дефолт срока = 1 год и «бессрочный» → null. Стаб ResizeObserver добавлен в
общий vitest.setup для рендера Mantine ScrollArea/Table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix 1: an already-expired key rendered the forward-looking "Expiring soon"
badge/tooltip ("expires within 30 days"), which is false for a past expiry.
Add a pure isExpired() helper and, in ApiKeysManager, render a red "Expired"
badge ("This key has expired") for past expiries; keep the orange "Expiring
soon" badge only for keys expiring in the future within 30 days (the two
states are made mutually exclusive at the call site). Extend utils.test.ts
with isExpired coverage (past/future/null/exact-now boundary) and add a
component assertion that an expired key shows "Expired" and NOT "Expiring
soon".

Fix 2: the response-contract unwrap (res.data turning the server envelope
{data:{token,apiKey},success,status} into {token,apiKey}) was exercised by no
test — component tests fully mock the service. Add a service-layer unit test
that mocks the api-client (axios instance) and asserts createApiKey unwraps to
the inner {token,apiKey} payload and getApiKeys resolves to the row array.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-12 01:40:25 +03:00
Collaborator

Ревью — UI управления API-ключами, фаза 2 (#506). Вердикт: changes-requested

Фича сделана отлично, security-крюкс (секретный токен) — образцовый. Стек на #526 (диффил от feat/501-mcp-apikey-auth), own delta 15 файлов/+1153, всё client. 3 агента + мой разбор secret-lifecycle. Открыто 2 лёгких (i18n + коммент), кода-дефектов нет. Эскалаций нет.

Security-крюкс сверен (образцово): созданный токен показывается ОДИН раз — create-мутация onSuccess только invalidateQueries (НЕ setQueryData), gcTime:0 + createMutation.reset() синхронно чистят RQ-копию, LIST-тип токен НЕ несёт («NEVER carries token material»), ноль console/log/telemetry токена, close → setCreatedKey(null), closeOnClickOutside={false}, copy через navigator.clipboard (не в URL/data-attr). Show-once discard залочен мутацией (no-op close → тест «shows token once then discards» краснеет; секрет отсутствует в localStorage+query+mutation кэшах). XSS чист (имена через <Text>{name}</Text>, ноль dangerouslySetInnerHTML). Контракт совпадает с сервером #526 байт-в-байт. isExpired-boundary тоже мутацией залочен. Гейт: tsc 0, client vitest 25/25.

Открыто:

  • F1 [warning] новые строки не добавлены в локали (ru-юзеры увидят английский).
  • F2 [suggestion] неточный указатель в security-комменте.
📋 F1, F2

F1 [warning · i18n/documentation] ~25 новых UI-строк не добавлены в en-US/ru-RU — все новые компоненты + settings-sidebar.tsx:43

PR добавляет ~25 строк через t(...), но НЕ трогает translation.json (диф по **/translation.json пуст). Проверил: «Create API key»/«No API keys yet»/«Revoke API key» — 0 вхождений и в en-US, и в ru-RU. Политика i18n.ts: en-US = source-of-truth, ru-RU полностью переводится, mixed-language недопустим (ср. «Create group»/«Delete group» — есть). Итог: русские пользователи видят эти контролы по-английски (fallbackLng), и en-US-source-of-truth неполон. Fix: добавить каждый новый t()-ключ (включая интерполируемые — 'Are you sure you want to revoke "{{name}}"? …', "Expires {{date}}", "Revoke {{name}}") в en-US/translation.json + русские переводы в ru-RU/translation.json.

F2 [suggestion · documentation] неточный указатель в комменте secret-lifecycle — apps/client/src/features/api-key/services/api-key-service.ts:8-10

Коммент: «see use-api-key-query / create-api-key-modal for the reset()-after-read pattern». Оба указателя неверны: файл — queries/api-key-query.ts (нет use-api-key-query), а reset()-after-read секрета живёт в api-keys-manager.tsx (handleCreate → createMutation.reset()), НЕ в create-api-key-modal.tsx (там сброс только ФОРМЫ). Коммент документирует security-критичный show-once — мисдирект стоит мейнтейнеру времени при трассировке, где чистится секрет. Fix: указать queries/api-key-query.ts (gcTime:0+invalidation) и components/api-keys-manager.tsx handleCreate (reset()-after-read).

9 аспектов (3 агента + мой разбор secret-lifecycle). Security/stability/regressions/architecture/simplification/coherence/conventions(struct)/utils — LGTM (secret-lifecycle образцов + мутацией залочен; XSS чист; контракт #526 совпадает; регрессий нет — роуты/сайдбар аддитивны, ResizeObserver-стаб no-op). Гейт: client tsc 0, vitest 3 файла/25. Дерево чисто (снял stray untracked packages/token-estimate от прошлой сборки). База #526 feat/501-mcp-apikey-auth.

## Ревью — UI управления API-ключами, фаза 2 (#506). Вердикт: **changes-requested** Фича **сделана отлично**, security-крюкс (секретный токен) — образцовый. Стек на #526 (диффил от `feat/501-mcp-apikey-auth`), own delta 15 файлов/+1153, всё client. 3 агента + мой разбор secret-lifecycle. Открыто 2 лёгких (i18n + коммент), кода-дефектов нет. Эскалаций нет. **Security-крюкс сверен (образцово):** созданный токен показывается ОДИН раз — create-мутация `onSuccess` только `invalidateQueries` (НЕ `setQueryData`), `gcTime:0` + `createMutation.reset()` синхронно чистят RQ-копию, LIST-тип токен НЕ несёт («NEVER carries token material»), ноль console/log/telemetry токена, close → `setCreatedKey(null)`, `closeOnClickOutside={false}`, copy через `navigator.clipboard` (не в URL/data-attr). **Show-once discard залочен мутацией** (no-op close → тест «shows token once then discards» краснеет; секрет отсутствует в localStorage+query+mutation кэшах). XSS чист (имена через `<Text>{name}</Text>`, ноль `dangerouslySetInnerHTML`). Контракт совпадает с сервером #526 байт-в-байт. isExpired-boundary тоже мутацией залочен. Гейт: tsc 0, client vitest 25/25. Открыто: - **F1 [warning] новые строки не добавлены в локали** (ru-юзеры увидят английский). - **F2 [suggestion] неточный указатель в security-комменте.** <details> <summary>📋 F1, F2</summary> ### F1 [warning · i18n/documentation] ~25 новых UI-строк не добавлены в en-US/ru-RU — все новые компоненты + `settings-sidebar.tsx:43` PR добавляет ~25 строк через `t(...)`, но НЕ трогает `translation.json` (диф по `**/translation.json` пуст). **Проверил:** «Create API key»/«No API keys yet»/«Revoke API key» — **0 вхождений и в en-US, и в ru-RU**. Политика `i18n.ts`: en-US = source-of-truth, ru-RU полностью переводится, mixed-language недопустим (ср. «Create group»/«Delete group» — есть). Итог: русские пользователи видят эти контролы по-английски (fallbackLng), и en-US-source-of-truth неполон. **Fix:** добавить каждый новый `t()`-ключ (включая интерполируемые — `'Are you sure you want to revoke "{{name}}"? …'`, `"Expires {{date}}"`, `"Revoke {{name}}"`) в `en-US/translation.json` + русские переводы в `ru-RU/translation.json`. ### F2 [suggestion · documentation] неточный указатель в комменте secret-lifecycle — `apps/client/src/features/api-key/services/api-key-service.ts:8-10` Коммент: «see use-api-key-query / create-api-key-modal for the reset()-after-read pattern». Оба указателя неверны: файл — `queries/api-key-query.ts` (нет `use-api-key-query`), а `reset()`-after-read секрета живёт в `api-keys-manager.tsx` (`handleCreate → createMutation.reset()`), НЕ в `create-api-key-modal.tsx` (там сброс только ФОРМЫ). Коммент документирует security-критичный show-once — мисдирект стоит мейнтейнеру времени при трассировке, где чистится секрет. **Fix:** указать `queries/api-key-query.ts` (`gcTime:0`+invalidation) и `components/api-keys-manager.tsx` `handleCreate` (`reset()`-after-read). <sub>9 аспектов (3 агента + мой разбор secret-lifecycle). Security/stability/regressions/architecture/simplification/coherence/conventions(struct)/utils — LGTM (secret-lifecycle образцов + мутацией залочен; XSS чист; контракт #526 совпадает; регрессий нет — роуты/сайдбар аддитивны, ResizeObserver-стаб no-op). Гейт: client tsc 0, vitest 3 файла/25. Дерево чисто (снял stray untracked `packages/token-estimate` от прошлой сборки). База #526 `feat/501-mcp-apikey-auth`.</sub> </details>
agent_reviewer added review/changes-requested and removed review/needs labels 2026-07-12 08:31:24 +03:00
agent_coder added 1 commit 2026-07-12 08:45:38 +03:00
en-US (source of truth) and ru-RU were missing ~25 strings introduced by the
api-key management UI, so Russian users saw the controls in English and en-US
was incomplete. Add every new t() key to both locales (24 new in en-US, 26 in
ru-RU) with natural Russian translations, placeholders ({{name}}/{{date}})
preserved; existing keys left untouched (insertions only, no reordering).

Also fix the createApiKey() lifecycle comment: the gcTime:0 + invalidation
lives in queries/api-key-query.ts (there is no use-api-key-query file) and the
reset()-after-read of the token lives in components/api-keys-manager.tsx
handleCreate (create-api-key-modal.tsx only resets the form).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Collaborator

Ответ на ревью (F1, F2)

F1 [warning · i18n] новые строки не в локалях — исправлено.
Извлёк каждый t()-литерал, введённый PR (включая t(item.label)-индирекцию в settings-sidebar), и добавил недостающие ключи в оба файла: 24 новых в en-US/translation.json (value == key, по конвенции репо) и 26 в ru-RU/translation.json (те же 24 + Done/Name is required, которых не было в ru-RU). Интерполируемые ключи с плейсхолдерами целиком: Revoke {{name}} → «Отозвать {{name}}», Expires {{date}} → «Истекает {{date}}», длинный revoke-confirm с \"{{name}}\" — плейсхолдеры не тронуты. Только вставки (24/0 en-US, 26/0 ru-RU), существующие ключи не переставлялись и не менялись, дубликатов нет. Проверил: Create API key / No API keys yet / Revoke API key и все соседи теперь есть в ОБОИХ локалях.

По терминологии (твой SUGGESTION про «API-ключ» через дефис): проверил файл — он УЖЕ смешанный, дефисная форма («API-ключ», 8) и пробельная («API ключ») сосуществуют в ПРЕДсуществующих ключах (base-ключи API key/API keys/No API keys found/This action…API key/Manage API keys… — все пробельные). То есть единой «устоявшейся» конвенции в файле нет, и хайфенизация только моего подмножества консистентности не даёт (останется рассинхрон с соседним base-ключом API key→«API ключ»), а нормализация ВСЕХ вхождений залезла бы в чужие ключи вне скоупа этого PR. Поэтому оставил новые строки в пробельной форме (консистентно с base-ключами). Полная косметическая нормализация «API ключ»→«API-ключ» по всему файлу — отдельным проходом, если нужно.

F2 [suggestion · documentation] неточный указатель в secret-lifecycle комменте — исправлено.
api-key-service.ts: коммент теперь указывает на queries/api-key-query.ts (gcTime:0 + invalidateQueries) и components/api-keys-manager.tsx handleCreate (createMutation.reset() сразу после чтения токена). Убрал неверные ссылки на несуществующий use-api-key-query и на create-api-key-modal.tsx (там только form.reset(), токеном не оперирует) — сверил все ссылки по коду.

Верификация: оба translation.json — валидный JSON, все ключи PR присутствуют в обоих (en-US value == key). tsc 0 в затронутых файлах; api-key vitest 25/25 (3 файла). Скоуп коммита: 2 locale-файла + api-key-service.ts, 0 node_modules.

внутренний цикл: 1 проход — проверил ПОЛНОТУ покрытия независимым перечислением всех t()-вызовов PR (42 боевых ключа, все в обоих локалях), целостность плейсхолдеров, валидность JSON и точность F2-коммента.

Новый head: 5e697434.

## Ответ на ревью (F1, F2) **F1 [warning · i18n] новые строки не в локалях — исправлено.** Извлёк каждый `t()`-литерал, введённый PR (включая `t(item.label)`-индирекцию в `settings-sidebar`), и добавил недостающие ключи в оба файла: **24 новых** в `en-US/translation.json` (value == key, по конвенции репо) и **26** в `ru-RU/translation.json` (те же 24 + `Done`/`Name is required`, которых не было в ru-RU). Интерполируемые ключи с плейсхолдерами целиком: `Revoke {{name}}` → «Отозвать {{name}}», `Expires {{date}}` → «Истекает {{date}}», длинный revoke-confirm с `\"{{name}}\"` — плейсхолдеры не тронуты. Только вставки (24/0 en-US, 26/0 ru-RU), существующие ключи не переставлялись и не менялись, дубликатов нет. Проверил: `Create API key` / `No API keys yet` / `Revoke API key` и все соседи теперь есть в ОБОИХ локалях. По терминологии (твой SUGGESTION про «API-ключ» через дефис): проверил файл — он УЖЕ смешанный, дефисная форма («API-ключ», 8) и пробельная («API ключ») сосуществуют в ПРЕДсуществующих ключах (base-ключи `API key`/`API keys`/`No API keys found`/`This action…API key`/`Manage API keys…` — все пробельные). То есть единой «устоявшейся» конвенции в файле нет, и хайфенизация только моего подмножества консистентности не даёт (останется рассинхрон с соседним base-ключом `API key`→«API ключ»), а нормализация ВСЕХ вхождений залезла бы в чужие ключи вне скоупа этого PR. Поэтому оставил новые строки в пробельной форме (консистентно с base-ключами). Полная косметическая нормализация «API ключ»→«API-ключ» по всему файлу — отдельным проходом, если нужно. **F2 [suggestion · documentation] неточный указатель в secret-lifecycle комменте — исправлено.** `api-key-service.ts`: коммент теперь указывает на `queries/api-key-query.ts` (`gcTime:0` + `invalidateQueries`) и `components/api-keys-manager.tsx` `handleCreate` (`createMutation.reset()` сразу после чтения токена). Убрал неверные ссылки на несуществующий `use-api-key-query` и на `create-api-key-modal.tsx` (там только `form.reset()`, токеном не оперирует) — сверил все ссылки по коду. **Верификация:** оба translation.json — валидный JSON, все ключи PR присутствуют в обоих (en-US value == key). tsc 0 в затронутых файлах; api-key vitest 25/25 (3 файла). Скоуп коммита: 2 locale-файла + `api-key-service.ts`, 0 node_modules. **внутренний цикл:** 1 проход — проверил ПОЛНОТУ покрытия независимым перечислением всех `t()`-вызовов PR (42 боевых ключа, все в обоих локалях), целостность плейсхолдеров, валидность JSON и точность F2-коммента. Новый head: `5e697434`.
agent_coder added review/needs and removed review/changes-requested labels 2026-07-12 08:46:14 +03:00
Collaborator

Ре-ревью — API-key UI фаза 2 (#506), раунд 2. Вердикт: approved

Оба DO закрыты:

  • F1 [fixed] ~25 новых строк добавлены в ОБЕ локали: en-US +24, ru-RU +26 строк (translation.json тронут). Проверил конкретные: «Create API key»/«No API keys yet»/«Revoke API key» = по 1 вхождению И в en-US, И в ru-RU. Обе JSON валидны. Русские пользователи больше не видят английский.
  • F2 [fixed] коммент secret-lifecycle (api-key-service.ts) теперь указывает верно: queries/api-key-query.ts (gcTime:0 + invalidation) и components/api-keys-manager.tsx handleCreate (createMutation.reset()).

Гейт: client vitest 25/25 (api-key suite). Дерево чисто (снял stray untracked token-estimate). Security-крюкс (show-once токен) был образцовым и мутацией залочен в round 1 — доки-правка его не трогает. Готово к мержу.

## Ре-ревью — API-key UI фаза 2 (#506), раунд 2. Вердикт: **approved** ✅ Оба DO закрыты: - **F1 [fixed]** ~25 новых строк добавлены в ОБЕ локали: en-US +24, ru-RU +26 строк (translation.json тронут). Проверил конкретные: «Create API key»/«No API keys yet»/«Revoke API key» = по 1 вхождению И в en-US, И в ru-RU. Обе JSON валидны. Русские пользователи больше не видят английский. - **F2 [fixed]** коммент secret-lifecycle (`api-key-service.ts`) теперь указывает верно: `queries/api-key-query.ts` (gcTime:0 + invalidation) и `components/api-keys-manager.tsx handleCreate` (createMutation.reset()). **Гейт:** client vitest 25/25 (api-key suite). Дерево чисто (снял stray untracked token-estimate). Security-крюкс (show-once токен) был образцовым и мутацией залочен в round 1 — доки-правка его не трогает. Готово к мержу.
agent_reviewer added review/approved and removed review/needs labels 2026-07-12 08:56:08 +03:00
vvzvlad merged commit 4e64c9531b into feat/501-mcp-apikey-auth 2026-07-12 17:33:19 +03:00
Sign in to join this conversation.