mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Merge remote-tracking branch 'origin/dev' into dev
Some checks are pending
Node.js CI / build (postgresql, 18.18, 10) (push) Waiting to run
Some checks are pending
Node.js CI / build (postgresql, 18.18, 10) (push) Waiting to run
This commit is contained in:
commit
97ebdc1bab
8 changed files with 67 additions and 70 deletions
|
|
@ -1,21 +1,15 @@
|
||||||
import { REALTIME_RANGE } from '@/lib/constants';
|
import { REALTIME_RANGE } from '@/lib/constants';
|
||||||
import { getQueryFilters, parseRequest } from '@/lib/request';
|
import { getQueryFilters, parseRequest } from '@/lib/request';
|
||||||
import { json, unauthorized } from '@/lib/response';
|
import { json, unauthorized } from '@/lib/response';
|
||||||
import { timezoneParam } from '@/lib/schema';
|
|
||||||
import { canViewWebsite } from '@/permissions';
|
import { canViewWebsite } from '@/permissions';
|
||||||
import { getRealtimeData } from '@/queries/sql';
|
import { getRealtimeData } from '@/queries/sql';
|
||||||
import { startOfMinute, subMinutes } from 'date-fns';
|
import { startOfMinute, subMinutes } from 'date-fns';
|
||||||
import z from 'zod';
|
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: Request,
|
request: Request,
|
||||||
{ params }: { params: Promise<{ websiteId: string }> },
|
{ params }: { params: Promise<{ websiteId: string }> },
|
||||||
) {
|
) {
|
||||||
const schema = z.object({
|
const { auth, query, error } = await parseRequest(request);
|
||||||
timezone: timezoneParam,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { auth, query, error } = await parseRequest(request, schema);
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return error();
|
return error();
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ export async function POST(request: Request) {
|
||||||
region,
|
region,
|
||||||
city,
|
city,
|
||||||
distinctId: id,
|
distinctId: id,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,13 @@
|
||||||
import { useTimezone } from '@/components/hooks/useTimezone';
|
|
||||||
import { REALTIME_INTERVAL } from '@/lib/constants';
|
import { REALTIME_INTERVAL } from '@/lib/constants';
|
||||||
import { useApi } from '../useApi';
|
import { useApi } from '../useApi';
|
||||||
|
import { RealtimeData } from '@/lib/types';
|
||||||
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[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useRealtimeQuery(websiteId: string) {
|
export function useRealtimeQuery(websiteId: string) {
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
const { timezone } = useTimezone();
|
|
||||||
const { data, isLoading, error } = useQuery<RealtimeData>({
|
const { data, isLoading, error } = useQuery<RealtimeData>({
|
||||||
queryKey: ['realtime', { websiteId, timezone }],
|
queryKey: ['realtime', { websiteId }],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return get(`/realtime/${websiteId}`, { timezone });
|
return get(`/realtime/${websiteId}`);
|
||||||
},
|
},
|
||||||
enabled: !!websiteId,
|
enabled: !!websiteId,
|
||||||
refetchInterval: REALTIME_INTERVAL,
|
refetchInterval: REALTIME_INTERVAL,
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ function getDateStringSQL(data: any, unit: string = 'utc', timezone?: string) {
|
||||||
|
|
||||||
function getDateSQL(field: string, unit: string, timezone?: string) {
|
function getDateSQL(field: string, unit: string, timezone?: string) {
|
||||||
if (timezone) {
|
if (timezone) {
|
||||||
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'), '${timezone}')`;
|
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'))`;
|
||||||
}
|
}
|
||||||
return `toDateTime(date_trunc('${unit}', ${field}))`;
|
return `toDateTime(date_trunc('${unit}', ${field}))`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,23 @@ export interface PageResult<T> {
|
||||||
sortDescending?: boolean;
|
sortDescending?: boolean;
|
||||||
search?: string;
|
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[];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ async function clickhouseQuery(
|
||||||
websiteId: string,
|
websiteId: string,
|
||||||
filters: QueryFilters,
|
filters: QueryFilters,
|
||||||
): Promise<{ x: string; y: number }[]> {
|
): Promise<{ x: string; y: number }[]> {
|
||||||
const { timezone = 'utc', unit = 'day' } = filters;
|
const { timezone = 'UTC', unit = 'day' } = filters;
|
||||||
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
|
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
|
||||||
const { filterQuery, cohortQuery, queryParams } = parseFilters({
|
const { filterQuery, cohortQuery, queryParams } = parseFilters({
|
||||||
...filters,
|
...filters,
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,44 @@
|
||||||
import { Prisma } from '@/generated/prisma/client';
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import prisma from '@/lib/prisma';
|
import prisma from '@/lib/prisma';
|
||||||
|
|
||||||
export async function createSession(data: Prisma.SessionCreateInput) {
|
const FUNCTION_NAME = 'createSession';
|
||||||
const {
|
|
||||||
id,
|
|
||||||
websiteId,
|
|
||||||
browser,
|
|
||||||
os,
|
|
||||||
device,
|
|
||||||
screen,
|
|
||||||
language,
|
|
||||||
country,
|
|
||||||
region,
|
|
||||||
city,
|
|
||||||
distinctId,
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
try {
|
export async function createSession(data: Prisma.SessionCreateInput) {
|
||||||
return await prisma.client.session.create({
|
const { rawQuery } = prisma;
|
||||||
data: {
|
|
||||||
id,
|
await rawQuery(
|
||||||
websiteId,
|
`
|
||||||
browser,
|
insert into session (
|
||||||
os,
|
session_id,
|
||||||
device,
|
website_id,
|
||||||
screen,
|
browser,
|
||||||
language,
|
os,
|
||||||
country,
|
device,
|
||||||
region,
|
screen,
|
||||||
city,
|
language,
|
||||||
distinctId,
|
country,
|
||||||
},
|
region,
|
||||||
});
|
city,
|
||||||
} catch (e: any) {
|
distinct_id,
|
||||||
if (e.message.toLowerCase().includes('unique constraint')) {
|
created_at
|
||||||
return null;
|
)
|
||||||
}
|
values (
|
||||||
throw e;
|
{{id}},
|
||||||
}
|
{{websiteId}},
|
||||||
|
{{browser}},
|
||||||
|
{{os}},
|
||||||
|
{{device}},
|
||||||
|
{{screen}},
|
||||||
|
{{language}},
|
||||||
|
{{country}},
|
||||||
|
{{region}},
|
||||||
|
{{city}},
|
||||||
|
{{distinctId}},
|
||||||
|
{{createdAt}}
|
||||||
|
)
|
||||||
|
on conflict (session_id) do nothing
|
||||||
|
`,
|
||||||
|
data,
|
||||||
|
FUNCTION_NAME,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ async function clickhouseQuery(
|
||||||
websiteId: string,
|
websiteId: string,
|
||||||
filters: QueryFilters,
|
filters: QueryFilters,
|
||||||
): Promise<{ x: string; y: number }[]> {
|
): Promise<{ x: string; y: number }[]> {
|
||||||
const { timezone = 'utc', unit = 'day' } = filters;
|
const { timezone = 'UTC', unit = 'day' } = filters;
|
||||||
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
|
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
|
||||||
const { filterQuery, cohortQuery, queryParams } = parseFilters({
|
const { filterQuery, cohortQuery, queryParams } = parseFilters({
|
||||||
...filters,
|
...filters,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue