Merge remote-tracking branch 'origin/dev' into dev
Some checks are pending
Node.js CI / build (postgresql, 18.18, 10) (push) Waiting to run

This commit is contained in:
Mike Cao 2025-11-12 16:40:50 -08:00
commit 97ebdc1bab
8 changed files with 67 additions and 70 deletions

View file

@ -1,21 +1,15 @@
import { REALTIME_RANGE } from '@/lib/constants';
import { getQueryFilters, parseRequest } from '@/lib/request';
import { json, unauthorized } from '@/lib/response';
import { timezoneParam } from '@/lib/schema';
import { canViewWebsite } from '@/permissions';
import { getRealtimeData } from '@/queries/sql';
import { startOfMinute, subMinutes } from 'date-fns';
import z from 'zod';
export async function GET(
request: Request,
{ params }: { params: Promise<{ websiteId: string }> },
) {
const schema = z.object({
timezone: timezoneParam,
});
const { auth, query, error } = await parseRequest(request, schema);
const { auth, query, error } = await parseRequest(request);
if (error) {
return error();

View file

@ -146,6 +146,7 @@ export async function POST(request: Request) {
region,
city,
distinctId: id,
createdAt,
});
}

View file

@ -1,34 +1,13 @@
import { useTimezone } from '@/components/hooks/useTimezone';
import { REALTIME_INTERVAL } from '@/lib/constants';
import { useApi } from '../useApi';
export interface RealtimeData {
countries: Record<string, number>;
events: any[];
pageviews: any[];
referrers: Record<string, number>;
timestamp: number;
series: {
views: any[];
visitors: any[];
};
totals: {
views: number;
visitors: number;
events: number;
countries: number;
};
urls: Record<string, number>;
visitors: any[];
}
import { RealtimeData } from '@/lib/types';
export function useRealtimeQuery(websiteId: string) {
const { get, useQuery } = useApi();
const { timezone } = useTimezone();
const { data, isLoading, error } = useQuery<RealtimeData>({
queryKey: ['realtime', { websiteId, timezone }],
queryKey: ['realtime', { websiteId }],
queryFn: async () => {
return get(`/realtime/${websiteId}`, { timezone });
return get(`/realtime/${websiteId}`);
},
enabled: !!websiteId,
refetchInterval: REALTIME_INTERVAL,

View file

@ -61,7 +61,7 @@ function getDateStringSQL(data: any, unit: string = 'utc', timezone?: string) {
function getDateSQL(field: string, unit: string, timezone?: string) {
if (timezone) {
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'), '${timezone}')`;
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'))`;
}
return `toDateTime(date_trunc('${unit}', ${field}))`;
}

View file

@ -116,3 +116,23 @@ export interface PageResult<T> {
sortDescending?: boolean;
search?: string;
}
export interface RealtimeData {
countries: Record<string, number>;
events: any[];
pageviews: any[];
referrers: Record<string, number>;
timestamp: number;
series: {
views: any[];
visitors: any[];
};
totals: {
views: number;
visitors: number;
events: number;
countries: number;
};
urls: Record<string, number>;
visitors: any[];
}

View file

@ -45,7 +45,7 @@ async function clickhouseQuery(
websiteId: string,
filters: QueryFilters,
): Promise<{ x: string; y: number }[]> {
const { timezone = 'utc', unit = 'day' } = filters;
const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({
...filters,

View file

@ -1,41 +1,44 @@
import { Prisma } from '@/generated/prisma/client';
import prisma from '@/lib/prisma';
export async function createSession(data: Prisma.SessionCreateInput) {
const {
id,
websiteId,
browser,
os,
device,
screen,
language,
country,
region,
city,
distinctId,
} = data;
const FUNCTION_NAME = 'createSession';
try {
return await prisma.client.session.create({
data: {
id,
websiteId,
browser,
os,
device,
screen,
language,
country,
region,
city,
distinctId,
},
});
} catch (e: any) {
if (e.message.toLowerCase().includes('unique constraint')) {
return null;
}
throw e;
}
export async function createSession(data: Prisma.SessionCreateInput) {
const { rawQuery } = prisma;
await rawQuery(
`
insert into session (
session_id,
website_id,
browser,
os,
device,
screen,
language,
country,
region,
city,
distinct_id,
created_at
)
values (
{{id}},
{{websiteId}},
{{browser}},
{{os}},
{{device}},
{{screen}},
{{language}},
{{country}},
{{region}},
{{city}},
{{distinctId}},
{{createdAt}}
)
on conflict (session_id) do nothing
`,
data,
FUNCTION_NAME,
);
}

View file

@ -45,7 +45,7 @@ async function clickhouseQuery(
websiteId: string,
filters: QueryFilters,
): Promise<{ x: string; y: number }[]> {
const { timezone = 'utc', unit = 'day' } = filters;
const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({
...filters,