Skip to content

Commit

Permalink
feat: add telemetry trpc feature
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Feb 27, 2024
1 parent b50de6b commit 0bd98ad
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 54 deletions.
19 changes: 12 additions & 7 deletions src/server/model/_schema/filter.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { z } from 'zod';

export const websiteFilterSchema = z.object({
timezone: z.string(),
export const baseFilterSchema = z.object({
url: z.string(),
referrer: z.string(),
title: z.string(),
os: z.string(),
browser: z.string(),
device: z.string(),
country: z.string(),
region: z.string(),
city: z.string(),
});

export const websiteFilterSchema = baseFilterSchema.merge(
z.object({
timezone: z.string(),
referrer: z.string(),
title: z.string(),
os: z.string(),
browser: z.string(),
device: z.string(),
})
);

const websiteStatsItemType = z.object({
value: z.number(),
prev: z.number(),
Expand Down
175 changes: 174 additions & 1 deletion src/server/model/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { TelemetrySession } from '@prisma/client';
import { Prisma, Telemetry, TelemetrySession } from '@prisma/client';
import { Request } from 'express';
import { hashUuid } from '../utils/common';
import { getRequestInfo } from '../utils/detect';
import { prisma } from './_client';
import {
BaseQueryFilters,
getDateQuery,
getTimestampIntervalQuery,
parseTelemetryFilters,
} from '../utils/prisma';
import { SESSION_COLUMNS } from '../utils/const';

export async function recordTelemetryEvent(req: Request) {
const { url = req.headers.referer, name, ...others } = req.query;
Expand Down Expand Up @@ -135,3 +142,169 @@ async function loadSession(

return session;
}

export async function loadTelemetry(
telemetryId: string
): Promise<Telemetry | null> {
const telemetry = await prisma.telemetry.findUnique({
where: {
id: telemetryId,
},
});

if (!telemetry || telemetry.deletedAt) {
return null;
}

return telemetry;
}

export async function getTelemetryPageview(
telemetryId: string,
filters: BaseQueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseTelemetryFilters(
telemetryId,
{
...filters,
}
);

return prisma.$queryRaw`
select
${getDateQuery('"TelemetryEvent"."createdAt"', unit, timezone)} x,
count(1) y
from "TelemetryEvent"
${joinSession}
where "TelemetryEvent"."telemetryId" = ${params.telemetryId}
and "TelemetryEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
${filterQuery}
group by 1
`;
}

export async function getTelemetrySession(
telemetryId: string,
filters: BaseQueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseTelemetryFilters(
telemetryId,
{
...filters,
}
);

return prisma.$queryRaw`
select
${getDateQuery('"TelemetryEvent"."createdAt"', unit, timezone)} x,
count(distinct "TelemetryEvent"."sessionId") y
from "TelemetryEvent"
${joinSession}
where "TelemetryEvent"."telemetryId" = ${params.telemetryId}
and "TelemetryEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
${filterQuery}
group by 1
`;
}

export async function getTelemetryStats(
telemetryId: string,
filters: BaseQueryFilters
): Promise<any> {
const { filterQuery, joinSession, params } = await parseTelemetryFilters(
telemetryId,
{
...filters,
}
);

return prisma.$queryRaw`
select
sum(t.c) as "pageviews",
count(distinct t."sessionId") as "uniques"
from (
select
"TelemetryEvent"."sessionId",
${getDateQuery('"TelemetryEvent"."createdAt"', 'hour')}
from "TelemetryEvent"
join "Telemetry"
on "TelemetryEvent"."telemetryId" = "Telemetry"."id"
${joinSession}
where "Telemetry"."id" = ${params.telemetryId}
and "TelemetryEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
${filterQuery}
group by 1, 2
) as t
`;
}

export async function getTelemetrySessionMetrics(
telemetryId: string,
column: string,
filters: BaseQueryFilters
): Promise<{ x: string; y: number }[]> {
const { filterQuery, joinSession, params } = await parseTelemetryFilters(
telemetryId,
{
...filters,
},
{
joinSession: SESSION_COLUMNS.includes(column),
}
);
const includeCountry = column === 'city' || column === 'subdivision1';

return prisma.$queryRaw`select
${Prisma.sql([`"${column}"`])} x,
count(distinct "TelemetryEvent"."sessionId") y
${includeCountry ? Prisma.sql([', country']) : Prisma.empty}
from "TelemetryEvent"
${joinSession}
where "TelemetryEvent"."telemetryId" = ${telemetryId}
and "TelemetryEvent"."createdAt"
between ${params.startDate}::timestamptz and ${
params.endDate
}::timestamptz
${filterQuery}
group by 1
${includeCountry ? Prisma.sql([', 3']) : Prisma.empty}
order by 2 desc
limit 100`;
}

export async function getTelemetryPageviewMetrics(
telemetryId: string,
column: string,
filters: BaseQueryFilters
): Promise<{ x: string; y: number }[]> {
const { filterQuery, joinSession, params } = await parseTelemetryFilters(
telemetryId,
{
...filters,
},
{ joinSession: SESSION_COLUMNS.includes(column) }
);

return prisma.$queryRaw`
select ${Prisma.sql([`"${column}"`])} x, count(*) y
from "TelemetryEvent"
${joinSession}
where "TelemetryEvent"."telemetryId" = ${telemetryId}
and "TelemetryEvent"."createdAt"
between ${params.startDate}::timestamptz and ${
params.endDate
}::timestamptz
${filterQuery}
group by 1
order by 2 desc
limit 100
`;
}
49 changes: 29 additions & 20 deletions src/server/model/website.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {
import type { DynamicData } from '../utils/types';
import dayjs from 'dayjs';
import {
QueryFilters,
WebsiteQueryFilters,
getDateQuery,
getTimestampIntervalQuery,
parseFilters,
parseWebsiteFilters,
} from '../utils/prisma';

export interface WebsiteEventPayload {
Expand Down Expand Up @@ -277,12 +277,12 @@ export async function getWebsiteOnlineUserCount(
return Number(res?.[0].x ?? 0);
}

export async function getSessionMetrics(
export async function getWebsiteSessionMetrics(
websiteId: string,
column: string,
filters: QueryFilters
filters: WebsiteQueryFilters
): Promise<{ x: string; y: number }[]> {
const { filterQuery, joinSession, params } = await parseFilters(
const { filterQuery, joinSession, params } = await parseWebsiteFilters(
websiteId,
{
...filters,
Expand Down Expand Up @@ -312,14 +312,14 @@ export async function getSessionMetrics(
limit 100`;
}

export async function getPageviewMetrics(
export async function getWebsitePageviewMetrics(
websiteId: string,
column: string,
filters: QueryFilters
filters: WebsiteQueryFilters
): Promise<{ x: string; y: number }[]> {
const eventType =
column === 'eventName' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView;
const { filterQuery, joinSession, params } = await parseFilters(
const { filterQuery, joinSession, params } = await parseWebsiteFilters(
websiteId,
{
...filters,
Expand Down Expand Up @@ -352,12 +352,15 @@ export async function getPageviewMetrics(

export async function getWorkspaceWebsitePageview(
websiteId: string,
filters: QueryFilters
filters: WebsiteQueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
});
const { filterQuery, joinSession, params } = await parseWebsiteFilters(
websiteId,
{
...filters,
}
);

return prisma.$queryRaw`
select
Expand All @@ -377,12 +380,15 @@ export async function getWorkspaceWebsitePageview(

export async function getWorkspaceWebsiteSession(
websiteId: string,
filters: QueryFilters
filters: WebsiteQueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
});
const { filterQuery, joinSession, params } = await parseWebsiteFilters(
websiteId,
{
...filters,
}
);

return prisma.$queryRaw`
select
Expand All @@ -402,11 +408,14 @@ export async function getWorkspaceWebsiteSession(

export async function getWorkspaceWebsiteStats(
websiteId: string,
filters: QueryFilters
filters: WebsiteQueryFilters
): Promise<any> {
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
});
const { filterQuery, joinSession, params } = await parseWebsiteFilters(
websiteId,
{
...filters,
}
);

return prisma.$queryRaw`
select
Expand Down
9 changes: 2 additions & 7 deletions src/server/model/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { prisma } from './_client';
import {
QueryFilters,
parseFilters,
getDateQuery,
getTimestampIntervalQuery,
} from '../utils/prisma';
import { parseWebsiteFilters } from '../utils/prisma';
import { DEFAULT_RESET_DATE, EVENT_TYPE } from '../utils/const';

export async function getWorkspaceUser(workspaceId: string, userId: string) {
Expand Down Expand Up @@ -67,7 +62,7 @@ export async function deleteWorkspaceWebsite(
}

export async function getWorkspaceWebsiteDateRange(websiteId: string) {
const { params } = await parseFilters(websiteId, {
const { params } = await parseWebsiteFilters(websiteId, {
startDate: new Date(DEFAULT_RESET_DATE),
});

Expand Down
Loading

0 comments on commit 0bd98ad

Please sign in to comment.