mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 20:57:17 +01:00
Some checks failed
Node.js CI / build (postgresql, 18.18, 10) (push) Has been cancelled
# Conflicts: # pnpm-lock.yaml
191 lines
5.3 KiB
TypeScript
191 lines
5.3 KiB
TypeScript
import { getRandomReferrer } from '../distributions/referrers.js';
|
|
import { addSeconds, randomInt, uuid } from '../utils.js';
|
|
import type { SessionData } from './sessions.js';
|
|
|
|
export const EVENT_TYPE = {
|
|
pageView: 1,
|
|
customEvent: 2,
|
|
} as const;
|
|
|
|
export interface PageConfig {
|
|
path: string;
|
|
title: string;
|
|
weight: number;
|
|
avgTimeOnPage: number;
|
|
}
|
|
|
|
export interface CustomEventConfig {
|
|
name: string;
|
|
weight: number;
|
|
pages?: string[];
|
|
data?: Record<string, string[] | number[]>;
|
|
}
|
|
|
|
export interface JourneyConfig {
|
|
pages: string[];
|
|
weight: number;
|
|
}
|
|
|
|
export interface EventData {
|
|
id: string;
|
|
websiteId: string;
|
|
sessionId: string;
|
|
visitId: string;
|
|
eventType: number;
|
|
urlPath: string;
|
|
urlQuery: string | null;
|
|
pageTitle: string | null;
|
|
hostname: string;
|
|
referrerDomain: string | null;
|
|
referrerPath: string | null;
|
|
utmSource: string | null;
|
|
utmMedium: string | null;
|
|
utmCampaign: string | null;
|
|
utmContent: string | null;
|
|
utmTerm: string | null;
|
|
gclid: string | null;
|
|
fbclid: string | null;
|
|
eventName: string | null;
|
|
tag: string | null;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface EventDataEntry {
|
|
id: string;
|
|
websiteId: string;
|
|
websiteEventId: string;
|
|
dataKey: string;
|
|
stringValue: string | null;
|
|
numberValue: number | null;
|
|
dateValue: Date | null;
|
|
dataType: number;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface SiteConfig {
|
|
hostname: string;
|
|
pages: PageConfig[];
|
|
journeys: JourneyConfig[];
|
|
customEvents: CustomEventConfig[];
|
|
}
|
|
|
|
function getPageTitle(pages: PageConfig[], path: string): string | null {
|
|
const page = pages.find(p => p.path === path);
|
|
return page?.title ?? null;
|
|
}
|
|
|
|
function getPageTimeOnPage(pages: PageConfig[], path: string): number {
|
|
const page = pages.find(p => p.path === path);
|
|
return page?.avgTimeOnPage ?? 30;
|
|
}
|
|
|
|
export function generateEventsForSession(
|
|
session: SessionData,
|
|
siteConfig: SiteConfig,
|
|
journey: string[],
|
|
): { events: EventData[]; eventDataEntries: EventDataEntry[] } {
|
|
const events: EventData[] = [];
|
|
const eventDataEntries: EventDataEntry[] = [];
|
|
const visitId = uuid();
|
|
|
|
let currentTime = session.createdAt;
|
|
const referrer = getRandomReferrer();
|
|
|
|
for (let i = 0; i < journey.length; i++) {
|
|
const pagePath = journey[i];
|
|
const isFirstPage = i === 0;
|
|
|
|
const eventId = uuid();
|
|
const pageTitle = getPageTitle(siteConfig.pages, pagePath);
|
|
|
|
events.push({
|
|
id: eventId,
|
|
websiteId: session.websiteId,
|
|
sessionId: session.id,
|
|
visitId,
|
|
eventType: EVENT_TYPE.pageView,
|
|
urlPath: pagePath,
|
|
urlQuery: null,
|
|
pageTitle,
|
|
hostname: siteConfig.hostname,
|
|
referrerDomain: isFirstPage ? referrer.domain : null,
|
|
referrerPath: isFirstPage ? referrer.path : null,
|
|
utmSource: isFirstPage ? referrer.utmSource : null,
|
|
utmMedium: isFirstPage ? referrer.utmMedium : null,
|
|
utmCampaign: isFirstPage ? referrer.utmCampaign : null,
|
|
utmContent: isFirstPage ? referrer.utmContent : null,
|
|
utmTerm: isFirstPage ? referrer.utmTerm : null,
|
|
gclid: isFirstPage ? referrer.gclid : null,
|
|
fbclid: isFirstPage ? referrer.fbclid : null,
|
|
eventName: null,
|
|
tag: null,
|
|
createdAt: currentTime,
|
|
});
|
|
|
|
// Check for custom events on this page
|
|
for (const customEvent of siteConfig.customEvents) {
|
|
// Check if this event can occur on this page
|
|
if (customEvent.pages && !customEvent.pages.includes(pagePath)) {
|
|
continue;
|
|
}
|
|
|
|
// Random chance based on weight
|
|
if (Math.random() < customEvent.weight) {
|
|
currentTime = addSeconds(currentTime, randomInt(2, 15));
|
|
|
|
const customEventId = uuid();
|
|
events.push({
|
|
id: customEventId,
|
|
websiteId: session.websiteId,
|
|
sessionId: session.id,
|
|
visitId,
|
|
eventType: EVENT_TYPE.customEvent,
|
|
urlPath: pagePath,
|
|
urlQuery: null,
|
|
pageTitle,
|
|
hostname: siteConfig.hostname,
|
|
referrerDomain: null,
|
|
referrerPath: null,
|
|
utmSource: null,
|
|
utmMedium: null,
|
|
utmCampaign: null,
|
|
utmContent: null,
|
|
utmTerm: null,
|
|
gclid: null,
|
|
fbclid: null,
|
|
eventName: customEvent.name,
|
|
tag: null,
|
|
createdAt: currentTime,
|
|
});
|
|
|
|
// Generate event data if configured
|
|
if (customEvent.data) {
|
|
for (const [key, values] of Object.entries(customEvent.data)) {
|
|
const value = values[Math.floor(Math.random() * values.length)];
|
|
const isNumber = typeof value === 'number';
|
|
|
|
eventDataEntries.push({
|
|
id: uuid(),
|
|
websiteId: session.websiteId,
|
|
websiteEventId: customEventId,
|
|
dataKey: key,
|
|
stringValue: isNumber ? null : String(value),
|
|
numberValue: isNumber ? value : null,
|
|
dateValue: null,
|
|
dataType: isNumber ? 2 : 1, // 1 = string, 2 = number
|
|
createdAt: currentTime,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Time spent on page before navigating
|
|
const timeOnPage = getPageTimeOnPage(siteConfig.pages, pagePath);
|
|
const variance = Math.floor(timeOnPage * 0.5);
|
|
const actualTime = timeOnPage + randomInt(-variance, variance);
|
|
currentTime = addSeconds(currentTime, Math.max(5, actualTime));
|
|
}
|
|
|
|
return { events, eventDataEntries };
|
|
}
|