import { z } from 'zod'; import { getSessionList, sessionService } from '@openpanel/db'; import { zChartEventFilter } from '@openpanel/validation'; import { createTRPCRouter, protectedProcedure } from '../trpc'; export function encodeCursor(cursor: { createdAt: string; id: string; }): string { const json = JSON.stringify(cursor); return Buffer.from(json, 'utf8').toString('base64url'); // URL-safe } export function decodeCursor( encoded: string, ): { createdAt: string; id: string } | null { try { const json = Buffer.from(encoded, 'base64url').toString('utf8'); const obj = JSON.parse(json); if (typeof obj.createdAt === 'string' && typeof obj.id === 'string') { return obj; } return null; } catch { return null; } } export const sessionRouter = createTRPCRouter({ list: protectedProcedure .input( z.object({ projectId: z.string(), profileId: z.string().optional(), cursor: z.string().nullish(), filters: z.array(zChartEventFilter).default([]), startDate: z.date().optional(), endDate: z.date().optional(), search: z.string().optional(), take: z.number().default(50), }), ) .query(async ({ input }) => { const cursor = input.cursor ? decodeCursor(input.cursor) : null; const data = await getSessionList({ ...input, cursor, }); return { data: data.items, meta: { next: data.meta.next ? encodeCursor(data.meta.next) : undefined, }, }; }), byId: protectedProcedure .input(z.object({ sessionId: z.string(), projectId: z.string() })) .query(async ({ input: { sessionId, projectId } }) => { return sessionService.byId(sessionId, projectId); }), });