session replay updates. add clickhouse implementation, filters, tables

This commit is contained in:
Francis Cao committed 2026-03-03 12:47:27 -08:00
1 parent e9ac09ec5d
commit 5e68661c5d
23 files changed
+359 -150

No files matched your search

+3 -8
View File
@@ -1,4 +1,3 @@
import { gzipSync } from 'node:zlib';
import { isbot } from 'isbot';
import { serializeError } from 'serialize-error';
import { z } from 'zod';
@@ -8,7 +7,7 @@ import { parseToken } from '@/lib/jwt';
import { parseRequest } from '@/lib/request';
import { badRequest, forbidden, json, serverError } from '@/lib/response';
import { getWebsite } from '@/queries/prisma';
import { saveReplayChunk } from '@/queries/sql';
import { saveRecording } from '@/queries/sql';
const schema = z.object({
website: z.uuid(),
@@ -75,18 +74,14 @@ export async function POST(request: Request) {
const startedAt = new Date(Math.min(...eventTimestamps));
const endedAt = new Date(Math.max(...eventTimestamps));
// Compress events
const eventsJson = JSON.stringify(events);
const compressed = gzipSync(Buffer.from(eventsJson, 'utf-8'));
// Use timestamp-based chunk index for ordering
const chunkIndex = timestamp || Math.floor(Date.now() / 1000);
await saveReplayChunk({
await saveRecording({
websiteId,
sessionId,
chunkIndex,
events: compressed,
events,
eventCount: events.length,
startedAt,
endedAt,
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { getQueryFilters, parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { filterParams, pagingParams, searchParams } from '@/lib/schema';
import { filterParams, pagingParams, searchParams, withDateRange } from '@/lib/schema';
import { canViewWebsite } from '@/permissions';
import { getWebsiteEvents } from '@/queries/sql';
@@ -9,9 +8,7 @@ export async function GET(
request: Request,
{ params }: { params: Promise<{ websiteId: string }> },
) {
const schema = z.object({
startAt: z.coerce.number().optional(),
endAt: z.coerce.number().optional(),
const schema = withDateRange({
...filterParams,
...pagingParams,
...searchParams,
@@ -1,4 +1,3 @@
import { gunzipSync } from 'node:zlib';
import { parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { canViewWebsite } from '@/permissions';
@@ -22,12 +21,7 @@ export async function GET(
const chunks = await getReplayChunks(websiteId, sessionId);
// Decompress and concatenate all chunks
const allEvents = chunks.flatMap(chunk => {
const decompressed = gunzipSync(Buffer.from(chunk.events));
return JSON.parse(decompressed.toString('utf-8'));
});
const allEvents = chunks.flatMap(chunk => chunk.events);
const startedAt = chunks.length > 0 ? chunks[0].startedAt : null;
const endedAt = chunks.length > 0 ? chunks[chunks.length - 1].endedAt : null;
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { getQueryFilters, parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { dateRangeParams, pagingParams, searchParams } from '@/lib/schema';
import { filterParams, pagingParams, searchParams, withDateRange } from '@/lib/schema';
import { canViewWebsite } from '@/permissions';
import { getSessionReplays } from '@/queries/sql';
@@ -9,8 +8,8 @@ export async function GET(
request: Request,
{ params }: { params: Promise<{ websiteId: string }> },
) {
const schema = z.object({
...dateRangeParams,
const schema = withDateRange({
...filterParams,
...pagingParams,
...searchParams,
});