From ba0b3987199fafe634228fb21d2872202bf78943 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Fri, 15 Sep 2023 00:53:03 +0800 Subject: [PATCH] feat: make pageviewstats can return data --- src/server/init.ts | 6 ++++++ src/server/main.ts | 1 + src/server/model/workspace.ts | 18 +++++++++--------- src/server/router/workspace.ts | 12 +++++++++--- src/server/utils/common.ts | 5 ++++- src/server/utils/prisma.ts | 28 ++++++++++++++++++++++------ 6 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 src/server/init.ts diff --git a/src/server/init.ts b/src/server/init.ts new file mode 100644 index 0000000..8a3283d --- /dev/null +++ b/src/server/init.ts @@ -0,0 +1,6 @@ +(BigInt.prototype as any).toJSON = function () { + const int = Number.parseInt(this.toString()); + return int ?? this.toString(); +}; + +export {}; diff --git a/src/server/main.ts b/src/server/main.ts index 538abf9..8a9d049 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -1,4 +1,5 @@ import 'dotenv/config'; +import './init'; import express from 'express'; import 'express-async-errors'; import ViteExpress from 'vite-express'; diff --git a/src/server/model/workspace.ts b/src/server/model/workspace.ts index 0f85527..fb9a4ef 100644 --- a/src/server/model/workspace.ts +++ b/src/server/model/workspace.ts @@ -1,6 +1,7 @@ import { prisma } from './_client'; import { QueryFilters, parseFilters, getDateQuery } from '../utils/prisma'; import { DEFAULT_RESET_DATE, EVENT_TYPE } from '../utils/const'; +import { Prisma } from '@prisma/client'; export async function getWorkspaceUser(workspaceId: string, userId: string) { const info = await prisma.workspacesOnUsers.findFirst({ @@ -115,15 +116,14 @@ export async function getWorkspaceWebsitePageviewStats( return prisma.$queryRaw` select - ${getDateQuery('website_event.created_at', unit, timezone)} x, - count(*) y - from website_event - ${joinSession} - where website_event.website_id = ${params.websiteId} - and website_event.created_at - between ${params.startDate} and ${(params as any).endDate} - and event_type = {{eventType}} - ${filterQuery} + ${getDateQuery('"WebsiteEvent"."createdAt"', unit, timezone)} x, + count(1) y + from "WebsiteEvent" + ${joinSession ? Prisma.sql([joinSession]) : Prisma.empty} + where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid + and "WebsiteEvent"."createdAt" + between ${params.startDate}::timestamptz and ${params.endDate}::timestamptz + and "WebsiteEvent"."eventType" = ${EVENT_TYPE.pageView} group by 1 `; } diff --git a/src/server/router/workspace.ts b/src/server/router/workspace.ts index c6c0b5a..b3309e9 100644 --- a/src/server/router/workspace.ts +++ b/src/server/router/workspace.ts @@ -139,7 +139,9 @@ workspaceRouter.get( '/:workspaceId/website/:websiteId/pageviews', validate( param('workspaceId').isUUID().withMessage('workspaceId should be UUID'), - param('websiteId').isUUID().withMessage('workspaceId should be UUID') + param('websiteId').isUUID().withMessage('workspaceId should be UUID'), + query('startAt').isNumeric().withMessage('startAt should be number'), + query('endAt').isNumeric().withMessage('startAt should be number') ), auth(), workspacePermission(), @@ -184,11 +186,15 @@ workspaceRouter.get( city, }; - const website = await getWorkspaceWebsitePageviewStats( + console.log('filters', filters); + + const stats = await getWorkspaceWebsitePageviewStats( websiteId, filters as QueryFilters ); - res.json({ website }); + console.log('stats', stats); + + res.json({ stats }); } ); diff --git a/src/server/utils/common.ts b/src/server/utils/common.ts index 29d937b..6e231e3 100644 --- a/src/server/utils/common.ts +++ b/src/server/utils/common.ts @@ -3,10 +3,13 @@ import crypto from 'crypto'; import { DATA_TYPE } from './const'; import { DynamicDataType } from './types'; import dayjs from 'dayjs'; +import minMax from 'dayjs/plugin/minMax'; import jwt from 'jsonwebtoken'; import _ from 'lodash'; import { getWorkspaceWebsiteDateRange } from '../model/workspace'; +dayjs.extend(minMax); + export function isUuid(value: string) { return validate(value); } @@ -152,7 +155,7 @@ export function parseToken(token: string, secret = jwtSecret) { } export function maxDate(...args: any[]) { - return _.max(args.filter((n) => dayjs(n).isValid())); + return dayjs.max(args.filter((n) => dayjs(n).isValid())); } export async function parseDateRange({ diff --git a/src/server/utils/prisma.ts b/src/server/utils/prisma.ts index 4371b50..8c22c52 100644 --- a/src/server/utils/prisma.ts +++ b/src/server/utils/prisma.ts @@ -1,3 +1,5 @@ +import { Prisma } from '@prisma/client'; +import dayjs from 'dayjs'; import _ from 'lodash'; import { loadWebsite } from '../model/website'; import { maxDate } from './common'; @@ -50,14 +52,22 @@ export async function parseFilters( return { joinSession: options?.joinSession || - Object.keys(filters).find((key) => SESSION_COLUMNS.includes(key)) - ? `inner join session on website_event.session_id = session.session_id` + Object.entries(filters).find( + ([key, value]) => + typeof value !== 'undefined' && SESSION_COLUMNS.includes(key) + ) + ? `inner join "WebsiteSession" on "WebsiteEvent"."sessionId" = "WebsiteSession"."id"` : '', filterQuery: getFilterQuery(filters, options), params: { ...normalizeFilters(filters), websiteId, - startDate: maxDate(filters.startDate, website.resetAt), + startDate: dayjs( + maxDate(filters.startDate, website.resetAt) + ).toISOString(), + endDate: filters.endDate + ? dayjs(filters.endDate).toISOString() + : undefined, websiteDomain: website.domain, }, }; @@ -82,6 +92,8 @@ export function getFilterQuery( const operator = value?.filter ?? OPERATORS.equals; const column = _.get(FILTER_COLUMNS, name, options?.columns?.[name]); + // TODO + if (value !== undefined && column) { arr.push(`and ${mapFilter(column, operator, name)}`); @@ -118,9 +130,13 @@ export function getDateQuery( field: string, unit: keyof typeof POSTGRESQL_DATE_FORMATS, timezone?: string -): string { +) { if (timezone) { - return `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return Prisma.sql([ + `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${POSTGRESQL_DATE_FORMATS[unit]}')`, + ]); } - return `to_char(date_trunc('${unit}', ${field}), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return Prisma.sql([ + `to_char(date_trunc('${unit}', ${field}), '${POSTGRESQL_DATE_FORMATS[unit]}')`, + ]); }