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);