mirror of
https://github.com/umami-software/umami.git
synced 2026-02-14 01:25:37 +01:00
commit
6ced493c22
15 changed files with 202 additions and 282 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -12,6 +12,7 @@ node_modules
|
||||||
/.next/
|
/.next/
|
||||||
/out/
|
/out/
|
||||||
/prisma/
|
/prisma/
|
||||||
|
/src/generated/
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ RUN set -x \
|
||||||
&& apk add --no-cache curl
|
&& apk add --no-cache curl
|
||||||
|
|
||||||
# Script dependencies
|
# Script dependencies
|
||||||
RUN pnpm add npm-run-all dotenv prisma@6.1.0
|
RUN pnpm add npm-run-all dotenv prisma@6.7.0
|
||||||
|
|
||||||
# Permissions for prisma
|
# Permissions for prisma
|
||||||
RUN chown -R nextjs:nodejs node_modules/.pnpm/
|
RUN chown -R nextjs:nodejs node_modules/.pnpm/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.18.0",
|
"version": "2.18.1",
|
||||||
"description": "A modern, privacy-focused alternative to Google Analytics.",
|
"description": "A modern, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -162,7 +162,6 @@
|
||||||
"postcss-flexbugs-fixes": "^5.0.2",
|
"postcss-flexbugs-fixes": "^5.0.2",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-preset-env": "7.8.3",
|
"postcss-preset-env": "7.8.3",
|
||||||
"postcss-rtlcss": "^4.0.1",
|
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"prompts": "2.4.2",
|
"prompts": "2.4.2",
|
||||||
"rollup": "^3.28.0",
|
"rollup": "^3.28.0",
|
||||||
|
|
|
||||||
28
pnpm-lock.yaml
generated
28
pnpm-lock.yaml
generated
|
|
@ -291,9 +291,6 @@ importers:
|
||||||
postcss-preset-env:
|
postcss-preset-env:
|
||||||
specifier: 7.8.3
|
specifier: 7.8.3
|
||||||
version: 7.8.3(postcss@8.5.3)
|
version: 7.8.3(postcss@8.5.3)
|
||||||
postcss-rtlcss:
|
|
||||||
specifier: ^4.0.1
|
|
||||||
version: 4.0.9(postcss@8.5.3)
|
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^2.6.2
|
specifier: ^2.6.2
|
||||||
version: 2.8.8
|
version: 2.8.8
|
||||||
|
|
@ -346,8 +343,6 @@ importers:
|
||||||
specifier: ^5.5.3
|
specifier: ^5.5.3
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
|
|
||||||
src/generated/prisma: {}
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@ampproject/remapping@2.3.0':
|
'@ampproject/remapping@2.3.0':
|
||||||
|
|
@ -5513,12 +5508,6 @@ packages:
|
||||||
postcss-resolve-nested-selector@0.1.6:
|
postcss-resolve-nested-selector@0.1.6:
|
||||||
resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==}
|
resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==}
|
||||||
|
|
||||||
postcss-rtlcss@4.0.9:
|
|
||||||
resolution: {integrity: sha512-dCNKEf+FgTv+EA3XI8ysg2RnpS5s3/iZmU+9qpCNFxHU/BhK+4hz7jyCsCAfo0CLnDrMPtaQENhwb+EGm1wh7Q==}
|
|
||||||
engines: {node: '>=18.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.4.21
|
|
||||||
|
|
||||||
postcss-safe-parser@6.0.0:
|
postcss-safe-parser@6.0.0:
|
||||||
resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
|
resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
|
||||||
engines: {node: '>=12.0'}
|
engines: {node: '>=12.0'}
|
||||||
|
|
@ -5898,11 +5887,6 @@ packages:
|
||||||
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
rtlcss@4.1.1:
|
|
||||||
resolution: {integrity: sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
|
|
||||||
|
|
@ -12588,11 +12572,6 @@ snapshots:
|
||||||
|
|
||||||
postcss-resolve-nested-selector@0.1.6: {}
|
postcss-resolve-nested-selector@0.1.6: {}
|
||||||
|
|
||||||
postcss-rtlcss@4.0.9(postcss@8.5.3):
|
|
||||||
dependencies:
|
|
||||||
postcss: 8.5.3
|
|
||||||
rtlcss: 4.1.1
|
|
||||||
|
|
||||||
postcss-safe-parser@6.0.0(postcss@8.5.3):
|
postcss-safe-parser@6.0.0(postcss@8.5.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.3
|
||||||
|
|
@ -13008,13 +12987,6 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
rtlcss@4.1.1:
|
|
||||||
dependencies:
|
|
||||||
escalade: 3.2.0
|
|
||||||
picocolors: 1.1.1
|
|
||||||
postcss: 8.5.3
|
|
||||||
strip-json-comments: 3.1.1
|
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
-- postgreSQL
|
-- PostgreSQL
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
UPDATE "website_event" we
|
UPDATE "website_event" we
|
||||||
SET fbclid = url.fbclid,
|
SET fbclid = LEFT(url.fbclid, 255),
|
||||||
gclid = url.gclid,
|
gclid = LEFT(url.gclid, 255),
|
||||||
li_fat_id = url.li_fat_id,
|
li_fat_id = LEFT(url.li_fat_id, 255),
|
||||||
msclkid = url.msclkid,
|
msclkid = LEFT(url.msclkid, 255),
|
||||||
ttclid = url.ttclid,
|
ttclid = LEFT(url.ttclid, 255),
|
||||||
twclid = url.twclid,
|
twclid = LEFT(url.twclid, 255),
|
||||||
utm_campaign = url.utm_campaign,
|
utm_campaign = LEFT(url.utm_campaign, 255),
|
||||||
utm_content = url.utm_content,
|
utm_content = LEFT(url.utm_content, 255),
|
||||||
utm_medium = url.utm_medium,
|
utm_medium = LEFT(url.utm_medium, 255),
|
||||||
utm_source = url.utm_source,
|
utm_source = LEFT(url.utm_source, 255),
|
||||||
utm_term = url.utm_term
|
utm_term = LEFT(url.utm_term, 255)
|
||||||
FROM (SELECT event_id, website_id, session_id,
|
FROM (SELECT event_id, website_id, session_id,
|
||||||
(regexp_matches(url_query, '(?:[&?]|^)fbclid=([^&]+)', 'i'))[1] AS fbclid,
|
(regexp_matches(url_query, '(?:[&?]|^)fbclid=([^&]+)', 'i'))[1] AS fbclid,
|
||||||
(regexp_matches(url_query, '(?:[&?]|^)gclid=([^&]+)', 'i'))[1] AS gclid,
|
(regexp_matches(url_query, '(?:[&?]|^)gclid=([^&]+)', 'i'))[1] AS gclid,
|
||||||
|
|
@ -31,18 +31,18 @@ WHERE we.event_id = url.event_id
|
||||||
and we.website_id = url.website_id;
|
and we.website_id = url.website_id;
|
||||||
|
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
-- mySQL
|
-- MySQL
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
UPDATE `website_event`
|
UPDATE `website_event`
|
||||||
SET fbclid = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)fbclid=[^&]+'), '=', -1), '&', 1),
|
SET fbclid = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)fbclid=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
gclid = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)gclid=[^&]+'), '=', -1), '&', 1),
|
gclid = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)gclid=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
li_fat_id = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)li_fat_id=[^&]+'), '=', -1), '&', 1),
|
li_fat_id = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)li_fat_id=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
msclkid = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)msclkid=[^&]+'), '=', -1), '&', 1),
|
msclkid = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)msclkid=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
ttclid = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)ttclid=[^&]+'), '=', -1), '&', 1),
|
ttclid = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)ttclid=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
twclid = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)twclid=[^&]+'), '=', -1), '&', 1),
|
twclid = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)twclid=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
utm_campaign = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_campaign=[^&]+'), '=', -1), '&', 1),
|
utm_campaign = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_campaign=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
utm_content = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_content=[^&]+'), '=', -1), '&', 1),
|
utm_content = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_content=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
utm_medium = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_medium=[^&]+'), '=', -1), '&', 1),
|
utm_medium = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_medium=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
utm_source = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_source=[^&]+'), '=', -1), '&', 1),
|
utm_source = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_source=[^&]+'), '=', -1), '&', 1), 255),
|
||||||
utm_term = SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_term=[^&]+'), '=', -1), '&', 1)
|
utm_term = LEFT(SUBSTRING_INDEX(SUBSTRING_INDEX(REGEXP_SUBSTR(url_query, '(?:[&?]|^)utm_term=[^&]+'), '=', -1), '&', 1), 255)
|
||||||
WHERE 1 = 1;
|
WHERE 1 = 1;
|
||||||
|
|
@ -7,8 +7,10 @@ const routesManifestPath = path.resolve(__dirname, '../.next/routes-manifest.jso
|
||||||
const originalPath = path.resolve(__dirname, '../.next/routes-manifest-orig.json');
|
const originalPath = path.resolve(__dirname, '../.next/routes-manifest-orig.json');
|
||||||
const originalManifest = require(originalPath);
|
const originalManifest = require(originalPath);
|
||||||
|
|
||||||
const API_PATH = '/api/:path*';
|
const basePath = originalManifest.basePath;
|
||||||
const TRACKER_SCRIPT = '/script.js';
|
|
||||||
|
const API_PATH = basePath + '/api/:path*';
|
||||||
|
const TRACKER_SCRIPT = basePath + '/script.js';
|
||||||
|
|
||||||
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
||||||
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
||||||
|
|
@ -20,14 +22,16 @@ if (collectApiEndpoint) {
|
||||||
const apiRoute = originalManifest.headers.find(route => route.source === API_PATH);
|
const apiRoute = originalManifest.headers.find(route => route.source === API_PATH);
|
||||||
const routeRegex = new RegExp(apiRoute.regex);
|
const routeRegex = new RegExp(apiRoute.regex);
|
||||||
|
|
||||||
|
const normalizedSource = basePath + collectApiEndpoint;
|
||||||
|
|
||||||
rewrites.push({
|
rewrites.push({
|
||||||
source: collectApiEndpoint,
|
source: normalizedSource,
|
||||||
destination: '/api/send',
|
destination: basePath + '/api/send',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!routeRegex.test(collectApiEndpoint)) {
|
if (!routeRegex.test(normalizedSource)) {
|
||||||
headers.push({
|
headers.push({
|
||||||
source: collectApiEndpoint,
|
source: normalizedSource,
|
||||||
headers: apiRoute.headers,
|
headers: apiRoute.headers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +44,7 @@ if (trackerScriptName) {
|
||||||
|
|
||||||
if (names) {
|
if (names) {
|
||||||
names.forEach(name => {
|
names.forEach(name => {
|
||||||
const normalizedSource = `/${name.replace(/^\/+/, '')}`;
|
const normalizedSource = `${basePath}/${name.replace(/^\/+/, '')}`;
|
||||||
|
|
||||||
rewrites.push({
|
rewrites.push({
|
||||||
source: normalizedSource,
|
source: normalizedSource,
|
||||||
|
|
|
||||||
|
|
@ -187,26 +187,19 @@ export async function POST(request: Request) {
|
||||||
websiteId,
|
websiteId,
|
||||||
sessionId,
|
sessionId,
|
||||||
visitId,
|
visitId,
|
||||||
|
createdAt,
|
||||||
|
|
||||||
|
// Page
|
||||||
|
pageTitle: safeDecodeURIComponent(title),
|
||||||
|
hostname: hostname || urlDomain,
|
||||||
urlPath: safeDecodeURI(urlPath),
|
urlPath: safeDecodeURI(urlPath),
|
||||||
urlQuery,
|
urlQuery,
|
||||||
utmSource,
|
|
||||||
utmMedium,
|
|
||||||
utmCampaign,
|
|
||||||
utmContent,
|
|
||||||
utmTerm,
|
|
||||||
referrerPath: safeDecodeURI(referrerPath),
|
referrerPath: safeDecodeURI(referrerPath),
|
||||||
referrerQuery,
|
referrerQuery,
|
||||||
referrerDomain,
|
referrerDomain,
|
||||||
pageTitle: safeDecodeURIComponent(title),
|
|
||||||
gclid,
|
// Session
|
||||||
fbclid,
|
distinctId: id,
|
||||||
msclkid,
|
|
||||||
ttclid,
|
|
||||||
lifatid,
|
|
||||||
twclid,
|
|
||||||
eventName: name,
|
|
||||||
eventData: data,
|
|
||||||
hostname: hostname || urlDomain,
|
|
||||||
browser,
|
browser,
|
||||||
os,
|
os,
|
||||||
device,
|
device,
|
||||||
|
|
@ -215,24 +208,39 @@ export async function POST(request: Request) {
|
||||||
country,
|
country,
|
||||||
region,
|
region,
|
||||||
city,
|
city,
|
||||||
|
|
||||||
|
// Events
|
||||||
|
eventName: name,
|
||||||
|
eventData: data,
|
||||||
tag,
|
tag,
|
||||||
distinctId: id,
|
|
||||||
createdAt,
|
// UTM
|
||||||
|
utmSource,
|
||||||
|
utmMedium,
|
||||||
|
utmCampaign,
|
||||||
|
utmContent,
|
||||||
|
utmTerm,
|
||||||
|
|
||||||
|
// Click IDs
|
||||||
|
gclid,
|
||||||
|
fbclid,
|
||||||
|
msclkid,
|
||||||
|
ttclid,
|
||||||
|
lifatid,
|
||||||
|
twclid,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === COLLECTION_TYPE.identify) {
|
if (type === COLLECTION_TYPE.identify) {
|
||||||
if (!data) {
|
if (data) {
|
||||||
return badRequest('Data required.');
|
await saveSessionData({
|
||||||
|
websiteId,
|
||||||
|
sessionId,
|
||||||
|
sessionData: data,
|
||||||
|
distinctId: id,
|
||||||
|
createdAt,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveSessionData({
|
|
||||||
websiteId,
|
|
||||||
sessionId,
|
|
||||||
sessionData: data,
|
|
||||||
distinctId: id,
|
|
||||||
createdAt,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = createToken({ websiteId, sessionId, visitId, iat }, secret());
|
const token = createToken({ websiteId, sessionId, visitId, iat }, secret());
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function renderDateLabels(unit: string, locale: string) {
|
||||||
|
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case 'minute':
|
case 'minute':
|
||||||
return formatDate(d, 'p', locale).split(' ')[0];
|
return formatDate(d, 'h:mm', locale);
|
||||||
case 'hour':
|
case 'hour':
|
||||||
return formatDate(d, 'p', locale);
|
return formatDate(d, 'p', locale);
|
||||||
case 'day':
|
case 'day':
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,23 @@ import prisma from '@/lib/prisma';
|
||||||
import { uuid } from '@/lib/crypto';
|
import { uuid } from '@/lib/crypto';
|
||||||
import { saveEventData } from './saveEventData';
|
import { saveEventData } from './saveEventData';
|
||||||
|
|
||||||
export async function saveEvent(args: {
|
export interface SaveEventArgs {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
visitId: string;
|
visitId: string;
|
||||||
|
createdAt?: Date;
|
||||||
|
|
||||||
|
// Page
|
||||||
|
pageTitle?: string;
|
||||||
|
hostname?: string;
|
||||||
urlPath: string;
|
urlPath: string;
|
||||||
urlQuery?: string;
|
urlQuery?: string;
|
||||||
utmSource?: string;
|
|
||||||
utmMedium?: string;
|
|
||||||
utmCampaign?: string;
|
|
||||||
utmContent?: string;
|
|
||||||
utmTerm?: string;
|
|
||||||
referrerPath?: string;
|
referrerPath?: string;
|
||||||
referrerQuery?: string;
|
referrerQuery?: string;
|
||||||
referrerDomain?: string;
|
referrerDomain?: string;
|
||||||
pageTitle?: string;
|
|
||||||
gclid?: string;
|
// Session
|
||||||
fbclid?: string;
|
distinctId?: string;
|
||||||
msclkid?: string;
|
|
||||||
ttclid?: string;
|
|
||||||
lifatid?: string;
|
|
||||||
twclid?: string;
|
|
||||||
eventName?: string;
|
|
||||||
eventData?: any;
|
|
||||||
hostname?: string;
|
|
||||||
browser?: string;
|
browser?: string;
|
||||||
os?: string;
|
os?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
|
|
@ -38,73 +31,65 @@ export async function saveEvent(args: {
|
||||||
country?: string;
|
country?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
city?: string;
|
city?: string;
|
||||||
tag?: string;
|
|
||||||
distinctId?: string;
|
|
||||||
createdAt?: Date;
|
|
||||||
}) {
|
|
||||||
return runQuery({
|
|
||||||
[PRISMA]: () => relationalQuery(args),
|
|
||||||
[CLICKHOUSE]: () => clickhouseQuery(args),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function relationalQuery(data: {
|
// Events
|
||||||
websiteId: string;
|
eventName?: string;
|
||||||
sessionId: string;
|
eventData?: any;
|
||||||
visitId: string;
|
tag?: string;
|
||||||
urlPath: string;
|
|
||||||
urlQuery?: string;
|
// UTM
|
||||||
utmSource?: string;
|
utmSource?: string;
|
||||||
utmMedium?: string;
|
utmMedium?: string;
|
||||||
utmCampaign?: string;
|
utmCampaign?: string;
|
||||||
utmContent?: string;
|
utmContent?: string;
|
||||||
utmTerm?: string;
|
utmTerm?: string;
|
||||||
referrerPath?: string;
|
|
||||||
referrerQuery?: string;
|
// Click IDs
|
||||||
referrerDomain?: string;
|
|
||||||
gclid?: string;
|
gclid?: string;
|
||||||
fbclid?: string;
|
fbclid?: string;
|
||||||
msclkid?: string;
|
msclkid?: string;
|
||||||
ttclid?: string;
|
ttclid?: string;
|
||||||
lifatid?: string;
|
lifatid?: string;
|
||||||
twclid?: string;
|
twclid?: string;
|
||||||
pageTitle?: string;
|
}
|
||||||
eventName?: string;
|
|
||||||
eventData?: any;
|
export async function saveEvent(args: SaveEventArgs) {
|
||||||
tag?: string;
|
return runQuery({
|
||||||
hostname?: string;
|
[PRISMA]: () => relationalQuery(args),
|
||||||
createdAt?: Date;
|
[CLICKHOUSE]: () => clickhouseQuery(args),
|
||||||
}) {
|
});
|
||||||
const {
|
}
|
||||||
websiteId,
|
|
||||||
sessionId,
|
async function relationalQuery({
|
||||||
visitId,
|
websiteId,
|
||||||
urlPath,
|
sessionId,
|
||||||
urlQuery,
|
visitId,
|
||||||
utmSource,
|
createdAt,
|
||||||
utmMedium,
|
pageTitle,
|
||||||
utmCampaign,
|
tag,
|
||||||
utmContent,
|
hostname,
|
||||||
utmTerm,
|
urlPath,
|
||||||
referrerPath,
|
urlQuery,
|
||||||
referrerQuery,
|
referrerPath,
|
||||||
referrerDomain,
|
referrerQuery,
|
||||||
eventName,
|
referrerDomain,
|
||||||
eventData,
|
eventName,
|
||||||
pageTitle,
|
eventData,
|
||||||
gclid,
|
utmSource,
|
||||||
fbclid,
|
utmMedium,
|
||||||
msclkid,
|
utmCampaign,
|
||||||
ttclid,
|
utmContent,
|
||||||
lifatid,
|
utmTerm,
|
||||||
twclid,
|
gclid,
|
||||||
tag,
|
fbclid,
|
||||||
hostname,
|
msclkid,
|
||||||
createdAt,
|
ttclid,
|
||||||
} = data;
|
lifatid,
|
||||||
|
twclid,
|
||||||
|
}: SaveEventArgs) {
|
||||||
const websiteEventId = uuid();
|
const websiteEventId = uuid();
|
||||||
|
|
||||||
const websiteEvent = prisma.client.websiteEvent.create({
|
await prisma.client.websiteEvent.create({
|
||||||
data: {
|
data: {
|
||||||
id: websiteEventId,
|
id: websiteEventId,
|
||||||
websiteId,
|
websiteId,
|
||||||
|
|
@ -146,83 +131,49 @@ async function relationalQuery(data: {
|
||||||
createdAt,
|
createdAt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return websiteEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(data: {
|
async function clickhouseQuery({
|
||||||
websiteId: string;
|
websiteId,
|
||||||
sessionId: string;
|
sessionId,
|
||||||
visitId: string;
|
visitId,
|
||||||
urlPath: string;
|
distinctId,
|
||||||
urlQuery?: string;
|
createdAt,
|
||||||
utmSource?: string;
|
pageTitle,
|
||||||
utmMedium?: string;
|
browser,
|
||||||
utmCampaign?: string;
|
os,
|
||||||
utmContent?: string;
|
device,
|
||||||
utmTerm?: string;
|
screen,
|
||||||
referrerPath?: string;
|
language,
|
||||||
referrerQuery?: string;
|
country,
|
||||||
referrerDomain?: string;
|
region,
|
||||||
pageTitle?: string;
|
city,
|
||||||
gclid?: string;
|
tag,
|
||||||
fbclid?: string;
|
hostname,
|
||||||
msclkid?: string;
|
urlPath,
|
||||||
ttclid?: string;
|
urlQuery,
|
||||||
lifatid?: string;
|
referrerPath,
|
||||||
twclid?: string;
|
referrerQuery,
|
||||||
eventName?: string;
|
referrerDomain,
|
||||||
eventData?: any;
|
eventName,
|
||||||
hostname?: string;
|
eventData,
|
||||||
browser?: string;
|
utmSource,
|
||||||
os?: string;
|
utmMedium,
|
||||||
device?: string;
|
utmCampaign,
|
||||||
screen?: string;
|
utmContent,
|
||||||
language?: string;
|
utmTerm,
|
||||||
country?: string;
|
gclid,
|
||||||
region?: string;
|
fbclid,
|
||||||
city?: string;
|
msclkid,
|
||||||
tag?: string;
|
ttclid,
|
||||||
distinctId?: string;
|
lifatid,
|
||||||
createdAt?: Date;
|
twclid,
|
||||||
}) {
|
}: SaveEventArgs) {
|
||||||
const {
|
|
||||||
websiteId,
|
|
||||||
sessionId,
|
|
||||||
visitId,
|
|
||||||
urlPath,
|
|
||||||
urlQuery,
|
|
||||||
utmSource,
|
|
||||||
utmMedium,
|
|
||||||
utmCampaign,
|
|
||||||
utmContent,
|
|
||||||
utmTerm,
|
|
||||||
referrerPath,
|
|
||||||
referrerQuery,
|
|
||||||
referrerDomain,
|
|
||||||
gclid,
|
|
||||||
fbclid,
|
|
||||||
msclkid,
|
|
||||||
ttclid,
|
|
||||||
lifatid,
|
|
||||||
twclid,
|
|
||||||
pageTitle,
|
|
||||||
eventName,
|
|
||||||
eventData,
|
|
||||||
country,
|
|
||||||
region,
|
|
||||||
city,
|
|
||||||
tag,
|
|
||||||
distinctId,
|
|
||||||
createdAt,
|
|
||||||
...args
|
|
||||||
} = data;
|
|
||||||
const { insert, getUTCString } = clickhouse;
|
const { insert, getUTCString } = clickhouse;
|
||||||
const { sendMessage } = kafka;
|
const { sendMessage } = kafka;
|
||||||
const eventId = uuid();
|
const eventId = uuid();
|
||||||
|
|
||||||
const message = {
|
const message = {
|
||||||
...args,
|
|
||||||
website_id: websiteId,
|
website_id: websiteId,
|
||||||
session_id: sessionId,
|
session_id: sessionId,
|
||||||
visit_id: visitId,
|
visit_id: visitId,
|
||||||
|
|
@ -252,6 +203,12 @@ async function clickhouseQuery(data: {
|
||||||
tag: tag,
|
tag: tag,
|
||||||
distinct_id: distinctId,
|
distinct_id: distinctId,
|
||||||
created_at: getUTCString(createdAt),
|
created_at: getUTCString(createdAt),
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
hostname,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (kafka.enabled) {
|
if (kafka.enabled) {
|
||||||
|
|
@ -271,6 +228,4 @@ async function clickhouseQuery(data: {
|
||||||
createdAt,
|
createdAt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { Prisma } from '@prisma/client';
|
|
||||||
import { DATA_TYPE } from '@/lib/constants';
|
import { DATA_TYPE } from '@/lib/constants';
|
||||||
import { uuid } from '@/lib/crypto';
|
import { uuid } from '@/lib/crypto';
|
||||||
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
|
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
|
||||||
|
|
@ -8,7 +7,7 @@ import kafka from '@/lib/kafka';
|
||||||
import prisma from '@/lib/prisma';
|
import prisma from '@/lib/prisma';
|
||||||
import { DynamicData } from '@/lib/types';
|
import { DynamicData } from '@/lib/types';
|
||||||
|
|
||||||
export async function saveEventData(data: {
|
export interface SaveEventDataArgs {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
eventId: string;
|
eventId: string;
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
|
|
@ -16,19 +15,16 @@ export async function saveEventData(data: {
|
||||||
eventName?: string;
|
eventName?: string;
|
||||||
eventData: DynamicData;
|
eventData: DynamicData;
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
export async function saveEventData(data: SaveEventDataArgs) {
|
||||||
return runQuery({
|
return runQuery({
|
||||||
[PRISMA]: () => relationalQuery(data),
|
[PRISMA]: () => relationalQuery(data),
|
||||||
[CLICKHOUSE]: () => clickhouseQuery(data),
|
[CLICKHOUSE]: () => clickhouseQuery(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function relationalQuery(data: {
|
async function relationalQuery(data: SaveEventDataArgs) {
|
||||||
websiteId: string;
|
|
||||||
eventId: string;
|
|
||||||
eventData: DynamicData;
|
|
||||||
createdAt?: Date;
|
|
||||||
}): Promise<Prisma.BatchPayload> {
|
|
||||||
const { websiteId, eventId, eventData, createdAt } = data;
|
const { websiteId, eventId, eventData, createdAt } = data;
|
||||||
|
|
||||||
const jsonKeys = flattenJSON(eventData);
|
const jsonKeys = flattenJSON(eventData);
|
||||||
|
|
@ -46,20 +42,12 @@ async function relationalQuery(data: {
|
||||||
createdAt,
|
createdAt,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return prisma.client.eventData.createMany({
|
await prisma.client.eventData.createMany({
|
||||||
data: flattenedData,
|
data: flattenedData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(data: {
|
async function clickhouseQuery(data: SaveEventDataArgs) {
|
||||||
websiteId: string;
|
|
||||||
eventId: string;
|
|
||||||
sessionId?: string;
|
|
||||||
urlPath?: string;
|
|
||||||
eventName?: string;
|
|
||||||
eventData: DynamicData;
|
|
||||||
createdAt?: Date;
|
|
||||||
}) {
|
|
||||||
const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data;
|
const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data;
|
||||||
|
|
||||||
const { insert, getUTCString } = clickhouse;
|
const { insert, getUTCString } = clickhouse;
|
||||||
|
|
@ -88,6 +76,4 @@ async function clickhouseQuery(data: {
|
||||||
} else {
|
} else {
|
||||||
await insert('event_data', messages);
|
await insert('event_data', messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
referrer_domain as domain,
|
referrer_domain as domain,
|
||||||
referrer_query as query,
|
url_query as query,
|
||||||
count(distinct session_id) as visitors
|
count(distinct session_id) as visitors
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {{websiteId::uuid}}
|
where website_id = {{websiteId::uuid}}
|
||||||
|
|
@ -41,7 +41,7 @@ async function clickhouseQuery(
|
||||||
const sql = `
|
const sql = `
|
||||||
select
|
select
|
||||||
referrer_domain as domain,
|
referrer_domain as domain,
|
||||||
referrer_query as query,
|
url_query as query,
|
||||||
uniq(session_id) as visitors
|
uniq(session_id) as visitors
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||||
where website_event.website_id = {{websiteId::uuid}}
|
where website_event.website_id = {{websiteId::uuid}}
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
${dateQuery}
|
${dateQuery}
|
||||||
order by website_event.created_at asc
|
order by website_event.created_at desc
|
||||||
limit 100
|
limit 100
|
||||||
`,
|
`,
|
||||||
params,
|
params,
|
||||||
|
|
@ -59,7 +59,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters): Promis
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
${dateQuery}
|
${dateQuery}
|
||||||
order by createdAt asc
|
order by createdAt desc
|
||||||
limit 100
|
limit 100
|
||||||
`,
|
`,
|
||||||
params,
|
params,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export async function getRealtimeData(
|
||||||
|
|
||||||
const uniques = new Set();
|
const uniques = new Set();
|
||||||
|
|
||||||
const { countries, urls, referrers, events } = activity.reduce(
|
const { countries, urls, referrers, events } = activity.reverse().reduce(
|
||||||
(
|
(
|
||||||
obj: { countries: any; urls: any; referrers: any; events: any },
|
obj: { countries: any; urls: any; referrers: any; events: any },
|
||||||
event: {
|
event: {
|
||||||
|
|
|
||||||
|
|
@ -398,7 +398,7 @@ async function clickhouseQuery(
|
||||||
li_fat_id != '', 'LinkedIn Ads',
|
li_fat_id != '', 'LinkedIn Ads',
|
||||||
twclid != '', 'Twitter Ads (X)','') name,
|
twclid != '', 'Twitter Ads (X)','') name,
|
||||||
${currency ? 'sum(e.value)' : 'uniqExact(we.session_id)'} value
|
${currency ? 'sum(e.value)' : 'uniqExact(we.session_id)'} value
|
||||||
from model fm
|
from model m
|
||||||
join website_event we
|
join website_event we
|
||||||
on we.created_at = m.created_at
|
on we.created_at = m.created_at
|
||||||
and we.session_id = m.session_id
|
and we.session_id = m.session_id
|
||||||
|
|
|
||||||
|
|
@ -7,28 +7,29 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
|
||||||
import kafka from '@/lib/kafka';
|
import kafka from '@/lib/kafka';
|
||||||
import clickhouse from '@/lib/clickhouse';
|
import clickhouse from '@/lib/clickhouse';
|
||||||
|
|
||||||
export async function saveSessionData(data: {
|
export interface SaveSessionDataArgs {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
sessionData: DynamicData;
|
sessionData: DynamicData;
|
||||||
distinctId?: string;
|
distinctId?: string;
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
export async function saveSessionData(data: SaveSessionDataArgs) {
|
||||||
return runQuery({
|
return runQuery({
|
||||||
[PRISMA]: () => relationalQuery(data),
|
[PRISMA]: () => relationalQuery(data),
|
||||||
[CLICKHOUSE]: () => clickhouseQuery(data),
|
[CLICKHOUSE]: () => clickhouseQuery(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function relationalQuery(data: {
|
export async function relationalQuery({
|
||||||
websiteId: string;
|
websiteId,
|
||||||
sessionId: string;
|
sessionId,
|
||||||
sessionData: DynamicData;
|
sessionData,
|
||||||
distinctId?: string;
|
distinctId,
|
||||||
createdAt?: Date;
|
createdAt,
|
||||||
}) {
|
}: SaveSessionDataArgs) {
|
||||||
const { client } = prisma;
|
const { client } = prisma;
|
||||||
const { websiteId, sessionId, sessionData, distinctId, createdAt } = data;
|
|
||||||
|
|
||||||
const jsonKeys = flattenJSON(sessionData);
|
const jsonKeys = flattenJSON(sessionData);
|
||||||
|
|
||||||
|
|
@ -75,19 +76,15 @@ export async function relationalQuery(data: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flattenedData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(data: {
|
async function clickhouseQuery({
|
||||||
websiteId: string;
|
websiteId,
|
||||||
sessionId: string;
|
sessionId,
|
||||||
sessionData: DynamicData;
|
sessionData,
|
||||||
distinctId?: string;
|
distinctId,
|
||||||
createdAt?: Date;
|
createdAt,
|
||||||
}) {
|
}: SaveSessionDataArgs) {
|
||||||
const { websiteId, sessionId, sessionData, distinctId, createdAt } = data;
|
|
||||||
|
|
||||||
const { insert, getUTCString } = clickhouse;
|
const { insert, getUTCString } = clickhouse;
|
||||||
const { sendMessage } = kafka;
|
const { sendMessage } = kafka;
|
||||||
|
|
||||||
|
|
@ -112,6 +109,4 @@ async function clickhouseQuery(data: {
|
||||||
} else {
|
} else {
|
||||||
await insert('session_data', messages);
|
await insert('session_data', messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue