From 65f18d12ab91bb12870551a8bfbfcdaa467d5236 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 1 Mar 2025 14:40:37 -0800 Subject: [PATCH] Added timestamp property to payload. --- next-env.d.ts | 2 +- src/app/api/send/route.ts | 13 +++++++++---- src/lib/schema.ts | 2 ++ src/queries/sql/events/saveEvent.ts | 10 ++++++++-- src/queries/sql/events/saveEventData.ts | 10 ++++++---- src/queries/sql/sessions/saveSessionData.ts | 11 +++++++---- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 40c3d680..1b3be084 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/src/app/api/send/route.ts b/src/app/api/send/route.ts index 80db8f96..cc3b03cb 100644 --- a/src/app/api/send/route.ts +++ b/src/app/api/send/route.ts @@ -7,16 +7,16 @@ import { badRequest, json, forbidden, serverError } from '@/lib/response'; import { fetchSession, fetchWebsite } from '@/lib/load'; import { getClientInfo, hasBlockedIp } from '@/lib/detect'; import { secret, uuid, visitSalt } from '@/lib/crypto'; -import { COLLECTION_TYPE, DOMAIN_REGEX } from '@/lib/constants'; +import { COLLECTION_TYPE } from '@/lib/constants'; +import { anyObjectParam, urlOrPathParam } from '@/lib/schema'; import { createSession, saveEvent, saveSessionData } from '@/queries'; -import { urlOrPathParam } from '@/lib/schema'; const schema = z.object({ type: z.enum(['event', 'identify']), payload: z.object({ website: z.string().uuid(), - data: z.object({}).passthrough().optional(), - hostname: z.string().regex(DOMAIN_REGEX).max(100).optional(), + data: anyObjectParam.optional(), + hostname: z.string().max(100).optional(), language: z.string().max(35).optional(), referrer: urlOrPathParam.optional(), screen: z.string().max(11).optional(), @@ -26,6 +26,7 @@ const schema = z.object({ tag: z.string().max(50).optional(), ip: z.string().ip().optional(), userAgent: z.string().optional(), + timestamp: z.coerce.number().int().optional(), }), }); @@ -55,6 +56,7 @@ export async function POST(request: Request) { data, title, tag, + timestamp, } = payload; // Cache check @@ -88,6 +90,7 @@ export async function POST(request: Request) { } const sessionId = uuid(websiteId, ip, userAgent); + const createdAt = timestamp ? new Date(timestamp * 1000) : new Date(); // Find session if (!clickhouse.enabled && !cache?.sessionId) { @@ -179,6 +182,7 @@ export async function POST(request: Request) { subdivision2, city, tag, + createdAt, }); } @@ -191,6 +195,7 @@ export async function POST(request: Request) { websiteId, sessionId, sessionData: data, + createdAt, }); } diff --git a/src/lib/schema.ts b/src/lib/schema.ts index 8df7be9f..4e2b3e4a 100644 --- a/src/lib/schema.ts +++ b/src/lib/schema.ts @@ -36,6 +36,8 @@ export const unitParam = z.string().refine(value => UNIT_TYPES.includes(value), export const roleParam = z.enum(['team-member', 'team-view-only', 'team-manager']); +export const anyObjectParam = z.object({}).passthrough(); + export const urlOrPathParam = z.string().refine( value => { try { diff --git a/src/queries/sql/events/saveEvent.ts b/src/queries/sql/events/saveEvent.ts index 65ee1175..148b03f3 100644 --- a/src/queries/sql/events/saveEvent.ts +++ b/src/queries/sql/events/saveEvent.ts @@ -29,6 +29,7 @@ export async function saveEvent(args: { subdivision2?: string; city?: string; tag?: string; + createdAt?: Date; }) { return runQuery({ [PRISMA]: () => relationalQuery(args), @@ -49,6 +50,7 @@ async function relationalQuery(data: { eventName?: string; eventData?: any; tag?: string; + createdAt?: Date; }) { const { websiteId, @@ -63,6 +65,7 @@ async function relationalQuery(data: { eventData, pageTitle, tag, + createdAt, } = data; const websiteEventId = uuid(); @@ -81,6 +84,7 @@ async function relationalQuery(data: { eventType: eventName ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, eventName: eventName ? eventName?.substring(0, EVENT_NAME_LENGTH) : null, tag, + createdAt, }, }); @@ -92,6 +96,7 @@ async function relationalQuery(data: { urlPath: urlPath?.substring(0, URL_LENGTH), eventName: eventName?.substring(0, EVENT_NAME_LENGTH), eventData, + createdAt, }); } @@ -121,6 +126,7 @@ async function clickhouseQuery(data: { subdivision2?: string; city?: string; tag?: string; + createdAt?: Date; }) { const { websiteId, @@ -139,12 +145,12 @@ async function clickhouseQuery(data: { subdivision2, city, tag, + createdAt, ...args } = data; const { insert, getUTCString } = clickhouse; const { sendMessage } = kafka; const eventId = uuid(); - const createdAt = getUTCString(); const message = { ...args, @@ -170,7 +176,7 @@ async function clickhouseQuery(data: { event_type: eventName ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, event_name: eventName ? eventName?.substring(0, EVENT_NAME_LENGTH) : null, tag: tag, - created_at: createdAt, + created_at: getUTCString(createdAt), }; if (kafka.enabled) { diff --git a/src/queries/sql/events/saveEventData.ts b/src/queries/sql/events/saveEventData.ts index 7c158da4..16a5cab1 100644 --- a/src/queries/sql/events/saveEventData.ts +++ b/src/queries/sql/events/saveEventData.ts @@ -15,7 +15,7 @@ export async function saveEventData(data: { urlPath?: string; eventName?: string; eventData: DynamicData; - createdAt?: string; + createdAt?: Date; }) { return runQuery({ [PRISMA]: () => relationalQuery(data), @@ -27,8 +27,9 @@ async function relationalQuery(data: { websiteId: string; eventId: string; eventData: DynamicData; + createdAt?: Date; }): Promise { - const { websiteId, eventId, eventData } = data; + const { websiteId, eventId, eventData, createdAt } = data; const jsonKeys = flattenJSON(eventData); @@ -42,6 +43,7 @@ async function relationalQuery(data: { numberValue: a.dataType === DATA_TYPE.number ? a.value : null, dateValue: a.dataType === DATA_TYPE.date ? new Date(a.value) : null, dataType: a.dataType, + createdAt, })); return prisma.client.eventData.createMany({ @@ -56,7 +58,7 @@ async function clickhouseQuery(data: { urlPath?: string; eventName?: string; eventData: DynamicData; - createdAt?: string; + createdAt?: Date; }) { const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data; @@ -77,7 +79,7 @@ async function clickhouseQuery(data: { string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, date_value: dataType === DATA_TYPE.date ? getUTCString(value) : null, - created_at: createdAt, + created_at: getUTCString(createdAt), }; }); diff --git a/src/queries/sql/sessions/saveSessionData.ts b/src/queries/sql/sessions/saveSessionData.ts index 35f0c712..a060e9a8 100644 --- a/src/queries/sql/sessions/saveSessionData.ts +++ b/src/queries/sql/sessions/saveSessionData.ts @@ -11,6 +11,7 @@ export async function saveSessionData(data: { websiteId: string; sessionId: string; sessionData: DynamicData; + createdAt?: Date; }) { return runQuery({ [PRISMA]: () => relationalQuery(data), @@ -22,9 +23,10 @@ export async function relationalQuery(data: { websiteId: string; sessionId: string; sessionData: DynamicData; + createdAt?: Date; }) { const { client } = prisma; - const { websiteId, sessionId, sessionData } = data; + const { websiteId, sessionId, sessionData, createdAt } = data; const jsonKeys = flattenJSON(sessionData); @@ -37,6 +39,7 @@ export async function relationalQuery(data: { numberValue: a.dataType === DATA_TYPE.number ? a.value : null, dateValue: a.dataType === DATA_TYPE.date ? new Date(a.value) : null, dataType: a.dataType, + createdAt, })); const existing = await client.sessionData.findMany({ @@ -77,12 +80,12 @@ async function clickhouseQuery(data: { websiteId: string; sessionId: string; sessionData: DynamicData; + createdAt?: Date; }) { - const { websiteId, sessionId, sessionData } = data; + const { websiteId, sessionId, sessionData, createdAt } = data; const { insert, getUTCString } = clickhouse; const { sendMessage } = kafka; - const createdAt = getUTCString(); const jsonKeys = flattenJSON(sessionData); @@ -95,7 +98,7 @@ async function clickhouseQuery(data: { string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, date_value: dataType === DATA_TYPE.date ? getUTCString(value) : null, - created_at: createdAt, + created_at: getUTCString(createdAt), }; });