Fetch recorder settings from website config endpoint

This commit is contained in:
Mike Cao
2026-05-14 03:26:24 -07:00
parent c724f42daf
commit c6733897b4
4 changed files with 88 additions and 18 deletions
+2
View File
@@ -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,
}),
@@ -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 = `<script defer src="${recorderUrl}" ${recorderAttrs}></script>`;
const recorderCode = `<script defer src="${recorderUrl}" data-website-id="${websiteId}"></script>`;
const handleToggle = async (value: boolean) => {
const previous = enabled;
@@ -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 },
);
}
+40 -15
View File
@@ -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);