Перейти к основному содержимому

0012. Журнал аудита действий пользователей и AI-агентов

Копия ADR из репозитория: backend/docs/adr/0012-audit-log.md. Канонический текст — в репозитории; портал обновляется командой npm run sync:adr.

  • Статус: accepted
  • Дата: 2026-06-22
  • Связанные задачи: OP-45

Контекст и проблема​

ТЗ 5.10 и требования модулей предписывают журнал аудита значимых операций (изменение цен/ставок, отправка ответов/жалоб, бронирование) — с явным указанием актора, в том числе AI-агента, объекта и результата. Сейчас аудит эмулируется строковыми логами (this.logger.log('audit.wb-account-…') в wb-accounts.service.ts), которые помечены как заглушки под будущий F6-стор.

Ограничения и опоры из уже сделанного:

  • ORM — TypeORM, миграции явные (0001-orm-and-migrations).
  • Корреляция — CorrelationService.getCorrelationId() из ALS (0011-structured-logging).
  • Актор в системе — только JwtAuthenticatedUser {id,email,role} (0004-rbac…); понятия «AI-агент»/«система» как актора ещё нет.
  • JSONB разрешён конвенцией (0001), но в коде ещё не применялся.

Рассмотренные варианты​

Способ фиксации​

  1. Только программный AuditService.record() — модули вызывают явно.
    • Плюсы: простота, полный контроль над before/after.
    • Минусы: «пометка операций» из ТЗ закрыта лишь частично; многословно на местах вызова.
  2. Только декоратор + интерсептор (@Audit) — авто-фиксация.
    • Плюсы: декларативно, минимум шума в коде.
    • Минусы: интерсептор не знает доменного состояния «до» → before/after недоступны; невыразимы сложные случаи.
  3. Гибрид: сервис + декоратор/интерсептор (выбран).
    • Плюсы: декоратор закрывает массовый простой случай (факт+актор+исход+correlation-id), сервис — сложные случаи с before/after; покрывает оба требования ТЗ.
    • Минусы: два пути фиксации (компенсируется тем, что интерсептор — тонкая обёртка над сервисом).

Модель актора​

  1. Хранить только userId.
    • Минусы: не выразить агента/систему (cron, фоновые джобы) — прямо противоречит ТЗ 5.10.
  2. Полиморфный актор: actorType (user/agent/system) + actorId + actorLabel (выбран).
    • Плюсы: forward-compatible под AI-агентов (появятся с OP-47/48/49) и системные действия; уже сейчас выражает user и system.
    • Минусы: на момент OP-45 ветка agent не имеет продюсера (схема готова, данные — позже).

Решение​

Гибридный механизм + полиморфный актор. Состав модуля src/modules/audit-log/:

  • AuditLogEntity (audit_logs, наследует BaseEntity): actorType (enum audit_actor_type), actorId, actorLabel, action, entityType, entityId, outcome (enum audit_outcome), before/after/metadata (JSONB — первое применение в проекте), correlationId. Индексы по актору, объекту, действию, correlation-id и дате. Записи иммутабельны (не обновляются, не удаляются).
  • AuditService.record() — подставляет correlation-id из ALS, маскирует секреты (redactSecrets), и никогда не пробрасывает исключение в бизнес-код (провал аудита логируется и проглатывается).
  • @Audit({action, entityType}) + AuditInterceptor — авто-фиксация факта/актора/исхода; при ошибке хэндлера ставит outcome=failure и пробрасывает оригинальную ошибку.
  • GET /audit-logs — только для роли Admin, с whitelist-фильтрами и пагинацией (OP-27).
  • Первый консьюмер: ретрофит строковых audit.*-логов в wb-accounts.service.ts на record().

Последствия​

  • Положительные: единый, тестируемый журнал аудита; готовность к акторам-агентам и системным действиям без миграции; сшивка с логами через correlation-id; чувствительные данные маскируются; аудит не влияет на надёжность бизнес-операций.
  • Отрицательные / компромиссы: два пути фиксации; ветка актора agent пока без продюсера; before/after недоступны через интерсептор (только через явный record).
  • Дальше: партиционирование audit_logs по времени при росте — после OP-91 (менеджер партиций); продюсеры-агенты — с инфраструктурой LLM (OP-47/48/49); остальные модули подключают аудит по мере реализации.