mirror of
https://github.com/umami-software/umami.git
synced 2026-02-07 22:27:16 +01:00
Merge branch 'dev' of https://github.com/umami-software/umami into feat/um-290-update-clickhouse-client
This commit is contained in:
commit
8e240af9e8
325 changed files with 2853 additions and 3296 deletions
|
|
@ -19,6 +19,7 @@ export const DEFAULT_ANIMATION_DURATION = 300;
|
|||
export const DEFAULT_DATE_RANGE = '24hour';
|
||||
export const DEFAULT_WEBSITE_LIMIT = 10;
|
||||
export const DEFAULT_RESET_DATE = '2000-01-01';
|
||||
export const DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
export const REALTIME_RANGE = 30;
|
||||
export const REALTIME_INTERVAL = 5000;
|
||||
|
|
@ -29,23 +30,7 @@ export const FILTER_DAY = 'filter-day';
|
|||
export const FILTER_RANGE = 'filter-range';
|
||||
export const FILTER_REFERRERS = 'filter-referrers';
|
||||
export const FILTER_PAGES = 'filter-pages';
|
||||
|
||||
export const USER_FILTER_TYPES = {
|
||||
all: 'All',
|
||||
username: 'Username',
|
||||
} as const;
|
||||
export const WEBSITE_FILTER_TYPES = { all: 'All', name: 'Name', domain: 'Domain' } as const;
|
||||
export const TEAM_FILTER_TYPES = { all: 'All', name: 'Name', 'user:username': 'Owner' } as const;
|
||||
export const REPORT_FILTER_TYPES = {
|
||||
all: 'All',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
type: 'Type',
|
||||
'user:username': 'Username',
|
||||
'website:name': 'Website Name',
|
||||
'website:domain': 'Website Domain',
|
||||
} as const;
|
||||
|
||||
export const UNIT_TYPES = ['year', 'month', 'hour', 'day'];
|
||||
export const EVENT_COLUMNS = ['url', 'referrer', 'title', 'query', 'event'];
|
||||
|
||||
export const SESSION_COLUMNS = [
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
} from 'next-basics';
|
||||
import { NextApiRequestCollect } from 'pages/api/send';
|
||||
import { getUserById } from '../queries';
|
||||
import { NextApiRequestQueryBody } from './types';
|
||||
|
||||
const log = debug('umami:middleware');
|
||||
|
||||
|
|
@ -83,14 +82,18 @@ export const useAuth = createMiddleware(async (req, res, next) => {
|
|||
next();
|
||||
});
|
||||
|
||||
export const useValidate = createMiddleware(async (req: any, res, next) => {
|
||||
try {
|
||||
const { yup } = req as NextApiRequestQueryBody;
|
||||
export const useValidate = async (schema, req, res) => {
|
||||
return createMiddleware(async (req: any, res, next) => {
|
||||
try {
|
||||
const rules = schema[req.method];
|
||||
|
||||
yup[req.method] && yup[req.method].validateSync({ ...req.query, ...req.body });
|
||||
} catch (e: any) {
|
||||
return badRequest(res, e.message);
|
||||
}
|
||||
if (rules) {
|
||||
rules.validateSync(req.method === 'GET' ? { ...req.query } : { ...req.body });
|
||||
}
|
||||
} catch (e: any) {
|
||||
return badRequest(res, e.message);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
next();
|
||||
})(req, res);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import prisma from '@umami/prisma-client';
|
||||
import moment from 'moment-timezone';
|
||||
import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
|
||||
import { FILTER_COLUMNS, SESSION_COLUMNS, OPERATORS } from './constants';
|
||||
import { FILTER_COLUMNS, SESSION_COLUMNS, OPERATORS, DEFAULT_PAGE_SIZE } from './constants';
|
||||
import { loadWebsite } from './load';
|
||||
import { maxDate } from './date';
|
||||
import { QueryFilters, QueryOptions, SearchFilter } from './types';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
const MYSQL_DATE_FORMATS = {
|
||||
minute: '%Y-%m-%d %H:%i:00',
|
||||
|
|
@ -171,7 +171,7 @@ async function rawQuery(sql: string, data: object): Promise<any> {
|
|||
return prisma.rawQuery(query, params);
|
||||
}
|
||||
|
||||
function getPageFilters(filters: SearchFilter<any>): [
|
||||
function getPageFilters(filters: SearchFilter): [
|
||||
{
|
||||
orderBy: {
|
||||
[x: string]: string;
|
||||
|
|
@ -185,20 +185,20 @@ function getPageFilters(filters: SearchFilter<any>): [
|
|||
orderBy: string;
|
||||
},
|
||||
] {
|
||||
const { pageSize = 10, page = 1, orderBy } = filters || {};
|
||||
const { page = 1, pageSize = DEFAULT_PAGE_SIZE, orderBy, sortDescending = false } = filters || {};
|
||||
|
||||
return [
|
||||
{
|
||||
...(pageSize > 0 && { take: pageSize, skip: pageSize * (page - 1) }),
|
||||
...(pageSize > 0 && { take: +pageSize, skip: +pageSize * (page - 1) }),
|
||||
...(orderBy && {
|
||||
orderBy: [
|
||||
{
|
||||
[orderBy]: 'asc',
|
||||
[orderBy]: sortDescending ? 'desc' : 'asc',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{ pageSize, page: +page, orderBy },
|
||||
{ page: +page, pageSize, orderBy },
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
13
src/lib/schema.ts
Normal file
13
src/lib/schema.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import * as yup from 'yup';
|
||||
|
||||
export const dateRange = {
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
};
|
||||
|
||||
export const pageInfo = {
|
||||
query: yup.string(),
|
||||
page: yup.number().integer().positive(),
|
||||
pageSize: yup.number().integer().positive().max(200),
|
||||
orderBy: yup.string(),
|
||||
};
|
||||
|
|
@ -5,12 +5,8 @@ import {
|
|||
EVENT_TYPE,
|
||||
KAFKA_TOPIC,
|
||||
PERMISSIONS,
|
||||
REPORT_FILTER_TYPES,
|
||||
REPORT_TYPES,
|
||||
ROLES,
|
||||
TEAM_FILTER_TYPES,
|
||||
USER_FILTER_TYPES,
|
||||
WEBSITE_FILTER_TYPES,
|
||||
} from './constants';
|
||||
import * as yup from 'yup';
|
||||
import { TIME_UNIT } from './date';
|
||||
|
|
@ -27,46 +23,42 @@ export type DynamicDataType = ObjectValues<typeof DATA_TYPE>;
|
|||
export type KafkaTopic = ObjectValues<typeof KAFKA_TOPIC>;
|
||||
export type ReportType = ObjectValues<typeof REPORT_TYPES>;
|
||||
|
||||
export type ReportSearchFilterType = ObjectValues<typeof REPORT_FILTER_TYPES>;
|
||||
export type UserSearchFilterType = ObjectValues<typeof USER_FILTER_TYPES>;
|
||||
export type WebsiteSearchFilterType = ObjectValues<typeof WEBSITE_FILTER_TYPES>;
|
||||
export type TeamSearchFilterType = ObjectValues<typeof TEAM_FILTER_TYPES>;
|
||||
|
||||
export interface WebsiteSearchFilter extends SearchFilter<WebsiteSearchFilterType> {
|
||||
export interface WebsiteSearchFilter extends SearchFilter {
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
includeTeams?: boolean;
|
||||
onlyTeams?: boolean;
|
||||
}
|
||||
|
||||
export interface UserSearchFilter extends SearchFilter<UserSearchFilterType> {
|
||||
export interface UserSearchFilter extends SearchFilter {
|
||||
teamId?: string;
|
||||
}
|
||||
|
||||
export interface TeamSearchFilter extends SearchFilter<TeamSearchFilterType> {
|
||||
export interface TeamSearchFilter extends SearchFilter {
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export interface ReportSearchFilter extends SearchFilter<ReportSearchFilterType> {
|
||||
export interface ReportSearchFilter extends SearchFilter {
|
||||
userId?: string;
|
||||
websiteId?: string;
|
||||
includeTeams?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchFilter<T> {
|
||||
filter?: string;
|
||||
filterType?: T;
|
||||
pageSize: number;
|
||||
page: number;
|
||||
export interface SearchFilter {
|
||||
query?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
orderBy?: string;
|
||||
sortDescending?: boolean;
|
||||
}
|
||||
|
||||
export interface FilterResult<T> {
|
||||
data: T;
|
||||
count: number;
|
||||
pageSize: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
orderBy?: string;
|
||||
sortDescending?: boolean;
|
||||
}
|
||||
|
||||
export interface DynamicData {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
import moment from 'moment';
|
||||
import * as yup from 'yup';
|
||||
import { UNIT_TYPES } from './constants';
|
||||
|
||||
export function getDateRangeValidation() {
|
||||
return {
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
};
|
||||
}
|
||||
export const TimezoneTest = yup.string().test(
|
||||
'timezone',
|
||||
() => `Invalid timezone`,
|
||||
value => moment.tz.zone(value) !== null,
|
||||
);
|
||||
|
||||
// ex: /funnel|insights|retention/i
|
||||
export function getFilterValidation(matchRegex) {
|
||||
return {
|
||||
filter: yup.string(),
|
||||
filterType: yup.string().matches(matchRegex),
|
||||
pageSize: yup.number().integer().positive().max(200),
|
||||
page: yup.number().integer().positive(),
|
||||
orderBy: yup.string(),
|
||||
};
|
||||
}
|
||||
export const UnitTypeTest = yup.string().test(
|
||||
'unit',
|
||||
() => `Invalid unit`,
|
||||
value => UNIT_TYPES.includes(value),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue