2020-08-12 05:05:40 +02:00
import moment from 'moment-timezone' ;
2020-09-25 08:02:11 +02:00
import prisma from 'lib/db' ;
2020-08-18 09:51:32 +02:00
import { subMinutes } from 'date-fns' ;
2021-03-13 08:45:19 +01:00
import {
MYSQL ,
POSTGRESQL ,
MYSQL _DATE _FORMATS ,
POSTGRESQL _DATE _FORMATS ,
URL _LENGTH ,
} from 'lib/constants' ;
2020-08-30 08:17:32 +02:00
2020-08-12 05:05:40 +02:00
export function getDatabase ( ) {
2020-09-04 20:06:32 +02:00
const type =
2020-08-30 09:02:03 +02:00
process . env . DATABASE _TYPE ||
2020-09-04 20:06:32 +02:00
( process . env . DATABASE _URL && process . env . DATABASE _URL . split ( ':' ) [ 0 ] ) ;
if ( type === 'postgres' ) {
return 'postgresql' ;
}
return type ;
2020-08-12 05:05:40 +02:00
}
2020-09-25 08:02:11 +02:00
export function getDateQuery ( field , unit , timezone ) {
const db = getDatabase ( ) ;
2020-08-30 08:17:32 +02:00
if ( db === POSTGRESQL ) {
if ( timezone ) {
return ` 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 ] } ') ` ;
2020-08-28 08:45:37 +02:00
}
2020-08-30 08:17:32 +02:00
if ( db === MYSQL ) {
if ( timezone ) {
const tz = moment . tz ( timezone ) . format ( 'Z' ) ;
return ` DATE_FORMAT(convert_tz( ${ field } ,'+00:00',' ${ tz } '), ' ${ MYSQL _DATE _FORMATS [ unit ] } ') ` ;
}
return ` DATE_FORMAT( ${ field } , ' ${ MYSQL _DATE _FORMATS [ unit ] } ') ` ;
}
2020-08-28 08:45:37 +02:00
}
2020-09-25 08:02:11 +02:00
export function getTimestampInterval ( field ) {
const db = getDatabase ( ) ;
if ( db === POSTGRESQL ) {
return ` floor(extract(epoch from max( ${ field } ) - min( ${ field } ))) ` ;
}
if ( db === MYSQL ) {
return ` floor(unix_timestamp(max( ${ field } )) - unix_timestamp(min( ${ field } ))) ` ;
}
}
2022-04-10 12:51:43 +02:00
export function getFilterQuery ( table , filters = { } , params = [ ] ) {
const query = Object . keys ( filters ) . reduce ( ( arr , key ) => {
const value = filters [ key ] ;
if ( value === undefined ) {
return arr ;
}
switch ( key ) {
case 'url' :
2022-04-12 11:14:45 +02:00
if ( table === 'session' || table === 'pageview' || table === 'event' ) {
2022-04-14 12:09:26 +02:00
arr . push ( ` and ( ${ table } . ${ key } = $ ${ params . length + 1 } or ${ table } . ${ key } like $ ${ params . length + 2 } or ( ${ table } . ${ key } like $ ${ params . length + 3 } and ${ table } . ${ key } not like $ ${ params . length + 4 } )) ` ) ;
2022-04-10 12:51:43 +02:00
params . push ( decodeURIComponent ( value ) ) ;
2022-04-12 11:14:45 +02:00
params . push ( ` ${ decodeURIComponent ( value ) } ?% ` ) ;
2022-04-14 12:09:26 +02:00
params . push ( ` % ${ decodeURIComponent ( value ) } #% ` ) ;
params . push ( ` % ${ decodeURIComponent ( value ) } #/% ` ) ;
2022-04-10 12:51:43 +02:00
}
break ;
case 'os' :
case 'browser' :
case 'device' :
case 'country' :
if ( table === 'session' ) {
arr . push ( ` and ${ table } . ${ key } = $ ${ params . length + 1 } ` ) ;
params . push ( decodeURIComponent ( value ) ) ;
}
break ;
case 'event_type' :
if ( table === 'event' ) {
arr . push ( ` and ${ table } . ${ key } = $ ${ params . length + 1 } ` ) ;
params . push ( decodeURIComponent ( value ) ) ;
}
break ;
case 'referrer' :
if ( table === 'pageview' ) {
arr . push ( ` and ${ table } .referrer like $ ${ params . length + 1 } ` ) ;
params . push ( ` % ${ decodeURIComponent ( value ) } % ` ) ;
}
break ;
case 'domain' :
if ( table === 'pageview' ) {
arr . push ( ` and ${ table } .referrer not like $ ${ params . length + 1 } ` ) ;
arr . push ( ` and ${ table } .referrer not like '/%' ` ) ;
params . push ( ` %:// ${ value } /% ` ) ;
}
break ;
}
return arr ;
} , [ ] ) ;
return query . join ( '\n' ) ;
}
2022-04-11 09:19:29 +02:00
export function parseFilters ( table , filters = { } , params = [ ] ) {
const { domain , url , referrer , os , browser , device , country , event _type } = filters ;
const pageviewFilters = { domain , url , referrer } ;
const sessionFilters = { os , browser , device , country } ;
const eventFilters = { event _type } ;
return {
pageviewFilters ,
sessionFilters ,
eventFilters ,
event : { event _type } ,
joinSession :
os || browser || device || country
? ` inner join session on ${ table } .session_id = session.session_id `
: '' ,
2022-04-12 11:14:45 +02:00
pageviewQuery : table === 'event' ? getFilterQuery ( 'event' , pageviewFilters , params ) : getFilterQuery ( 'pageview' , pageviewFilters , params ) ,
2022-04-11 09:19:29 +02:00
sessionQuery : getFilterQuery ( 'session' , sessionFilters , params ) ,
eventQuery : getFilterQuery ( 'event' , eventFilters , params ) ,
} ;
}
2022-04-10 12:51:43 +02:00
export async function runQuery ( query ) {
return query . catch ( e => {
throw e ;
} ) ;
}
export async function rawQuery ( query , params = [ ] ) {
const db = getDatabase ( ) ;
if ( db !== POSTGRESQL && db !== MYSQL ) {
return Promise . reject ( new Error ( 'Unknown database.' ) ) ;
}
const sql = db === MYSQL ? query . replace ( /\$[0-9]+/g , '?' ) : query ;
return runQuery ( prisma . $queryRawUnsafe . apply ( prisma , [ sql , ... params ] ) ) ;
}
2020-08-12 07:24:41 +02:00
export async function getWebsiteById ( website _id ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . website . findUnique ( {
2020-08-12 07:24:41 +02:00
where : {
website _id ,
} ,
} ) ,
) ;
}
export async function getWebsiteByUuid ( website _uuid ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . website . findUnique ( {
2020-08-12 07:24:41 +02:00
where : {
website _uuid ,
} ,
} ) ,
) ;
}
2020-08-15 10:17:15 +02:00
export async function getWebsiteByShareId ( share _id ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . website . findUnique ( {
2020-08-15 10:17:15 +02:00
where : {
share _id ,
} ,
} ) ,
) ;
}
2020-08-12 07:24:41 +02:00
export async function getUserWebsites ( user _id ) {
return runQuery (
prisma . website . findMany ( {
where : {
user _id ,
} ,
orderBy : {
name : 'asc' ,
} ,
} ) ,
) ;
}
2021-12-02 01:03:18 +01:00
export async function getAllWebsites ( ) {
let data = await runQuery (
prisma . website . findMany ( {
orderBy : [
{
user _id : 'asc' ,
} ,
{
name : 'asc' ,
} ,
] ,
include : {
account : {
select : {
username : true ,
} ,
} ,
} ,
} ) ,
) ;
return data . map ( i => ( { ... i , account : i . account . username } ) ) ;
}
2020-08-12 07:24:41 +02:00
export async function createWebsite ( user _id , data ) {
return runQuery (
prisma . website . create ( {
data : {
account : {
connect : {
user _id ,
} ,
} ,
... data ,
} ,
} ) ,
) ;
}
export async function updateWebsite ( website _id , data ) {
return runQuery (
prisma . website . update ( {
where : {
website _id ,
} ,
data ,
} ) ,
) ;
}
2021-08-10 23:03:55 +02:00
export async function resetWebsite ( website _id ) {
return runQuery ( prisma . $queryRaw ` delete from session where website_id= ${ website _id } ` ) ;
}
2020-08-12 07:24:41 +02:00
export async function deleteWebsite ( website _id ) {
return runQuery (
prisma . website . delete ( {
where : {
website _id ,
} ,
} ) ,
) ;
}
export async function createSession ( website _id , data ) {
return runQuery (
prisma . session . create ( {
data : {
2021-03-25 13:44:37 +01:00
website _id ,
2020-08-12 07:24:41 +02:00
... data ,
} ,
select : {
session _id : true ,
} ,
} ) ,
) ;
}
export async function getSessionByUuid ( session _uuid ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . session . findUnique ( {
2020-08-12 07:24:41 +02:00
where : {
session _uuid ,
} ,
} ) ,
) ;
}
export async function savePageView ( website _id , session _id , url , referrer ) {
return runQuery (
prisma . pageview . create ( {
data : {
2021-03-25 13:44:37 +01:00
website _id ,
session _id ,
2021-03-27 04:56:08 +01:00
url : url ? . substr ( 0 , URL _LENGTH ) ,
referrer : referrer ? . substr ( 0 , URL _LENGTH ) ,
2020-08-12 07:24:41 +02:00
} ,
} ) ,
) ;
}
export async function saveEvent ( website _id , session _id , url , event _type , event _value ) {
return runQuery (
prisma . event . create ( {
data : {
2021-03-25 13:44:37 +01:00
website _id ,
session _id ,
2021-03-27 04:56:08 +01:00
url : url ? . substr ( 0 , URL _LENGTH ) ,
event _type : event _type ? . substr ( 0 , 50 ) ,
event _value : event _value ? . substr ( 0 , 50 ) ,
2020-08-12 07:24:41 +02:00
} ,
} ) ,
) ;
}
export async function getAccounts ( ) {
2022-04-04 07:25:32 +02:00
return runQuery (
prisma . account . findMany ( {
orderBy : [
{ is _admin : 'desc' } ,
{
username : 'asc' ,
} ,
] ,
} ) ,
) ;
2020-08-12 07:24:41 +02:00
}
export async function getAccountById ( user _id ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . account . findUnique ( {
2020-08-12 07:24:41 +02:00
where : {
user _id ,
} ,
} ) ,
) ;
}
export async function getAccountByUsername ( username ) {
return runQuery (
2020-12-01 21:29:36 +01:00
prisma . account . findUnique ( {
2020-08-12 07:24:41 +02:00
where : {
username ,
} ,
} ) ,
) ;
}
export async function updateAccount ( user _id , data ) {
return runQuery (
prisma . account . update ( {
where : {
user _id ,
} ,
data ,
} ) ,
) ;
}
export async function deleteAccount ( user _id ) {
return runQuery (
prisma . account . delete ( {
where : {
user _id ,
} ,
} ) ,
) ;
}
export async function createAccount ( data ) {
return runQuery (
prisma . account . create ( {
data ,
} ) ,
) ;
}
2020-10-09 08:26:05 +02:00
export async function getSessions ( websites , start _at ) {
return runQuery (
prisma . session . findMany ( {
where : {
website : {
website _id : {
in : websites ,
} ,
} ,
created _at : {
gte : start _at ,
} ,
} ,
} ) ,
) ;
}
export async function getPageviews ( websites , start _at ) {
return runQuery (
prisma . pageview . findMany ( {
where : {
website : {
website _id : {
in : websites ,
} ,
} ,
created _at : {
gte : start _at ,
} ,
} ,
} ) ,
) ;
}
export async function getEvents ( websites , start _at ) {
return runQuery (
prisma . event . findMany ( {
where : {
website : {
website _id : {
in : websites ,
} ,
} ,
created _at : {
gte : start _at ,
} ,
} ,
} ) ,
) ;
}
2020-10-09 00:02:48 +02:00
export function getWebsiteStats ( website _id , start _at , end _at , filters = { } ) {
2020-09-26 07:31:18 +02:00
const params = [ website _id , start _at , end _at ] ;
2022-04-11 09:19:29 +02:00
const { pageviewQuery , sessionQuery , joinSession } = parseFilters ( 'pageview' , filters , params ) ;
2021-11-22 07:00:14 +01:00
2020-09-25 08:02:11 +02:00
return rawQuery (
`
2020-08-12 05:05:40 +02:00
select sum ( t . c ) as "pageviews" ,
count ( distinct t . session _id ) as "uniques" ,
2020-08-24 19:52:47 +02:00
sum ( case when t . c = 1 then 1 else 0 end ) as "bounces" ,
2020-08-12 05:05:40 +02:00
sum ( t . time ) as "totaltime"
from (
2022-04-10 12:51:43 +02:00
select pageview . session _id ,
$ { getDateQuery ( 'pageview.created_at' , 'hour' ) } ,
2020-08-12 05:05:40 +02:00
count ( * ) c ,
2022-04-10 12:51:43 +02:00
$ { getTimestampInterval ( 'pageview.created_at' ) } as "time"
2020-08-12 05:05:40 +02:00
from pageview
2022-04-10 12:51:43 +02:00
$ { joinSession }
where pageview . website _id = $1
and pageview . created _at between $2 and $3
2022-04-11 09:19:29 +02:00
$ { pageviewQuery }
$ { sessionQuery }
2020-08-12 05:05:40 +02:00
group by 1 , 2
) t
2020-09-25 08:02:11 +02:00
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 08:02:11 +02:00
) ;
2020-08-12 05:05:40 +02:00
}
2020-10-09 00:02:48 +02:00
export function getPageviewStats (
2020-08-12 07:24:41 +02:00
website _id ,
start _at ,
end _at ,
timezone = 'utc' ,
unit = 'day' ,
count = '*' ,
2021-11-22 07:00:14 +01:00
filters = { } ,
2020-08-12 07:24:41 +02:00
) {
2020-09-26 07:31:18 +02:00
const params = [ website _id , start _at , end _at ] ;
2022-04-11 09:19:29 +02:00
const { pageviewQuery , sessionQuery , joinSession } = parseFilters ( 'pageview' , filters , params ) ;
2021-11-22 07:00:14 +01:00
2020-09-25 08:02:11 +02:00
return rawQuery (
`
2022-04-10 12:51:43 +02:00
select $ { getDateQuery ( 'pageview.created_at' , unit , timezone ) } t ,
2020-09-25 08:02:11 +02:00
count ( $ { count } ) y
from pageview
2022-04-10 12:51:43 +02:00
$ { joinSession }
where pageview . website _id = $1
and pageview . created _at between $2 and $3
2022-04-11 09:19:29 +02:00
$ { pageviewQuery }
$ { sessionQuery }
2020-09-25 08:02:11 +02:00
group by 1
order by 1
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 08:02:11 +02:00
) ;
2020-08-12 05:05:40 +02:00
}
2020-09-29 05:23:42 +02:00
export function getSessionMetrics ( website _id , start _at , end _at , field , filters = { } ) {
2020-09-26 07:31:18 +02:00
const params = [ website _id , start _at , end _at ] ;
2022-04-11 09:19:29 +02:00
const { pageviewQuery , sessionQuery , joinSession } = parseFilters ( 'pageview' , filters , params ) ;
2022-04-07 15:33:01 +02:00
2020-09-25 09:15:58 +02:00
return rawQuery (
`
select $ { field } x , count ( * ) y
2022-04-10 12:51:43 +02:00
from session as x
where x . session _id in (
select pageview . session _id
2020-09-25 09:15:58 +02:00
from pageview
2022-04-10 12:51:43 +02:00
$ { joinSession }
where pageview . website _id = $1
and pageview . created _at between $2 and $3
2022-04-11 09:19:29 +02:00
$ { pageviewQuery }
$ { sessionQuery }
2020-09-25 09:15:58 +02:00
)
group by 1
order by 2 desc
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 09:15:58 +02:00
) ;
}
2020-09-29 05:23:42 +02:00
export function getPageviewMetrics ( website _id , start _at , end _at , field , table , filters = { } ) {
2020-09-26 07:31:18 +02:00
const params = [ website _id , start _at , end _at ] ;
2022-04-11 09:19:29 +02:00
console . log ( { table , filters } ) ;
const { pageviewQuery , sessionQuery , joinSession } = parseFilters ( table , filters , params ) ;
2022-03-20 22:14:54 +01:00
2020-09-25 08:02:11 +02:00
return rawQuery (
`
2020-09-25 09:15:58 +02:00
select $ { field } x , count ( * ) y
2020-09-25 08:02:11 +02:00
from $ { table }
2022-04-10 12:51:43 +02:00
$ { joinSession }
where $ { table } . website _id = $1
and $ { table } . created _at between $2 and $3
2022-04-11 09:19:29 +02:00
$ { pageviewQuery }
$ { joinSession && sessionQuery }
2020-09-25 08:02:11 +02:00
group by 1
order by 2 desc
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 08:02:11 +02:00
) ;
2020-08-12 05:05:40 +02:00
}
2020-08-18 09:51:32 +02:00
export function getActiveVisitors ( website _id ) {
2020-08-19 03:33:59 +02:00
const date = subMinutes ( new Date ( ) , 5 ) ;
2020-09-26 07:31:18 +02:00
const params = [ website _id , date ] ;
2020-08-19 03:33:59 +02:00
2020-09-25 08:02:11 +02:00
return rawQuery (
`
2020-08-18 09:51:32 +02:00
select count ( distinct session _id ) x
from pageview
where website _id = $1
and created _at >= $2
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 08:02:11 +02:00
) ;
2020-08-18 09:51:32 +02:00
}
2020-08-25 08:49:14 +02:00
2020-10-09 08:26:05 +02:00
export function getEventMetrics (
2020-09-29 05:23:42 +02:00
website _id ,
start _at ,
end _at ,
timezone = 'utc' ,
unit = 'day' ,
filters = { } ,
) {
2020-09-26 07:31:18 +02:00
const params = [ website _id , start _at , end _at ] ;
2021-09-29 10:38:52 +02:00
2020-09-25 08:02:11 +02:00
return rawQuery (
`
select
event _value x ,
$ { getDateQuery ( 'created_at' , unit , timezone ) } t ,
count ( * ) y
from event
where website _id = $1
and created _at between $2 and $3
2022-04-10 12:51:43 +02:00
$ { getFilterQuery ( 'event' , filters , params ) }
2020-09-25 08:02:11 +02:00
group by 1 , 2
order by 2
` ,
2020-09-26 07:31:18 +02:00
params ,
2020-09-25 08:02:11 +02:00
) ;
2020-08-27 12:42:24 +02:00
}
2020-10-10 20:04:07 +02:00
export async function getRealtimeData ( websites , time ) {
const [ pageviews , sessions , events ] = await Promise . all ( [
getPageviews ( websites , time ) ,
getSessions ( websites , time ) ,
getEvents ( websites , time ) ,
] ) ;
return {
pageviews : pageviews . map ( ( { view _id , ... props } ) => ( {
_ _id : ` p ${ view _id } ` ,
view _id ,
... props ,
} ) ) ,
sessions : sessions . map ( ( { session _id , ... props } ) => ( {
_ _id : ` s ${ session _id } ` ,
session _id ,
... props ,
} ) ) ,
events : events . map ( ( { event _id , ... props } ) => ( {
_ _id : ` e ${ event _id } ` ,
event _id ,
... props ,
} ) ) ,
timestamp : Date . now ( ) ,
} ;
}