0.134.48
Fixed
- Баги страниц аудита: обрезанные «Аргументы»/«Результат», URL-табы, имя пользователя. Три проблемы на страницах
/agents/[id]/auditи/settings/audit: (1) колонки «Аргументы»/«Результат» в табе инструментов отображались черезmax-w-[200px] truncate— полный текст был невидим; (2) на/settings/auditкаждый вид лога не имел своего URL (таб жил только в$state, перезагрузка сбрасывала его, шаринг ссылки невозможен); (3) колонка «Пользователь» показывала числовойuser_idвместо имени. - Бэкенд (
internal/store/{sqlite,postgres}.go,ListAuditLogFiltered):SELECT … FROM audit_logзаменён наLEFT JOIN users u ON u.id = a.user_idсCOALESCE(u.display_name,'') AS user_name,COALESCE(u.username,'') AS user_login. Все ссылки на колонки вWHERE/ORDER BYквалифицированы алиасомa.(после JOINid/action/resource_typeстали неоднозначными —ORDER BY idпадал с ambiguous column name). СтруктураAuditLogEntry(internal/store/store.go) расширена полямиUserName/UserLogin(godoc на английском, без cross-stack ссылок). Миграция не нужна —audit_log.user_idуже FK наusers(id) ON DELETE SET NULL(миграция 002),users.display_name/usernameсуществуют.CreateAuditLogи точки вызова middleware не тронуты (JOIN заполняет поля при чтении). - Frontend — URL-табы (
web/src/lib/features/audit/useAuditTab.svelte.ts): композабл по образцуusePermissionTab— чтение?tab=изwindow.location.searchпри инициализации +history.replaceStateпри смене. VALID-массивы объявленыas constс типом(typeof)[number](один источник правды для union, как вuseMemoryData). Два класса:AgentAuditTab(tools/cron/system) иSettingsAuditTab(system/tools/cron/logs). - Frontend — PageTabs generic (
web/src/lib/components/PageTabs.svelte): компонент параметризован<K extends string>(черезgenerics="K extends string"), что убрало небезопасные кастыas AgentAuditTabKeyна обеих страницах и даст type-safety будущим потребителям. - Frontend — аккордеон (
ExpandableToggle.svelte): клик по строке разворачивает detail-row (<pre class="whitespace-pre-wrap break-words">) с полным содержимым. Применён ко всем длинным полям: tools (args_summary/result_summary), system (details), cron (error), logs (message/attrs). Раскрытие — реальный<button>внутри первой ячейки (а неrole="button"на<td>), сaria-expanded,aria-labelс контекстом строки (timestamp) иaria-hiddenна глифе. Состояние —SvelteSet<string>с composite-ключом${tab}:${id}(исключает коллизии ID между таблицами). - Frontend — имя пользователя (
web/src/lib/utils/formatUser.ts): shared-утилитаuserDisplay:display_name→ иначеusername→ иначе числовойuser_id→ иначе «—». Используется на обеих страницах вместоentry.user_id ?? '—'. - Прочие правки по
ocr review(7 прогонов):$derivedдляcurrentCount,COLS_BY_TABrecord с комментариями-перечислением колонок,title={x || undefined}(вместо|| ''— пустой tooltip), восстановленыsticky left-0на ячейках времени, loading-spinner во всех табах,expandedIds.clear()консолидирован вloadCurrentTab, очистка entry-массивов приswitchTab(stale data cross-tab), сброс фильтров при смене таба,onMountctry/finallyдляloading,async switchTabcawait loadCronNames, button «Далее» disabled приcurrentCount < limit. - Тесты: Go
internal/store/audit_join_test.go—TestSQLiteStore_ListAuditLogFiltered_UserJoin: NULLuser_id→ пустыеuser_name/user_login, user сdisplay_name→ заполненные поля, удаление user (FK ON DELETE SET NULL) → строка остаётся с пустыми полями без паники. Vitestweb/src/lib/utils/formatUser.test.ts— 5 кейсов fallback-цепочки. - Проверки:
golangci-lint— 0 issues,go test ./internal/...— 27 пакетов green,npm run check— 0 errors (309 warnings — все pre-existing a11y),npm test— 420/420 passed,make build— OK. Оставлены как архитектурный scope-creep: snapshot-at-write-time для forensic integrity (задокументировано в godoc), server-sidetotal/hasMoreдля точного disabled кнопки пагинации, глобальныйAbortSignalдля async-loader races (pre-existing паттерн всего приложения).