mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-22 09:57:00 +01:00
Fixed realtime chart rendering.
This commit is contained in:
parent
7a5f28870f
commit
92a513e4d0
@ -19,9 +19,9 @@ import { RealtimeData } from 'lib/types';
|
|||||||
import styles from './Realtime.module.css';
|
import styles from './Realtime.module.css';
|
||||||
|
|
||||||
function mergeData(state = [], data = [], time: number) {
|
function mergeData(state = [], data = [], time: number) {
|
||||||
const ids = state.map(({ __id }) => __id);
|
const ids = state.map(({ id }) => id);
|
||||||
return state
|
return state
|
||||||
.concat(data.filter(({ __id }) => !ids.includes(__id)))
|
.concat(data.filter(({ id }) => !ids.includes(id)))
|
||||||
.filter(({ timestamp }) => timestamp >= time);
|
.filter(({ timestamp }) => timestamp >= time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,21 +38,18 @@ export function Realtime({ websiteId }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
if (!currentData) {
|
const date = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
|
||||||
setCurrentData(data);
|
const time = date.getTime();
|
||||||
} else {
|
const { pageviews, sessions, events, timestamp } = data;
|
||||||
const date = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
|
|
||||||
const time = date.getTime();
|
|
||||||
|
|
||||||
setCurrentData(state => ({
|
setCurrentData(state => ({
|
||||||
pageviews: mergeData(state?.pageviews, data.pageviews, time),
|
pageviews: mergeData(state?.pageviews, pageviews, time),
|
||||||
sessions: mergeData(state?.sessions, data.sessions, time),
|
sessions: mergeData(state?.sessions, sessions, time),
|
||||||
events: mergeData(state?.events, data.events, time),
|
events: mergeData(state?.events, events, time),
|
||||||
timestamp: data.timestamp,
|
timestamp,
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [data]);
|
}, [data, currentData]);
|
||||||
|
|
||||||
const realtimeData: RealtimeData = useMemo(() => {
|
const realtimeData: RealtimeData = useMemo(() => {
|
||||||
if (!currentData) {
|
if (!currentData) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { subMinutes } from 'date-fns';
|
import { startOfMinute, subMinutes } from 'date-fns';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useValidate } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
|
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
|
||||||
@ -6,6 +6,8 @@ import { NextApiResponse } from 'next';
|
|||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
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';
|
||||||
|
|
||||||
export interface RealtimeRequestQuery {
|
export interface RealtimeRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
startAt: number;
|
startAt: number;
|
||||||
@ -32,7 +34,7 @@ export default async (
|
|||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = subMinutes(new Date(), 30);
|
let startTime = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
|
||||||
|
|
||||||
if (+startAt > startTime.getTime()) {
|
if (+startAt > startTime.getTime()) {
|
||||||
startTime = new Date(+startAt);
|
startTime = new Date(+startAt);
|
||||||
|
@ -18,6 +18,9 @@ function relationalQuery(websiteId: string, startDate: Date, eventType: number)
|
|||||||
gte: startDate,
|
gte: startDate,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'asc',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ function clickhouseQuery(websiteId: string, startDate: Date, eventType: number)
|
|||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at >= {startDate:DateTime64}
|
and created_at >= {startDate:DateTime64}
|
||||||
and event_type = {eventType:UInt32}
|
and event_type = {eventType:UInt32}
|
||||||
|
order by created_at asc
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
websiteId,
|
websiteId,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { md5 } from 'next-basics';
|
|
||||||
import { getSessions, getEvents } from 'queries/index';
|
import { getSessions, getEvents } from 'queries/index';
|
||||||
import { EVENT_TYPE } from 'lib/constants';
|
import { EVENT_TYPE } from 'lib/constants';
|
||||||
|
|
||||||
@ -9,18 +8,35 @@ export async function getRealtimeData(websiteId: string, startDate: Date) {
|
|||||||
getEvents(websiteId, startDate, EVENT_TYPE.customEvent),
|
getEvents(websiteId, startDate, EVENT_TYPE.customEvent),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const decorate = (id: string, data: any[]) => {
|
const decorate = (type: string, data: any[]) => {
|
||||||
return data.map((props: { [key: string]: any }) => ({
|
return data.map((values: { [key: string]: any }) => ({
|
||||||
...props,
|
...values,
|
||||||
__id: md5(id, ...Object.values(props)),
|
__type: type,
|
||||||
__type: id,
|
timestamp: values.timestamp ? values.timestamp * 1000 : new Date(values.createdAt).getTime(),
|
||||||
timestamp: props.timestamp ? props.timestamp * 1000 : new Date(props.createdAt).getTime(),
|
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const set = new Set();
|
||||||
|
const uniques = (type: string, data: any[]) => {
|
||||||
|
return data.reduce((arr, values: { [key: string]: any }) => {
|
||||||
|
if (!set.has(values.id)) {
|
||||||
|
set.add(values.id);
|
||||||
|
|
||||||
|
return arr.concat({
|
||||||
|
...values,
|
||||||
|
__type: type,
|
||||||
|
timestamp: values.timestamp
|
||||||
|
? values.timestamp * 1000
|
||||||
|
: new Date(values.createdAt).getTime(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageviews: decorate('pageview', pageviews),
|
pageviews: decorate('pageview', pageviews),
|
||||||
sessions: decorate('session', sessions),
|
sessions: uniques('session', sessions),
|
||||||
events: decorate('event', events),
|
events: decorate('event', events),
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,9 @@ async function relationalQuery(websiteId: string, startDate: Date) {
|
|||||||
gte: startDate,
|
gte: startDate,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'asc',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +28,7 @@ async function clickhouseQuery(websiteId: string, startDate: Date) {
|
|||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select distinct
|
select
|
||||||
session_id as id,
|
session_id as id,
|
||||||
website_id as websiteId,
|
website_id as websiteId,
|
||||||
created_at as createdAt,
|
created_at as createdAt,
|
||||||
@ -43,6 +46,7 @@ async function clickhouseQuery(websiteId: string, startDate: Date) {
|
|||||||
from website_event
|
from website_event
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at >= {startDate:DateTime64}
|
and created_at >= {startDate:DateTime64}
|
||||||
|
order by created_at asc
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
websiteId,
|
websiteId,
|
||||||
|
Loading…
Reference in New Issue
Block a user