From c6733897b4bcbc3f2564d0aa8f9ee901a82c2825 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 14 May 2026 03:26:24 -0700 Subject: [PATCH] Fetch recorder settings from website config endpoint --- rollup.recorder.config.js | 2 + .../settings/WebsiteReplaySettings.tsx | 4 +- .../websites/[websiteId]/recorder/route.ts | 45 +++++++++++++++ src/recorder/index.js | 55 ++++++++++++++----- 4 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 src/app/api/websites/[websiteId]/recorder/route.ts diff --git a/rollup.recorder.config.js b/rollup.recorder.config.js index c1dd305e7..aa1794018 100644 --- a/rollup.recorder.config.js +++ b/rollup.recorder.config.js @@ -16,6 +16,8 @@ export default { replace({ __COLLECT_API_HOST__: process.env.COLLECT_API_HOST || '', __COLLECT_REPLAY_ENDPOINT__: process.env.COLLECT_REPLAY_ENDPOINT || '/api/record', + __RECORDER_CONFIG_ENDPOINT__: + process.env.RECORDER_CONFIG_ENDPOINT || '/api/websites/{websiteId}/recorder', delimiters: ['', ''], preventAssignment: true, }), diff --git a/src/app/(main)/websites/[websiteId]/settings/WebsiteReplaySettings.tsx b/src/app/(main)/websites/[websiteId]/settings/WebsiteReplaySettings.tsx index d62a6d2ac..39cd4e816 100644 --- a/src/app/(main)/websites/[websiteId]/settings/WebsiteReplaySettings.tsx +++ b/src/app/(main)/websites/[websiteId]/settings/WebsiteReplaySettings.tsx @@ -42,9 +42,7 @@ export function WebsiteReplaySettings({ websiteId }: { websiteId: string }) { ? `${process.env.cloudUrl}/${RECORDER_NAME}` : `${window?.location?.origin || ''}${process.env.basePath || ''}/${RECORDER_NAME}`; - let recorderAttrs = `data-website-id="${websiteId}" data-sample-rate="${sampleRate}" data-mask-level="${maskLevel}" data-max-duration="${parseInt(maxDuration, 10) || 300000}"`; - if (blockSelector) recorderAttrs += ` data-block-selector="${blockSelector}"`; - const recorderCode = ``; + const recorderCode = ``; const handleToggle = async (value: boolean) => { const previous = enabled; diff --git a/src/app/api/websites/[websiteId]/recorder/route.ts b/src/app/api/websites/[websiteId]/recorder/route.ts new file mode 100644 index 000000000..bec989c6d --- /dev/null +++ b/src/app/api/websites/[websiteId]/recorder/route.ts @@ -0,0 +1,45 @@ +import { parseRequest } from '@/lib/request'; +import { json } from '@/lib/response'; +import { getWebsite } from '@/queries/prisma'; + +interface ReplayConfig { + sampleRate?: number; + maskLevel?: string; + maxDuration?: number; + blockSelector?: string; +} + +export async function GET( + request: Request, + { params }: { params: Promise<{ websiteId: string }> }, +) { + const { error } = await parseRequest(request, null, { skipAuth: true }); + + if (error) { + return error(); + } + + const { websiteId } = await params; + const website = await getWebsite(websiteId); + + const headers = { + 'Cache-Control': 'public, max-age=60, stale-while-revalidate=300', + }; + + if (!website || !website.replayEnabled) { + return Response.json({ enabled: false }, { headers }); + } + + const config = (website.replayConfig as ReplayConfig) || {}; + + return Response.json( + { + enabled: true, + sampleRate: config.sampleRate ?? 0.15, + maskLevel: config.maskLevel ?? 'moderate', + maxDuration: config.maxDuration ?? 300000, + blockSelector: config.blockSelector ?? '', + }, + { headers }, + ); +} diff --git a/src/recorder/index.js b/src/recorder/index.js index 6265bbac4..dee818b71 100644 --- a/src/recorder/index.js +++ b/src/recorder/index.js @@ -11,23 +11,26 @@ import { record } from 'rrweb'; const website = config(`website-id`); const hostUrl = config(`host-url`); - const sampleRate = parseFloat(config(`sample-rate`) || '0.15'); - const maskLevel = config(`mask-level`) || 'moderate'; - const maxDuration = parseInt(config(`max-duration`) || '300000', 10); - const blockSelector = config(`block-selector`) || ''; if (!website) return; - // Sample rate check - if (sampleRate < 1 && Math.random() > sampleRate) return; - const host = hostUrl || '__COLLECT_API_HOST__' || currentScript.src.split('/').slice(0, -1).join('/'); - const endpoint = `${host.replace(/\/$/, '')}__COLLECT_REPLAY_ENDPOINT__`; + const hostBase = host.replace(/\/$/, ''); + const endpoint = `${hostBase}__COLLECT_REPLAY_ENDPOINT__`; + const configEndpoint = `${hostBase}__RECORDER_CONFIG_ENDPOINT__`.replace( + '{websiteId}', + website, + ); const FLUSH_EVENT_COUNT = 100; const FLUSH_INTERVAL = 10000; + let sampleRate = 0.15; + let maskLevel = 'moderate'; + let maxDuration = 300000; + let blockSelector = ''; + let eventBuffer = []; let stopFn = null; let flushTimer = null; @@ -265,11 +268,33 @@ import { record } from 'rrweb'; }); }; - if (document.readyState === 'complete') { - waitForSession(); - } else { - document.addEventListener('readystatechange', () => { - if (document.readyState === 'complete') waitForSession(); - }); - } + const bootstrap = async () => { + try { + const response = await fetch(configEndpoint, { credentials: 'omit' }); + if (!response.ok) return; + + const data = await response.json(); + if (!data?.enabled) return; + + if (typeof data.sampleRate === 'number') sampleRate = data.sampleRate; + if (typeof data.maskLevel === 'string') maskLevel = data.maskLevel; + if (typeof data.maxDuration === 'number') maxDuration = data.maxDuration; + if (typeof data.blockSelector === 'string') blockSelector = data.blockSelector; + } catch { + return; + } + + // Sample rate check + if (sampleRate < 1 && Math.random() > sampleRate) return; + + if (document.readyState === 'complete') { + waitForSession(); + } else { + document.addEventListener('readystatechange', () => { + if (document.readyState === 'complete') waitForSession(); + }); + } + }; + + bootstrap(); })(window);