UNPKG

overcentric

Version:

A lightweight, privacy-focused toolkit for modern SaaS web applications

122 lines (121 loc) 4.45 kB
import { record } from '@rrweb/record'; import { CONFIG, log } from "."; import { getSessionId } from "./session"; // Default maximum recording duration in milliseconds (1 hour) const DEFAULT_MAX_RECORDING_DURATION = 60 * 60 * 1000; /** * Initialize the session recording feature. */ async function initRecording(maxDuration = DEFAULT_MAX_RECORDING_DURATION) { log.debug('Starting recording'); const sessionId = getSessionId(); const events = []; const BATCH_SIZE = 100; const recordingStartTime = Date.now(); let isRecordingStopped = false; // Register the recording session try { const response = await fetch(`${CONFIG.basePath}/recordings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId, project_id: window.overcentricProjectId, context: window.overcentricContext, hostname: window.location.hostname, pathname: window.location.pathname }) }); if (!response.ok) { throw new Error(`Failed to register recording: ${response.status}`); } log.debug('Recording session registered'); } catch (error) { log.error('Failed to register recording session', error); return; // Don't proceed with recording if registration fails } // Function to send recording chunks to backend const sendRecordingEvents = async (eventsToSend) => { if (!eventsToSend.length) return; try { const response = await fetch(`${CONFIG.basePath}/recordings/${sessionId}/chunks`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: window.overcentricProjectId, events: eventsToSend, timestamp: Date.now() }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } log.debug(`Sent ${eventsToSend.length} recording events`); } catch (error) { log.error('Failed to send recording events', error); } }; // Set up periodic flush of events const flushInterval = setInterval(() => { // Check if max recording duration has been reached if (!isRecordingStopped && Date.now() - recordingStartTime >= maxDuration) { log.debug(`Maximum recording duration of ${maxDuration / 1000} seconds reached, stopping recording`); isRecordingStopped = true; } if (events.length > 0 && !isRecordingStopped) { sendRecordingEvents(events.splice(0)); } }, 5000); // Flush every 5 seconds // Clean up on page unload window.addEventListener('beforeunload', () => { clearInterval(flushInterval); if (events.length > 0 && !isRecordingStopped) { sendRecordingEvents(events.splice(0)); } }); // Function to manually stop recording const stopRecording = () => { if (!isRecordingStopped) { isRecordingStopped = true; clearInterval(flushInterval); if (events.length > 0) { sendRecordingEvents(events.splice(0)); } if (stopFn) stopFn(); log.debug('Recording stopped manually'); } }; // Automatically stop recording after maxDuration if (maxDuration > 0) { setTimeout(stopRecording, maxDuration); } // Add stopRecording function to window for external access if needed window.overcentricStopRecording = stopRecording; const stopFn = record({ emit(event) { // Only add events if we haven't reached the maximum duration if (!isRecordingStopped) { events.push(event); // Send events when batch size is reached if (events.length >= BATCH_SIZE) { sendRecordingEvents(events.splice(0)); } } }, maskAllInputs: true, sampling: { mousemove: true, scroll: 150, input: 'last' } }); } export { initRecording };