Merge branch 'feat/um-285-report-schema' into dev

This commit is contained in:
Brian Cao 2023-05-18 13:21:35 -07:00
commit 40f53e8856
29 changed files with 1007 additions and 14 deletions

View file

@ -1,6 +1,6 @@
import debug from 'debug';
import { UserReport } from '@prisma/client';
import redis from '@umami/redis-client';
import cache from 'lib/cache';
import debug from 'debug';
import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants';
import { secret } from 'lib/crypto';
import {
@ -10,11 +10,11 @@ import {
parseSecureToken,
parseToken,
} from 'next-basics';
import { getTeamUser, getTeamUserById } from 'queries';
import { getTeamUser } from 'queries';
import { getTeamWebsite, getTeamWebsiteByTeamMemberId } from 'queries/admin/teamWebsite';
import { validate } from 'uuid';
import { Auth } from './types';
import { loadWebsite } from './query';
import { Auth } from './types';
const log = debug('umami:auth');
@ -135,7 +135,34 @@ export async function canDeleteWebsite({ user }: Auth, websiteId: string) {
return false;
}
// To-do: Implement when payments are setup.
export async function canViewUserReport(auth: Auth, userReport: UserReport) {
if (auth.user.isAdmin) {
return true;
}
if ((auth.user.id = userReport.userId)) {
return true;
}
if (await canViewWebsite(auth, userReport.websiteId)) {
return true;
}
return false;
}
export async function canUpdateUserReport(auth: Auth, userReport: UserReport) {
if (auth.user.isAdmin) {
return true;
}
if ((auth.user.id = userReport.userId)) {
return true;
}
return false;
}
export async function canCreateTeam({ user }: Auth) {
if (user.isAdmin) {
return true;
@ -144,7 +171,6 @@ export async function canCreateTeam({ user }: Auth) {
return !!user;
}
// To-do: Implement when payments are setup.
export async function canViewTeam({ user }: Auth, teamId: string) {
if (user.isAdmin) {
return true;

View file

@ -121,13 +121,36 @@ function getFilterQuery(filters = {}, params = {}) {
return query.join('\n');
}
function getFunnelQuery(urls: string[]): {
columnsQuery: string;
conditionQuery: string;
urlParams: { [key: string]: string };
} {
return urls.reduce(
(pv, cv, i) => {
pv.columnsQuery += `\n,url_path = {url${i}:String}${
i > 0 && urls[i - 1] ? ` AND referrer_path = {url${i - 1}:String}` : ''
}`;
pv.conditionQuery += `${i > 0 ? ',' : ''} {url${i}:String}`;
pv.urlParams[`url${i}`] = cv;
return pv;
},
{
columnsQuery: '',
conditionQuery: '',
urlParams: {},
},
);
}
function parseFilters(filters: WebsiteMetricFilter = {}, params: any = {}) {
return {
filterQuery: getFilterQuery(filters, params),
};
}
async function rawQuery(query, params = {}) {
async function rawQuery<T>(query, params = {}): Promise<T> {
if (process.env.LOG_QUERY) {
log('QUERY:\n', query);
log('PARAMETERS:\n', params);
@ -135,7 +158,7 @@ async function rawQuery(query, params = {}) {
await connect();
return clickhouse.query(query, { params }).toPromise();
return clickhouse.query(query, { params }).toPromise() as Promise<T>;
}
async function findUnique(data) {
@ -168,6 +191,7 @@ export default {
getDateFormat,
getBetweenDates,
getFilterQuery,
getFunnelQuery,
getEventDataFilterQuery,
parseFilters,
findUnique,

View file

@ -32,6 +32,18 @@ function toUuid(): string {
}
}
function getAddMinutesQuery(field: string, minutes: number) {
const db = getDatabaseType(process.env.DATABASE_URL);
if (db === POSTGRESQL) {
return `${field} + interval '${minutes} minute'`;
}
if (db === MYSQL) {
return `DATE_ADD(${field}, interval ${minutes} minute)`;
}
}
function getDateQuery(field: string, unit: string, timezone?: string): string {
const db = getDatabaseType(process.env.DATABASE_URL);
@ -122,6 +134,50 @@ function getFilterQuery(filters = {}, params = []): string {
return query.join('\n');
}
function getFunnelQuery(
urls: string[],
windowMinutes: number,
initParamLength = 3,
): {
levelQuery: string;
sumQuery: string;
urlFilterQuery: string;
} {
return urls.reduce(
(pv, cv, i) => {
const levelNumber = i + 1;
const start = i > 0 ? ',' : '';
if (levelNumber >= 2) {
pv.levelQuery += `\n
, level${levelNumber} AS (
select cl.*,
l0.created_at level_${levelNumber}_created_at,
l0.url_path as level_${levelNumber}_url
from level${i} cl
left join level0 l0
on cl.session_id = l0.session_id
and l0.created_at between cl.level_${i}_created_at
and ${getAddMinutesQuery(`cl.level_${i}_created_at`, windowMinutes)}
and l0.referrer_path = $${i + initParamLength}
and l0.url_path = $${levelNumber + initParamLength}
)`;
}
pv.sumQuery += `\n${start}SUM(CASE WHEN level_${levelNumber}_url is not null THEN 1 ELSE 0 END) AS level${levelNumber}`;
pv.urlFilterQuery += `\n${start}$${levelNumber + initParamLength} `;
return pv;
},
{
levelQuery: '',
sumQuery: '',
urlFilterQuery: '',
},
);
}
function parseFilters(
filters: { [key: string]: any } = {},
params = [],
@ -152,9 +208,11 @@ async function rawQuery(query: string, params: never[] = []): Promise<any> {
export default {
...prisma,
getAddMinutesQuery,
getDateQuery,
getTimestampInterval,
getFilterQuery,
getFunnelQuery,
getEventDataFilterQuery,
toUuid,
parseFilters,