umami/src/queries/sql/events/saveEvent.ts
2025-08-18 15:49:10 -07:00

248 lines
5.2 KiB
TypeScript

import { EVENT_NAME_LENGTH, URL_LENGTH, EVENT_TYPE, PAGE_TITLE_LENGTH } from '@/lib/constants';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import clickhouse from '@/lib/clickhouse';
import kafka from '@/lib/kafka';
import prisma from '@/lib/prisma';
import { uuid } from '@/lib/crypto';
import { saveEventData } from './saveEventData';
import { saveRevenue } from './saveRevenue';
export interface SaveEventArgs {
websiteId: string;
sessionId: string;
visitId: string;
eventType: number;
createdAt?: Date;
// Page
pageTitle?: string;
hostname?: string;
urlPath: string;
urlQuery?: string;
referrerPath?: string;
referrerQuery?: string;
referrerDomain?: string;
// Session
distinctId?: string;
browser?: string;
os?: string;
device?: string;
screen?: string;
language?: string;
country?: string;
region?: string;
city?: string;
// Events
eventName?: string;
eventData?: any;
tag?: string;
// UTM
utmSource?: string;
utmMedium?: string;
utmCampaign?: string;
utmContent?: string;
utmTerm?: string;
// Click IDs
gclid?: string;
fbclid?: string;
msclkid?: string;
ttclid?: string;
lifatid?: string;
twclid?: string;
}
export async function saveEvent(args: SaveEventArgs) {
return runQuery({
[PRISMA]: () => relationalQuery(args),
[CLICKHOUSE]: () => clickhouseQuery(args),
});
}
async function relationalQuery({
websiteId,
sessionId,
visitId,
createdAt,
eventType,
pageTitle,
tag,
hostname,
urlPath,
urlQuery,
referrerPath,
referrerQuery,
referrerDomain,
eventName,
eventData,
utmSource,
utmMedium,
utmCampaign,
utmContent,
utmTerm,
gclid,
fbclid,
msclkid,
ttclid,
lifatid,
twclid,
}: SaveEventArgs) {
const websiteEventId = uuid();
await prisma.client.websiteEvent.create({
data: {
id: websiteEventId,
websiteId,
sessionId,
visitId,
urlPath: urlPath?.substring(0, URL_LENGTH),
urlQuery: urlQuery?.substring(0, URL_LENGTH),
utmSource,
utmMedium,
utmCampaign,
utmContent,
utmTerm,
referrerPath: referrerPath?.substring(0, URL_LENGTH),
referrerQuery: referrerQuery?.substring(0, URL_LENGTH),
referrerDomain: referrerDomain?.substring(0, URL_LENGTH),
pageTitle: pageTitle?.substring(0, PAGE_TITLE_LENGTH),
gclid,
fbclid,
msclkid,
ttclid,
lifatid,
twclid,
eventType,
eventName: eventName ? eventName?.substring(0, EVENT_NAME_LENGTH) : null,
tag,
hostname,
createdAt,
},
});
if (eventData) {
await saveEventData({
websiteId,
sessionId,
eventId: websiteEventId,
urlPath: urlPath?.substring(0, URL_LENGTH),
eventName: eventName?.substring(0, EVENT_NAME_LENGTH),
eventData,
createdAt,
});
const { revenue, currency } = eventData;
if (revenue > 0 && currency) {
await saveRevenue({
websiteId,
sessionId,
eventId: websiteEventId,
eventName: eventName?.substring(0, EVENT_NAME_LENGTH),
currency,
revenue,
createdAt,
});
}
}
}
async function clickhouseQuery({
websiteId,
sessionId,
visitId,
distinctId,
createdAt,
pageTitle,
browser,
os,
device,
screen,
language,
country,
region,
city,
tag,
hostname,
urlPath,
urlQuery,
referrerPath,
referrerQuery,
referrerDomain,
eventName,
eventData,
utmSource,
utmMedium,
utmCampaign,
utmContent,
utmTerm,
gclid,
fbclid,
msclkid,
ttclid,
lifatid,
twclid,
}: SaveEventArgs) {
const { insert, getUTCString } = clickhouse;
const { sendMessage } = kafka;
const eventId = uuid();
const message = {
website_id: websiteId,
session_id: sessionId,
visit_id: visitId,
event_id: eventId,
country: country,
region: country && region ? (region.includes('-') ? region : `${country}-${region}`) : null,
city: city,
url_path: urlPath?.substring(0, URL_LENGTH),
url_query: urlQuery?.substring(0, URL_LENGTH),
utm_source: utmSource,
utm_medium: utmMedium,
utm_campaign: utmCampaign,
utm_content: utmContent,
utm_term: utmTerm,
referrer_path: referrerPath?.substring(0, URL_LENGTH),
referrer_query: referrerQuery?.substring(0, URL_LENGTH),
referrer_domain: referrerDomain?.substring(0, URL_LENGTH),
page_title: pageTitle?.substring(0, PAGE_TITLE_LENGTH),
gclid: gclid,
fbclid: fbclid,
msclkid: msclkid,
ttclid: ttclid,
li_fat_id: lifatid,
twclid: twclid,
event_type: eventName ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView,
event_name: eventName ? eventName?.substring(0, EVENT_NAME_LENGTH) : null,
tag: tag,
distinct_id: distinctId,
created_at: getUTCString(createdAt),
browser,
os,
device,
screen,
language,
hostname,
};
if (kafka.enabled) {
await sendMessage('event', message);
} else {
await insert('website_event', [message]);
}
if (eventData) {
await saveEventData({
websiteId,
sessionId,
eventId,
urlPath: urlPath?.substring(0, URL_LENGTH),
eventName: eventName?.substring(0, EVENT_NAME_LENGTH),
eventData,
createdAt,
});
}
}