diff --git a/next.config.ts b/next.config.ts index 6fac599e4..17705dc20 100644 --- a/next.config.ts +++ b/next.config.ts @@ -14,14 +14,14 @@ const frameAncestors = process.env.ALLOWED_FRAME_URLS || ''; const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || ''; const trackerScriptURL = process.env.TRACKER_SCRIPT_URL || ''; -const contentSecurityPolicy = ` - default-src 'self'; - img-src 'self' https: data:; - script-src 'self' 'unsafe-eval' 'unsafe-inline'; - style-src 'self' 'unsafe-inline'; - connect-src *; - frame-ancestors 'self' ${frameAncestors}; -`; +const contentSecurityPolicy = [ + `default-src 'self'`, + `img-src * data:`, + `script-src 'self' 'unsafe-eval' 'unsafe-inline'`, + `style-src 'self' 'unsafe-inline'`, + `connect-src 'self' api.umami.is cloud.umami.is`, + `frame-ancestors 'self' ${frameAncestors}`, +]; const defaultHeaders = [ { @@ -30,7 +30,10 @@ const defaultHeaders = [ }, { key: 'Content-Security-Policy', - value: contentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(), + value: contentSecurityPolicy + .join(';') + .replace(/\s{2,}/g, ' ') + .trim(), }, ]; diff --git a/scripts/check-env.js b/scripts/check-env.js index 79c0984dd..c73bed546 100644 --- a/scripts/check-env.js +++ b/scripts/check-env.js @@ -23,5 +23,12 @@ if (!process.env.SKIP_DB_CHECK && !process.env.DATABASE_TYPE) { } if (process.env.CLOUD_URL) { - checkMissing(['CLOUD_URL', 'CLICKHOUSE_URL', 'REDIS_URL']); + checkMissing([ + 'CLOUD_URL', + 'CLICKHOUSE_URL', + 'REDIS_URL', + 'KAFKA_BROKER', + 'KAFKA_URL', + 'KAFKA_SASL_MECHANISM', + ]); } diff --git a/src/app/(main)/pixels/[pixelId]/page.tsx b/src/app/(main)/pixels/[pixelId]/page.tsx index 85456d29e..04800cae6 100644 --- a/src/app/(main)/pixels/[pixelId]/page.tsx +++ b/src/app/(main)/pixels/[pixelId]/page.tsx @@ -1,8 +1,8 @@ import { PixelPage } from './PixelPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { pixelId: string } }) { - const { pixelId } = await params; +export default function ({ params }: { params: { pixelId: string } }) { + const { pixelId } = params; return ; } diff --git a/src/app/api/websites/[websiteId]/metrics/expanded/route.ts b/src/app/api/websites/[websiteId]/metrics/expanded/route.ts index 703cf5ee7..c68acfe5b 100644 --- a/src/app/api/websites/[websiteId]/metrics/expanded/route.ts +++ b/src/app/api/websites/[websiteId]/metrics/expanded/route.ts @@ -1,8 +1,8 @@ -import { EVENT_COLUMNS, EVENT_TYPE, SESSION_COLUMNS } from '@/lib/constants'; +import { canViewWebsite } from '@/permissions'; +import { EVENT_COLUMNS, EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from '@/lib/constants'; import { getQueryFilters, parseRequest } from '@/lib/request'; import { badRequest, json, unauthorized } from '@/lib/response'; import { dateRangeParams, filterParams, searchParams } from '@/lib/schema'; -import { canViewWebsite } from '@/permissions'; import { getChannelExpandedMetrics, getEventExpandedMetrics, @@ -50,8 +50,13 @@ export async function GET( } if (EVENT_COLUMNS.includes(type)) { - if (type === 'event') { + const column = FILTER_COLUMNS[type] || type; + + if (column === 'event_name') { filters.eventType = EVENT_TYPE.customEvent; + } + + if (type === 'event') { return json(await getEventExpandedMetrics(websiteId, { type, limit, offset }, filters)); } else { return json(await getPageviewExpandedMetrics(websiteId, { type, limit, offset }, filters)); diff --git a/src/app/api/websites/[websiteId]/metrics/route.ts b/src/app/api/websites/[websiteId]/metrics/route.ts index 3bfeaa6a4..461b5359e 100644 --- a/src/app/api/websites/[websiteId]/metrics/route.ts +++ b/src/app/api/websites/[websiteId]/metrics/route.ts @@ -1,8 +1,7 @@ -import { EVENT_COLUMNS, EVENT_TYPE, SESSION_COLUMNS } from '@/lib/constants'; +import { canViewWebsite } from '@/permissions'; +import { EVENT_COLUMNS, EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from '@/lib/constants'; import { getQueryFilters, parseRequest } from '@/lib/request'; import { badRequest, json, unauthorized } from '@/lib/response'; -import { dateRangeParams, filterParams, searchParams } from '@/lib/schema'; -import { canViewWebsite } from '@/permissions'; import { getChannelMetrics, getEventMetrics, @@ -10,6 +9,7 @@ import { getSessionMetrics, } from '@/queries/sql'; import { z } from 'zod'; +import { dateRangeParams, filterParams, searchParams } from '@/lib/schema'; export async function GET( request: Request, @@ -50,8 +50,13 @@ export async function GET( } if (EVENT_COLUMNS.includes(type)) { - if (type === 'event') { + const column = FILTER_COLUMNS[type] || type; + + if (column === 'event_name') { filters.eventType = EVENT_TYPE.customEvent; + } + + if (type === 'event') { return json(await getEventMetrics(websiteId, { type, limit, offset }, filters)); } else { return json(await getPageviewMetrics(websiteId, { type, limit, offset }, filters)); diff --git a/src/lib/date.ts b/src/lib/date.ts index 6a967477e..410a8acf6 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -122,8 +122,9 @@ export function parseDateValue(value: string) { if (!match) return null; const { num, unit } = match.groups; + const formattedNum = +num > 0 ? +num - 1 : +num; - return { num: +num, unit }; + return { num: formattedNum, unit }; } export function parseDateRange(value: string, locale = 'en-US'): DateRange { diff --git a/src/queries/sql/events/getEventExpandedMetrics.ts b/src/queries/sql/events/getEventExpandedMetrics.ts index 6be99b409..96a452fa9 100644 --- a/src/queries/sql/events/getEventExpandedMetrics.ts +++ b/src/queries/sql/events/getEventExpandedMetrics.ts @@ -75,7 +75,6 @@ async function clickhouseQuery( const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, - eventType: EVENT_TYPE.customEvent, }); return rawQuery( diff --git a/src/queries/sql/getChannelExpandedMetrics.ts b/src/queries/sql/getChannelExpandedMetrics.ts index 571c9d9d8..af67c93c5 100644 --- a/src/queries/sql/getChannelExpandedMetrics.ts +++ b/src/queries/sql/getChannelExpandedMetrics.ts @@ -1,6 +1,7 @@ import clickhouse from '@/lib/clickhouse'; import { EMAIL_DOMAINS, + EVENT_TYPE, PAID_AD_PARAMS, SEARCH_DOMAINS, SHOPPING_DOMAINS, @@ -44,6 +45,7 @@ async function relationalQuery( const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -66,8 +68,7 @@ async function relationalQuery( from website_event ${cohortQuery} ${joinSessionQuery} - where website_event.website_id = {{websiteId::uuid}} - and website_event.event_type != 2 + where website_id = {{websiteId::uuid}} ${dateQuery} ${filterQuery} group by 1, 2 @@ -92,6 +93,7 @@ async function clickhouseQuery( const { queryParams, filterQuery, cohortQuery } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -138,7 +140,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 and name != '' ${filterQuery} group by prefix, name, session_id, visit_id diff --git a/src/queries/sql/getChannelMetrics.ts b/src/queries/sql/getChannelMetrics.ts index 5fc9187e6..05ddda3d2 100644 --- a/src/queries/sql/getChannelMetrics.ts +++ b/src/queries/sql/getChannelMetrics.ts @@ -1,6 +1,7 @@ import clickhouse from '@/lib/clickhouse'; import { EMAIL_DOMAINS, + EVENT_TYPE, PAID_AD_PARAMS, SEARCH_DOMAINS, SHOPPING_DOMAINS, @@ -25,6 +26,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -47,8 +49,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { from website_event ${cohortQuery} ${joinSessionQuery} - where website_event.website_id = {{websiteId::uuid}} - and website_event.event_type != 2 + where website_id = {{websiteId::uuid}} ${dateQuery} ${filterQuery} group by 1, 2 @@ -73,6 +74,7 @@ async function clickhouseQuery( const { queryParams, filterQuery, cohortQuery, dateQuery } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); const sql = ` @@ -106,7 +108,6 @@ async function clickhouseQuery( from website_event ${cohortQuery} where website_id = {websiteId:UUID} - and event_type != 2 ${dateQuery} ${filterQuery} group by 1, 2 diff --git a/src/queries/sql/getWebsiteStats.ts b/src/queries/sql/getWebsiteStats.ts index 9551cd8f7..79fb8bbe9 100644 --- a/src/queries/sql/getWebsiteStats.ts +++ b/src/queries/sql/getWebsiteStats.ts @@ -1,8 +1,9 @@ import clickhouse from '@/lib/clickhouse'; -import { EVENT_COLUMNS } from '@/lib/constants'; +import { EVENT_TYPE } from '@/lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; import prisma from '@/lib/prisma'; import { QueryFilters } from '@/lib/types'; +import { EVENT_COLUMNS } from '@/lib/constants'; export interface WebsiteStatsData { pageviews: number; @@ -29,6 +30,7 @@ async function relationalQuery( const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -51,7 +53,6 @@ async function relationalQuery( ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${filterQuery} group by 1, 2 ) as t @@ -68,6 +69,7 @@ async function clickhouseQuery( const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -91,7 +93,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by session_id, visit_id ) as t; @@ -114,7 +115,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by session_id, visit_id ) as t; diff --git a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts index a718c4113..cef79096c 100644 --- a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts @@ -64,7 +64,7 @@ async function relationalQuery( from website_event where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 + and event_type = {{eventType}} group by visit_id ) x on x.visit_id = website_event.visit_id @@ -82,7 +82,6 @@ async function relationalQuery( ${entryExitQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${excludeDomain} ${filterQuery} group by 1 @@ -128,7 +127,7 @@ async function clickhouseQuery( from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 + and event_type = {eventType:UInt32} group by visit_id) x ON x.visit_id = website_event.visit_id`; } @@ -155,7 +154,6 @@ async function clickhouseQuery( ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 and name != '' ${excludeDomain} ${filterQuery} diff --git a/src/queries/sql/pageviews/getPageviewMetrics.ts b/src/queries/sql/pageviews/getPageviewMetrics.ts index 0a92e6afd..71ce13b37 100644 --- a/src/queries/sql/pageviews/getPageviewMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewMetrics.ts @@ -62,7 +62,7 @@ async function relationalQuery( from website_event where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 + and event_type = {{eventType}} order by visit_id, created_at ${order} ) x on x.visit_id = website_event.visit_id @@ -79,7 +79,6 @@ async function relationalQuery( ${entryExitQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${excludeDomain} ${filterQuery} group by 1 @@ -125,7 +124,7 @@ async function clickhouseQuery( from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 + and event_type = {eventType:UInt32} group by visit_id) x ON x.visit_id = website_event.visit_id`; } @@ -138,7 +137,6 @@ async function clickhouseQuery( ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${excludeDomain} ${filterQuery} group by x @@ -176,7 +174,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${excludeDomain} ${filterQuery} ${groupByQuery}) as g diff --git a/src/queries/sql/pageviews/getPageviewStats.ts b/src/queries/sql/pageviews/getPageviewStats.ts index 1e7f9d61b..a8ee4734c 100644 --- a/src/queries/sql/pageviews/getPageviewStats.ts +++ b/src/queries/sql/pageviews/getPageviewStats.ts @@ -1,7 +1,7 @@ import clickhouse from '@/lib/clickhouse'; -import { EVENT_COLUMNS } from '@/lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; import prisma from '@/lib/prisma'; +import { EVENT_COLUMNS, EVENT_TYPE } from '@/lib/constants'; import { QueryFilters } from '@/lib/types'; export async function getPageviewStats(...args: [websiteId: string, filters: QueryFilters]) { @@ -17,6 +17,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { const { filterQuery, cohortQuery, joinSessionQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -29,7 +30,6 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${filterQuery} group by 1 order by 1 @@ -47,6 +47,7 @@ async function clickhouseQuery( const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -64,7 +65,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by t ) as g @@ -83,7 +83,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by t ) as g diff --git a/src/queries/sql/sessions/getSessionExpandedMetrics.ts b/src/queries/sql/sessions/getSessionExpandedMetrics.ts index b4ba5b671..56dba66e7 100644 --- a/src/queries/sql/sessions/getSessionExpandedMetrics.ts +++ b/src/queries/sql/sessions/getSessionExpandedMetrics.ts @@ -1,5 +1,5 @@ import clickhouse from '@/lib/clickhouse'; -import { FILTER_COLUMNS, SESSION_COLUMNS } from '@/lib/constants'; +import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from '@/lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; import prisma from '@/lib/prisma'; import { QueryFilters } from '@/lib/types'; @@ -40,6 +40,7 @@ async function relationalQuery( { ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }, { joinSession: SESSION_COLUMNS.includes(type), @@ -62,7 +63,6 @@ async function relationalQuery( ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${filterQuery} group by 1 ${includeCountry ? ', 3' : ''} @@ -85,6 +85,7 @@ async function clickhouseQuery( const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); const includeCountry = column === 'city' || column === 'region'; @@ -115,7 +116,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 and name != '' ${filterQuery} group by name, session_id, visit_id diff --git a/src/queries/sql/sessions/getSessionMetrics.ts b/src/queries/sql/sessions/getSessionMetrics.ts index 7133332b5..f4e6a22f6 100644 --- a/src/queries/sql/sessions/getSessionMetrics.ts +++ b/src/queries/sql/sessions/getSessionMetrics.ts @@ -55,7 +55,7 @@ async function relationalQuery( ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 + and event_type != 2 ${filterQuery} group by 1 ${includeCountry ? ', 3' : ''} diff --git a/src/queries/sql/sessions/getSessionStats.ts b/src/queries/sql/sessions/getSessionStats.ts index b1c9891eb..62ef44802 100644 --- a/src/queries/sql/sessions/getSessionStats.ts +++ b/src/queries/sql/sessions/getSessionStats.ts @@ -1,5 +1,5 @@ import clickhouse from '@/lib/clickhouse'; -import { EVENT_COLUMNS } from '@/lib/constants'; +import { EVENT_COLUMNS, EVENT_TYPE } from '@/lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; import prisma from '@/lib/prisma'; import { QueryFilters } from '@/lib/types'; @@ -17,6 +17,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -29,7 +30,6 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type != 2 ${filterQuery} group by 1 order by 1 @@ -47,6 +47,7 @@ async function clickhouseQuery( const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, + eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -64,7 +65,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by t ) as g @@ -83,7 +83,6 @@ async function clickhouseQuery( ${cohortQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type != 2 ${filterQuery} group by t ) as g