# Техническое задание: Браузерное расширение для записи сессий операторов ## Фаза 1 — Локальная запись + экспорт архива --- ## 1. Контекст и цель Операторы работают в браузере и периодически совершают ошибки, причины которых сложно диагностировать. Цель Фазы 1 — незаметно для оператора вести непрерывную запись его сессии, а по нажатию кнопки — сохранять архив с данными сессии в папку «Загрузки» для передачи в поддержку. --- ## 2. Стек | Компонент | Технология | |---|---| | Тип расширения | Chrome Extension, Manifest V3 | | Service Worker | Background script (Manifest V3) | | Захват экрана | `chrome.tabCapture` API | | Захват DOM | `rrweb` (npm: rrweb) | | Лог событий | Content Script | | Упаковка архива | `JSZip` (npm: jszip) | | Сохранение файла | `chrome.downloads` API | | Хранение буфера | In-memory (Service Worker + Content Script) | --- ## 3. Структура расширения ``` extension/ ├── manifest.json ├── background/ │ └── service-worker.js # координатор, буфер видео, упаковка архива ├── content/ │ ├── recorder.js # rrweb + лог событий │ └── inject.js # инжектируется на каждую страницу ├── popup/ │ ├── popup.html # кнопка «Сохранить архив» │ └── popup.js └── libs/ ├── rrweb.min.js └── jszip.min.js ``` --- ## 4. Что записывается ### 4.1 Кольцевой видеобуфер (последние 30 секунд) - Захват активной вкладки через `chrome.tabCapture.capture()` - Кодек: `video/webm; codecs=vp9` - Битрейт: `400 000 bps` (~3 МБ/мин) - `MediaRecorder` пишет чанки каждые `2000 мс` - Буфер хранится в памяти Service Worker как массив `{ data: Blob, timestamp: number }` - Чанки старше 30 секунд удаляются автоматически после каждого нового чанка - Первый чанк (WebM-заголовок) **никогда не удаляется** — он нужен для корректного воспроизведения файла **Ожидаемый размер в памяти:** ~1.5–2 МБ постоянно ### 4.2 DOM-снимки через rrweb (последние 10 минут) - `rrweb.record()` пишет события в массив - Хранится в Content Script, передаётся в Service Worker по запросу - События старше 10 минут удаляются по таймеру каждые 60 секунд - Маскирование чувствительных полей: все `input[type=password]` и поля с `data-sensitive="true"` маскируются через `rrweb` опцию `maskInputSelector` **Ожидаемый размер в памяти:** ~1–3 МБ ### 4.3 Лог действий пользователя (последние 10 минут) Каждая запись лога: ```json { "timestamp": 1716800000000, "type": "click | input | navigation | error | network", "target": "CSS-селектор элемента", "value": "текст/значение (маскируется для паролей)", "url": "текущий URL", "meta": {} } ``` Перехватываемые события: - `click` — клик по любому элементу (фиксируется `target`, текст элемента) - `input` — изменение значения поля (без паролей) - `navigation` — `popstate`, `hashchange`, переходы по страницам - `js_error` — `window.onerror`, `window.onunhandledrejection` - `network_error` — `fetch` / `XMLHttpRequest` с кодом ответа `>= 400` **Ожидаемый размер в памяти:** ~100–300 КБ ### 4.4 Скриншот текущего состояния - Делается в момент нажатия кнопки «Сохранить архив» - Через `chrome.tabs.captureVisibleTab()` - Формат: PNG, сохраняется как `screenshot.png` в архив --- ## 5. Кнопка «Сохранить архив» (Popup) ### UI - Минималистичный popup: одна кнопка **«Сохранить отчёт об ошибке»** - Под кнопкой: серый текст «Последние 30 сек видео + 10 мин лога» - Состояния кнопки: `idle` → `Сохраняю...` → `Готово! ✓` → `idle` (через 3 сек) ### Поведение при нажатии 1. Сделать скриншот активной вкладки 2. Запросить у Service Worker: видеобуфер + лог событий 3. Запросить у Content Script: rrweb-снимки 4. Собрать архив (см. раздел 6) 5. Скачать архив через `chrome.downloads.download()` 6. Показать статус «Готово! ✓» --- ## 6. Структура архива Имя файла: `session_report_YYYY-MM-DD_HH-MM-SS.zip` ``` session_report_2025-05-15_14-32-10.zip ├── video/ │ └── last_30sec.webm # склеенные чанки видеобуфера ├── rrweb/ │ └── session.json # массив DOM-событий rrweb ├── logs/ │ └── events.json # лог действий пользователя ├── screenshot.png # скриншот в момент сохранения └── meta.json # метаданные сессии ``` Содержимое `meta.json`: ```json { "timestamp": "2025-05-15T14:32:10Z", "url": "https://...", "userAgent": "Chrome/...", "extensionVersion": "1.0.0", "videoBufferSeconds": 30, "logDurationMinutes": 10, "totalEvents": 143 } ``` --- ## 7. Разрешения в manifest.json ```json { "permissions": [ "tabCapture", "activeTab", "downloads", "scripting", "storage" ], "host_permissions": [ "" ] } ``` > **Примечание:** `tabCapture` требует жеста пользователя при первом запуске. > При первой установке расширение запрашивает разрешение явно. --- ## 8. Важные ограничения и решения | Проблема | Решение | |---|---| | WebM без заголовка не воспроизводится | Первый чанк (header) хранится всегда, не удаляется из буфера | | Service Worker выгружается через 30 сек простоя | Keepalive через `chrome.alarms` каждые 20 сек | | `tabCapture` только для активной вкладки | Фиксируется при активации расширения; при смене вкладки — переподключение | | Большой объём rrweb-данных | Лимит буфера: 5 000 событий; при превышении удаляются самые старые | | Пароли и чувствительные данные | `maskInputSelector` в rrweb + фильтр в логгере событий | --- ## 9. Что НЕ входит в Фазу 1 - Отправка данных на сервер - AI-диагностика - Автоматическое создание тикетов - Аналитический дашборд - Запись звука Эти функции — Фаза 2. --- ## 10. Критерии приёмки Фазы 1 - [ ] Расширение устанавливается в Chrome без ошибок - [ ] Видеобуфер непрерывно работает на любой вкладке - [ ] В буфере всегда есть не более 30 секунд видео - [ ] rrweb пишет DOM-события без ошибок на типовых страницах (формы, таблицы, модальные окна) - [ ] Лог событий фиксирует клики, навигацию и сетевые ошибки - [ ] При нажатии кнопки архив скачивается в «Загрузки» за < 5 секунд - [ ] Архив открывается, `last_30sec.webm` воспроизводится в браузере - [ ] Пароли не попадают в лог и rrweb-снимки - [ ] RAM-потребление расширения не превышает 50 МБ в фоне