mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
fix UTC issues
This commit is contained in:
parent
004ccdc22f
commit
a15d0ca94a
@ -1,13 +1,15 @@
|
|||||||
|
import { useTimezone } from 'components/hooks';
|
||||||
|
import { REALTIME_INTERVAL } from 'lib/constants';
|
||||||
import { RealtimeData } from 'lib/types';
|
import { RealtimeData } from 'lib/types';
|
||||||
import { useApi } from './useApi';
|
import { useApi } from './useApi';
|
||||||
import { REALTIME_INTERVAL } from 'lib/constants';
|
|
||||||
|
|
||||||
export function useRealtime(websiteId: string) {
|
export function useRealtime(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],
|
queryKey: ['realtime', { websiteId, timezone }],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return get(`/realtime/${websiteId}`);
|
return get(`/realtime/${websiteId}`, { timezone });
|
||||||
},
|
},
|
||||||
enabled: !!websiteId,
|
enabled: !!websiteId,
|
||||||
refetchInterval: REALTIME_INTERVAL,
|
refetchInterval: REALTIME_INTERVAL,
|
||||||
|
@ -12,11 +12,11 @@ import { filtersToArray } from './params';
|
|||||||
const log = debug('umami:prisma');
|
const log = debug('umami:prisma');
|
||||||
|
|
||||||
const MYSQL_DATE_FORMATS = {
|
const MYSQL_DATE_FORMATS = {
|
||||||
minute: '%Y-%m-%dT%H:%i:00Z',
|
minute: '%Y-%m-%dT%H:%i:00',
|
||||||
hour: '%Y-%m-%dT%H:00:00Z',
|
hour: '%Y-%m-%d %H:00:00',
|
||||||
day: '%Y-%m-%dT00:00:00Z',
|
day: '%Y-%m-%d',
|
||||||
month: '%Y-%m-01T00:00:00Z',
|
month: '%Y-%m-01',
|
||||||
year: '%Y-01-01T00:00:00Z',
|
year: '%Y-01-01',
|
||||||
};
|
};
|
||||||
|
|
||||||
const POSTGRESQL_DATE_FORMATS = {
|
const POSTGRESQL_DATE_FORMATS = {
|
||||||
@ -82,15 +82,16 @@ function getDateSQL(field: string, unit: string, timezone?: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDateWeeklySQL(field: string) {
|
function getDateWeeklySQL(field: string, timezone?: string) {
|
||||||
const db = getDatabaseType();
|
const db = getDatabaseType();
|
||||||
|
|
||||||
if (db === POSTGRESQL) {
|
if (db === POSTGRESQL) {
|
||||||
return `concat(extract(dow from ${field}), ':', to_char(${field}, 'HH24'))`;
|
return `concat(extract(dow from (${field} at time zone '${timezone}')), ':', to_char((${field} at time zone '${timezone}'), 'HH24'))`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (db === MYSQL) {
|
if (db === MYSQL) {
|
||||||
return `date_format(${field}, '%w:%H')`;
|
const tz = moment.tz(timezone).format('Z');
|
||||||
|
return `date_format(convert_tz(${field},'+00:00','${tz}'), '%w:%H')`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,14 +7,17 @@ import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
|||||||
import { getRealtimeData } from 'queries';
|
import { getRealtimeData } from 'queries';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import { REALTIME_RANGE } from 'lib/constants';
|
import { REALTIME_RANGE } from 'lib/constants';
|
||||||
|
import { TimezoneTest } from 'lib/yup';
|
||||||
|
|
||||||
export interface RealtimeRequestQuery {
|
export interface RealtimeRequestQuery {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
GET: yup.object().shape({
|
GET: yup.object().shape({
|
||||||
websiteId: yup.string().uuid().required(),
|
websiteId: yup.string().uuid().required(),
|
||||||
|
timezone: TimezoneTest,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +26,7 @@ export default async (req: NextApiRequestQueryBody<RealtimeRequestQuery>, res: N
|
|||||||
await useValidate(schema, req, res);
|
await useValidate(schema, req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { websiteId } = req.query;
|
const { websiteId, timezone } = req.query;
|
||||||
|
|
||||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
@ -31,7 +34,7 @@ export default async (req: NextApiRequestQueryBody<RealtimeRequestQuery>, res: N
|
|||||||
|
|
||||||
const startDate = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
|
const startDate = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
|
||||||
|
|
||||||
const data = await getRealtimeData(websiteId, { startDate });
|
const data = await getRealtimeData(websiteId, { startDate, timezone });
|
||||||
|
|
||||||
return ok(res, data);
|
return ok(res, data);
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import { NextApiResponse } from 'next';
|
|||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { pageInfo } from 'lib/schema';
|
import { pageInfo } from 'lib/schema';
|
||||||
import { getWebsiteSessionsWeekly } from 'queries';
|
import { getWebsiteSessionsWeekly } from 'queries';
|
||||||
|
import { TimezoneTest } from 'lib/yup';
|
||||||
|
|
||||||
export interface ReportsRequestQuery extends PageParams {
|
export interface ReportsRequestQuery extends PageParams {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
@ -16,6 +18,7 @@ const schema = {
|
|||||||
websiteId: yup.string().uuid().required(),
|
websiteId: yup.string().uuid().required(),
|
||||||
startAt: yup.number().integer().required(),
|
startAt: yup.number().integer().required(),
|
||||||
endAt: yup.number().integer().min(yup.ref('startAt')).required(),
|
endAt: yup.number().integer().min(yup.ref('startAt')).required(),
|
||||||
|
timezone: TimezoneTest,
|
||||||
...pageInfo,
|
...pageInfo,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -28,7 +31,7 @@ export default async (
|
|||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
await useValidate(schema, req, res);
|
await useValidate(schema, req, res);
|
||||||
|
|
||||||
const { websiteId, startAt, endAt } = req.query;
|
const { websiteId, startAt, endAt, timezone } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||||
@ -38,7 +41,7 @@ export default async (
|
|||||||
const startDate = new Date(+startAt);
|
const startDate = new Date(+startAt);
|
||||||
const endDate = new Date(+endAt);
|
const endDate = new Date(+endAt);
|
||||||
|
|
||||||
const data = await getWebsiteSessionsWeekly(websiteId, { startDate, endDate });
|
const data = await getWebsiteSessionsWeekly(websiteId, { startDate, endDate, timezone });
|
||||||
|
|
||||||
return ok(res, data);
|
return ok(res, data);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getRealtimeActivity, getPageviewStats, getSessionStats } from 'queries/index';
|
import { getPageviewStats, getRealtimeActivity, getSessionStats } from 'queries/index';
|
||||||
|
|
||||||
function increment(data: object, key: string) {
|
function increment(data: object, key: string) {
|
||||||
if (key) {
|
if (key) {
|
||||||
@ -10,9 +10,12 @@ function increment(data: object, key: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRealtimeData(websiteId: string, criteria: { startDate: Date }) {
|
export async function getRealtimeData(
|
||||||
const { startDate } = criteria;
|
websiteId: string,
|
||||||
const filters = { startDate, endDate: new Date(), unit: 'minute' };
|
criteria: { startDate: Date; timezone: string },
|
||||||
|
) {
|
||||||
|
const { startDate, timezone } = criteria;
|
||||||
|
const filters = { startDate, endDate: new Date(), unit: 'minute', timezone };
|
||||||
const [activity, pageviews, sessions] = await Promise.all([
|
const [activity, pageviews, sessions] = await Promise.all([
|
||||||
getRealtimeActivity(websiteId, filters),
|
getRealtimeActivity(websiteId, filters),
|
||||||
getPageviewStats(websiteId, filters),
|
getPageviewStats(websiteId, filters),
|
||||||
|
@ -13,13 +13,14 @@ export async function getWebsiteSessionsWeekly(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||||
|
const { timezone = 'utc' } = filters;
|
||||||
const { rawQuery, getDateWeeklySQL, parseFilters } = prisma;
|
const { rawQuery, getDateWeeklySQL, parseFilters } = prisma;
|
||||||
const { params } = await parseFilters(websiteId, filters);
|
const { params } = await parseFilters(websiteId, filters);
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
${getDateWeeklySQL('created_at')} as time,
|
${getDateWeeklySQL('created_at', timezone)} as time,
|
||||||
count(distinct session_id) as value
|
count(distinct session_id) as value
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {{websiteId::uuid}}
|
where website_id = {{websiteId::uuid}}
|
||||||
@ -32,13 +33,14 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
||||||
|
const { timezone = 'utc' } = filters;
|
||||||
const { rawQuery } = clickhouse;
|
const { rawQuery } = clickhouse;
|
||||||
const { startDate, endDate } = filters;
|
const { startDate, endDate } = filters;
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
formatDateTime(created_at, '%w:%H') as time,
|
formatDateTime(toDateTime(created_at, '${timezone}'), '%w:%H') as time,
|
||||||
count(distinct session_id) as value
|
count(distinct session_id) as value
|
||||||
from website_event_stats_hourly
|
from website_event_stats_hourly
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user