mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-16 02:05:04 +01:00
Add api validations.
This commit is contained in:
parent
7d5a24044a
commit
7a7233ead4
@ -1,19 +1,20 @@
|
||||
import redis from '@umami/redis-client';
|
||||
import cors from 'cors';
|
||||
import debug from 'debug';
|
||||
import { getAuthToken, parseShareToken } from 'lib/auth';
|
||||
import { ROLES } from 'lib/constants';
|
||||
import { isUuid, secret } from 'lib/crypto';
|
||||
import { findSession } from 'lib/session';
|
||||
import {
|
||||
createMiddleware,
|
||||
unauthorized,
|
||||
badRequest,
|
||||
createMiddleware,
|
||||
parseSecureToken,
|
||||
tooManyRequest,
|
||||
unauthorized,
|
||||
} from 'next-basics';
|
||||
import debug from 'debug';
|
||||
import cors from 'cors';
|
||||
import redis from '@umami/redis-client';
|
||||
import { findSession } from 'lib/session';
|
||||
import { getAuthToken, parseShareToken } from 'lib/auth';
|
||||
import { secret, isUuid } from 'lib/crypto';
|
||||
import { ROLES } from 'lib/constants';
|
||||
import { getUserById } from '../queries';
|
||||
import { NextApiRequestCollect } from 'pages/api/send';
|
||||
import { getUserById } from '../queries';
|
||||
import { NextApiRequestQueryBody } from './types';
|
||||
|
||||
const log = debug('umami:middleware');
|
||||
|
||||
@ -75,3 +76,15 @@ export const useAuth = createMiddleware(async (req, res, next) => {
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export const useValidate = createMiddleware(async (req: any, res, next) => {
|
||||
try {
|
||||
const { yup } = req as NextApiRequestQueryBody;
|
||||
|
||||
yup[req.method].validateSync({ ...req.query, ...req.body });
|
||||
} catch (e: any) {
|
||||
return badRequest(res, e.message);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
17
lib/types.ts
17
lib/types.ts
@ -5,11 +5,13 @@ import {
|
||||
EVENT_TYPE,
|
||||
KAFKA_TOPIC,
|
||||
REPORT_FILTER_TYPES,
|
||||
REPORT_TYPES,
|
||||
ROLES,
|
||||
TEAM_FILTER_TYPES,
|
||||
USER_FILTER_TYPES,
|
||||
WEBSITE_FILTER_TYPES,
|
||||
} from './constants';
|
||||
import * as yup from 'yup';
|
||||
|
||||
type ObjectValues<T> = T[keyof T];
|
||||
|
||||
@ -18,6 +20,8 @@ export type Role = ObjectValues<typeof ROLES>;
|
||||
export type EventType = ObjectValues<typeof EVENT_TYPE>;
|
||||
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>;
|
||||
@ -47,8 +51,8 @@ export interface ReportSearchFilter extends SearchFilter<ReportSearchFilterType>
|
||||
export interface SearchFilter<T> {
|
||||
filter?: string;
|
||||
filterType?: T;
|
||||
pageSize?: number;
|
||||
page?: number;
|
||||
pageSize: number;
|
||||
page: number;
|
||||
orderBy?: string;
|
||||
}
|
||||
|
||||
@ -76,11 +80,19 @@ export interface Auth {
|
||||
};
|
||||
}
|
||||
|
||||
export interface YupRequest {
|
||||
GET?: yup.ObjectSchema<any>;
|
||||
POST?: yup.ObjectSchema<any>;
|
||||
PUT?: yup.ObjectSchema<any>;
|
||||
DELETE?: yup.ObjectSchema<any>;
|
||||
}
|
||||
|
||||
export interface NextApiRequestQueryBody<TQuery = any, TBody = any> extends NextApiRequest {
|
||||
auth?: Auth;
|
||||
query: TQuery & { [key: string]: string | string[] };
|
||||
body: TBody;
|
||||
headers: any;
|
||||
yup: YupRequest;
|
||||
}
|
||||
|
||||
export interface NextApiRequestAuth extends NextApiRequest {
|
||||
@ -168,7 +180,6 @@ export interface RealtimeUpdate {
|
||||
export interface DateRange {
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
unit: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
19
lib/yup.ts
Normal file
19
lib/yup.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import * as yup from 'yup';
|
||||
|
||||
export function getDateRangeValidation() {
|
||||
return {
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
};
|
||||
}
|
||||
|
||||
// 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(),
|
||||
};
|
||||
}
|
@ -1,19 +1,20 @@
|
||||
import redis from '@umami/redis-client';
|
||||
import debug from 'debug';
|
||||
import { setAuthKey } from 'lib/auth';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, User } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import {
|
||||
ok,
|
||||
unauthorized,
|
||||
badRequest,
|
||||
checkPassword,
|
||||
createSecureToken,
|
||||
methodNotAllowed,
|
||||
forbidden,
|
||||
methodNotAllowed,
|
||||
ok,
|
||||
unauthorized,
|
||||
} from 'next-basics';
|
||||
import redis from '@umami/redis-client';
|
||||
import { getUserByUsername } from 'queries';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { NextApiRequestQueryBody, User } from 'lib/types';
|
||||
import { setAuthKey } from 'lib/auth';
|
||||
import * as yup from 'yup';
|
||||
|
||||
const log = debug('umami:auth');
|
||||
|
||||
@ -27,6 +28,13 @@ export interface LoginResponse {
|
||||
user: User;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
username: yup.string().required(),
|
||||
password: yup.string().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, LoginRequestBody>,
|
||||
res: NextApiResponse<LoginResponse>,
|
||||
@ -35,13 +43,12 @@ export default async (
|
||||
return forbidden(res);
|
||||
}
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
return badRequest(res);
|
||||
}
|
||||
|
||||
const user = await getUserByUsername(username, { includePassword: true });
|
||||
|
||||
if (user && checkPassword(password, user.password)) {
|
||||
|
@ -1,26 +1,37 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getEventDataEvents } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface EventDataEventsRequestQuery {
|
||||
export interface EventDataFieldsRequestQuery {
|
||||
websiteId: string;
|
||||
dateRange: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
event?: string;
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
event: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
event: yup.string().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<EventDataEventsRequestQuery>,
|
||||
req: NextApiRequestQueryBody<EventDataFieldsRequestQuery, any>,
|
||||
res: NextApiResponse<any>,
|
||||
) => {
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const { websiteId, startAt, endAt, event } = req.query;
|
||||
|
||||
|
@ -1,19 +1,27 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getEventDataFields } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface EventDataFieldsRequestQuery {
|
||||
websiteId: string;
|
||||
dateRange: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
field?: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
field: yup.string(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<EventDataFieldsRequestQuery>,
|
||||
res: NextApiResponse<any>,
|
||||
@ -21,6 +29,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const { websiteId, startAt, endAt, field } = req.query;
|
||||
|
||||
|
@ -1,18 +1,24 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { getEventDataStats } from 'queries';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface EventDataStatsRequestQuery {
|
||||
websiteId: string;
|
||||
dateRange: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<EventDataStatsRequestQuery>,
|
||||
res: NextApiResponse<any>,
|
||||
@ -20,6 +26,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const { websiteId, startAt, endAt } = req.query;
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, User } from 'lib/types';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import {
|
||||
badRequest,
|
||||
checkPassword,
|
||||
forbidden,
|
||||
hashPassword,
|
||||
methodNotAllowed,
|
||||
forbidden,
|
||||
ok,
|
||||
} from 'next-basics';
|
||||
import { getUserById, updateUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserPasswordRequestQuery {
|
||||
id: string;
|
||||
@ -20,6 +21,14 @@ export interface UserPasswordRequestBody {
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
currentPassword: yup.string().required(),
|
||||
newPassword: yup.string().min(8).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<UserPasswordRequestQuery, UserPasswordRequestBody>,
|
||||
res: NextApiResponse<User>,
|
||||
@ -30,6 +39,9 @@ export default async (
|
||||
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { currentPassword, newPassword } = req.body;
|
||||
const { id } = req.auth.user;
|
||||
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { useCors } from 'lib/middleware';
|
||||
import { useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed } from 'next-basics';
|
||||
import userTeams from 'pages/api/users/[id]/teams';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface MyTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {}
|
||||
export interface MyTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Name|Owner/i),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<MyTeamsRequestQuery, any>,
|
||||
@ -12,7 +22,12 @@ export default async (
|
||||
) => {
|
||||
await useCors(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
req.query.id = req.auth.user.id;
|
||||
|
||||
return userTeams(req, res);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,20 @@
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed } from 'next-basics';
|
||||
|
||||
import userWebsites from 'pages/api/users/[id]/websites';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface MyWebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {}
|
||||
export interface MyWebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Name|Domain/i),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<MyWebsitesRequestQuery, any>,
|
||||
@ -14,6 +23,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
req.query.id = req.auth.user.id;
|
||||
|
||||
|
@ -1,22 +1,34 @@
|
||||
import { subMinutes } from 'date-fns';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getRealtimeData } from 'queries';
|
||||
|
||||
import * as yup from 'yup';
|
||||
export interface RealtimeRequestQuery {
|
||||
id: string;
|
||||
startAt: number;
|
||||
}
|
||||
|
||||
const currentDate = new Date().getTime();
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().max(currentDate).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<RealtimeRequestQuery>,
|
||||
res: NextApiResponse<RealtimeInit>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const { id: websiteId, startAt } = req.query;
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { canUpdateReport, canViewReport, canDeleteReport } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canDeleteReport, canUpdateReport, canViewReport } from 'lib/auth';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, ReportType, YupRequest } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getReportById, updateReport, deleteReport } from 'queries';
|
||||
import { deleteReport, getReportById, updateReport } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ReportRequestQuery {
|
||||
id: string;
|
||||
@ -11,12 +12,34 @@ export interface ReportRequestQuery {
|
||||
|
||||
export interface ReportRequestBody {
|
||||
websiteId: string;
|
||||
type: string;
|
||||
type: ReportType;
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: string;
|
||||
}
|
||||
|
||||
const schema: YupRequest = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
websiteId: yup.string().uuid().required(),
|
||||
type: yup
|
||||
.string()
|
||||
.matches(/funnel|insights|retention/i)
|
||||
.required(),
|
||||
name: yup.string().max(200).required(),
|
||||
description: yup.string().max(500),
|
||||
parameters: yup
|
||||
.object()
|
||||
.test('len', 'Must not exceed 6000 characters.', val => JSON.stringify(val).length < 6000),
|
||||
}),
|
||||
DELETE: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<ReportRequestQuery, ReportRequestBody>,
|
||||
res: NextApiResponse,
|
||||
@ -24,6 +47,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: reportId } = req.query;
|
||||
const {
|
||||
user: { id: userId },
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getFunnel } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface FunnelRequestBody {
|
||||
websiteId: string;
|
||||
@ -22,6 +23,21 @@ export interface FunnelResponse {
|
||||
endAt: number;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
urls: yup.array().min(2).of(yup.string()).required(),
|
||||
window: yup.number().positive().required(),
|
||||
dateRange: yup
|
||||
.object()
|
||||
.shape({
|
||||
startDate: yup.date().required(),
|
||||
endDate: yup.date().required(),
|
||||
})
|
||||
.required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, FunnelRequestBody>,
|
||||
res: NextApiResponse<FunnelResponse>,
|
||||
@ -29,6 +45,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const {
|
||||
websiteId,
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createReport, getReportsByUserId, getReportsByWebsiteId } from 'queries';
|
||||
import { methodNotAllowed, ok } from 'next-basics';
|
||||
import { createReport, getReportsByUserId } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ReportsRequestQuery extends SearchFilter<ReportSearchFilterType> {}
|
||||
|
||||
@ -14,11 +15,28 @@ export interface ReportRequestBody {
|
||||
type: string;
|
||||
description: string;
|
||||
parameters: {
|
||||
window: string;
|
||||
urls: string[];
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Name|Description|Type|Username|Website Name|Website Domain/i),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
name: yup.string().max(200).required(),
|
||||
type: yup
|
||||
.string()
|
||||
.matches(/funnel|insights|retention/i)
|
||||
.required(),
|
||||
description: yup.string().max(500),
|
||||
parameters: yup
|
||||
.object()
|
||||
.test('len', 'Must not exceed 6000 characters.', val => JSON.stringify(val).length < 6000),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, ReportRequestBody>,
|
||||
res: NextApiResponse,
|
||||
@ -26,6 +44,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getInsights } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface InsightsRequestBody {
|
||||
websiteId: string;
|
||||
@ -16,6 +17,37 @@ export interface InsightsRequestBody {
|
||||
groups: { name: string; type: string }[];
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
dateRange: yup
|
||||
.object()
|
||||
.shape({
|
||||
startDate: yup.date().required(),
|
||||
endDate: yup.date().required(),
|
||||
})
|
||||
.required(),
|
||||
fields: yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object().shape({
|
||||
name: yup.string().required(),
|
||||
type: yup.string().required(),
|
||||
value: yup.string().required(),
|
||||
}),
|
||||
)
|
||||
.min(1)
|
||||
.required(),
|
||||
filters: yup.array().of(yup.string()).min(1).required(),
|
||||
groups: yup.array().of(
|
||||
yup.object().shape({
|
||||
name: yup.string().required(),
|
||||
type: yup.string().required(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
function convertFilters(filters) {
|
||||
return filters.reduce((obj, { name, ...value }) => {
|
||||
obj[name] = value;
|
||||
@ -31,6 +63,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const {
|
||||
websiteId,
|
||||
|
@ -1,33 +1,43 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useCors, useAuth } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getRetention } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface RetentionRequestBody {
|
||||
websiteId: string;
|
||||
dateRange: { window; startDate: string; endDate: string };
|
||||
timezone: string;
|
||||
dateRange: { startDate: string; endDate: string };
|
||||
}
|
||||
|
||||
export interface RetentionResponse {
|
||||
startAt: number;
|
||||
endAt: number;
|
||||
}
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
dateRange: yup
|
||||
.object()
|
||||
.shape({
|
||||
startDate: yup.date().required(),
|
||||
endDate: yup.date().required(),
|
||||
})
|
||||
.required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, RetentionRequestBody>,
|
||||
res: NextApiResponse<RetentionResponse>,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const {
|
||||
websiteId,
|
||||
dateRange: { startDate, endDate },
|
||||
timezone,
|
||||
} = req.body;
|
||||
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
@ -37,7 +47,6 @@ export default async (
|
||||
const data = await getRetention(websiteId, {
|
||||
startDate: new Date(startDate),
|
||||
endDate: new Date(endDate),
|
||||
timezone,
|
||||
});
|
||||
|
||||
return ok(res, data);
|
||||
|
@ -1,14 +1,15 @@
|
||||
import isbot from 'isbot';
|
||||
import ipaddr from 'ipaddr.js';
|
||||
import { createToken, ok, send, badRequest, forbidden } from 'next-basics';
|
||||
import { saveEvent, saveSessionData } from 'queries';
|
||||
import { useCors, useSession } from 'lib/middleware';
|
||||
import { getJsonBody, getIpAddress } from 'lib/detect';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { Resolver } from 'dns/promises';
|
||||
import { CollectionType } from 'lib/types';
|
||||
import { COLLECTION_TYPE } from 'lib/constants';
|
||||
import ipaddr from 'ipaddr.js';
|
||||
import isbot from 'isbot';
|
||||
import { COLLECTION_TYPE, HOSTNAME_REGEX } from 'lib/constants';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { getIpAddress, getJsonBody } from 'lib/detect';
|
||||
import { useCors, useSession, useValidate } from 'lib/middleware';
|
||||
import { CollectionType, YupRequest } from 'lib/types';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { badRequest, createToken, forbidden, ok, send } from 'next-basics';
|
||||
import { saveEvent, saveSessionData } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface CollectRequestBody {
|
||||
payload: {
|
||||
@ -43,8 +44,32 @@ export interface NextApiRequestCollect extends NextApiRequest {
|
||||
city: string;
|
||||
};
|
||||
headers: { [key: string]: any };
|
||||
yup: YupRequest;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
payload: yup
|
||||
.object()
|
||||
.shape({
|
||||
data: yup.object(),
|
||||
hostname: yup.string().matches(HOSTNAME_REGEX).max(100),
|
||||
language: yup.string().max(35),
|
||||
referrer: yup.string().max(500),
|
||||
screen: yup.string().max(11),
|
||||
title: yup.string().max(500),
|
||||
url: yup.string().max(500),
|
||||
website: yup.string().uuid().required(),
|
||||
name: yup.string().max(50),
|
||||
})
|
||||
.required(),
|
||||
type: yup
|
||||
.string()
|
||||
.matches(/event|identify/i)
|
||||
.required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||
await useCors(req, res);
|
||||
|
||||
@ -54,11 +79,8 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||
|
||||
const { type, payload } = getJsonBody<CollectRequestBody>(req);
|
||||
|
||||
const error = validateBody({ type, payload });
|
||||
|
||||
if (error) {
|
||||
return badRequest(res, error);
|
||||
}
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (await hasBlockedIp(req)) {
|
||||
return forbidden(res);
|
||||
@ -118,22 +140,6 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||
return send(res, token);
|
||||
};
|
||||
|
||||
function validateBody({ type, payload }: CollectRequestBody) {
|
||||
if (!type || !payload) {
|
||||
return 'Invalid payload.';
|
||||
}
|
||||
|
||||
if (type !== COLLECTION_TYPE.event && type !== COLLECTION_TYPE.identify) {
|
||||
return 'Wrong payload type.';
|
||||
}
|
||||
|
||||
const { data } = payload;
|
||||
|
||||
if (data && !(typeof data === 'object' && !Array.isArray(data))) {
|
||||
return 'Invalid event data.';
|
||||
}
|
||||
}
|
||||
|
||||
async function hasBlockedIp(req: NextApiRequestCollect) {
|
||||
const ignoreIps = process.env.IGNORE_IP;
|
||||
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
|
||||
import { getWebsiteByShareId } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface ShareRequestQuery {
|
||||
id: string;
|
||||
@ -13,10 +15,19 @@ export interface ShareResponse {
|
||||
token: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<ShareRequestQuery>,
|
||||
res: NextApiResponse<ShareResponse>,
|
||||
) => {
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: shareId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Team } from '@prisma/client';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canDeleteTeam, canUpdateTeam, canViewTeam } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteTeam, getTeamById, updateTeam } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamRequestQuery {
|
||||
id: string;
|
||||
@ -15,12 +16,29 @@ export interface TeamRequestBody {
|
||||
accessCode: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
name: yup.string().max(50).required(),
|
||||
accessCode: yup.string().max(50).required(),
|
||||
}),
|
||||
DELETE: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<TeamRequestQuery, TeamRequestBody>,
|
||||
res: NextApiResponse<Team>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: teamId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,18 +1,28 @@
|
||||
import { canDeleteTeamUser } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteTeamUser } from 'queries';
|
||||
|
||||
import * as yup from 'yup';
|
||||
export interface TeamUserRequestQuery {
|
||||
id: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
DELETE: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
userId: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (req: NextApiRequestQueryBody<TeamUserRequestQuery>, res: NextApiResponse) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
const { id: teamId, userId } = req.query;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { canUpdateTeam, canViewTeam } from 'lib/auth';
|
||||
import { canViewTeam } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createTeamUser, getUserByUsername, getUsersByTeamId } from 'queries';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getUsersByTeamId } from 'queries';
|
||||
|
||||
export interface TeamUserRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
||||
id: string;
|
||||
@ -38,24 +38,5 @@ export default async (
|
||||
return ok(res, users);
|
||||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!(await canUpdateTeam(req.auth, teamId))) {
|
||||
return unauthorized(res, 'You must be the owner of this team.');
|
||||
}
|
||||
|
||||
const { email, roleId: roleId } = req.body;
|
||||
|
||||
// Check for User
|
||||
const user = await getUserByUsername(email);
|
||||
|
||||
if (!user) {
|
||||
return badRequest(res, 'The User does not exists.');
|
||||
}
|
||||
|
||||
const updated = await createTeamUser(user.id, teamId, roleId);
|
||||
|
||||
return ok(res, updated);
|
||||
}
|
||||
|
||||
return methodNotAllowed(res);
|
||||
};
|
||||
|
@ -1,21 +1,32 @@
|
||||
import { canDeleteTeamWebsite } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteTeamWebsite } from 'queries/admin/teamWebsite';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamWebsitesRequestQuery {
|
||||
id: string;
|
||||
websiteId: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
DELETE: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
websiteId: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<TeamWebsitesRequestQuery>,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: teamId, websiteId } = req.query;
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { canViewTeam } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getWebsites, getWebsitesByTeamId } from 'queries';
|
||||
import { getWebsitesByTeamId } from 'queries';
|
||||
import { createTeamWebsites } from 'queries/admin/teamWebsite';
|
||||
|
||||
export interface TeamWebsiteRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
||||
@ -14,12 +15,28 @@ export interface TeamWebsiteRequestBody {
|
||||
websiteIds?: string[];
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
...getFilterValidation(/All|Name|Domain/i),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
websiteIds: yup.array().of(yup.string()).min(1).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<TeamWebsiteRequestQuery, TeamWebsiteRequestBody>,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: teamId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,23 +1,39 @@
|
||||
import { Team } from '@prisma/client';
|
||||
import { canCreateTeam } from 'lib/auth';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createTeam, getTeamsByUserId } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface TeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {}
|
||||
export interface TeamsRequestBody extends SearchFilter<TeamSearchFilterType> {
|
||||
export interface TeamsRequestBody {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface MyTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Name|Owner/i),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
name: yup.string().max(50).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<TeamsRequestQuery, TeamsRequestBody>,
|
||||
res: NextApiResponse<Team[] | Team>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
|
@ -1,21 +1,30 @@
|
||||
import { Team } from '@prisma/client';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, notFound } from 'next-basics';
|
||||
import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
|
||||
import { ROLES } from 'lib/constants';
|
||||
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, notFound, ok } from 'next-basics';
|
||||
import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
export interface TeamsJoinRequestBody {
|
||||
accessCode: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
POST: yup.object().shape({
|
||||
accessCode: yup.string().max(50).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, TeamsJoinRequestBody>,
|
||||
res: NextApiResponse<Team>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const { accessCode } = req.body;
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
|
||||
import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserRequestQuery {
|
||||
id: string;
|
||||
@ -15,12 +16,27 @@ export interface UserRequestBody {
|
||||
role: Role;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
username: yup.string().max(255),
|
||||
password: yup.string(),
|
||||
role: yup.string().matches(/admin|user|view-only/i),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<UserRequestQuery, UserRequestBody>,
|
||||
res: NextApiResponse<User>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId, isAdmin },
|
||||
} = req.auth;
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getTeamsByUserId } from 'queries';
|
||||
|
||||
import * as yup from 'yup';
|
||||
export interface UserTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
||||
id: string;
|
||||
}
|
||||
@ -14,6 +15,13 @@ export interface UserTeamsRequestBody {
|
||||
shareId: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
...getFilterValidation('/All|Name|Owner/i'),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, UserTeamsRequestBody>,
|
||||
res: NextApiResponse,
|
||||
@ -21,6 +29,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { user } = req.auth;
|
||||
const { id: userId } = req.query;
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getEventDataUsage, getEventUsage, getUserWebsites } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserUsageRequestQuery {
|
||||
id: string;
|
||||
@ -21,6 +22,14 @@ export interface UserUsageRequestResponse {
|
||||
}[];
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<UserUsageRequestQuery>,
|
||||
res: NextApiResponse<UserUsageRequestResponse>,
|
||||
@ -28,6 +37,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { user } = req.auth;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,25 +1,36 @@
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getWebsitesByUserId } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface UserWebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
||||
id: string;
|
||||
}
|
||||
export interface UserWebsitesRequestBody {
|
||||
name: string;
|
||||
domain: string;
|
||||
shareId: string;
|
||||
includeTeams?: boolean;
|
||||
onlyTeams?: boolean;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
includeTeams: yup.boolean(),
|
||||
onlyTeams: yup.boolean(),
|
||||
...getFilterValidation(/All|Name|Domain/i),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<any, UserWebsitesRequestBody>,
|
||||
req: NextApiRequestQueryBody<UserWebsitesRequestQuery>,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { user } = req.auth;
|
||||
const { id: userId, page, filter, pageSize, includeTeams, onlyTeams } = req.query;
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { canCreateUser, canViewUsers } from 'lib/auth';
|
||||
import { ROLES } from 'lib/constants';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { useAuth, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, Role, SearchFilter, User, UserSearchFilterType } from 'lib/types';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createUser, getUserByUsername, getUsers } from 'queries';
|
||||
@ -15,12 +16,31 @@ export interface UsersRequestBody {
|
||||
role?: Role;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Username/i),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
username: yup.string().max(255).required(),
|
||||
password: yup.string().required(),
|
||||
id: yup.string().uuid(),
|
||||
role: yup
|
||||
.string()
|
||||
.matches(/admin|user|view-only/i)
|
||||
.required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<UsersRequestQuery, UsersRequestBody>,
|
||||
res: NextApiResponse<User[] | User>,
|
||||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await canViewUsers(req.auth))) {
|
||||
return unauthorized(res);
|
||||
@ -28,7 +48,7 @@ export default async (
|
||||
|
||||
const { page, filter, pageSize } = req.query;
|
||||
|
||||
const users = await getUsers({ page, filter, pageSize: +pageSize || null });
|
||||
const users = await getUsers({ page, filter, pageSize: pageSize ? +pageSize : null });
|
||||
|
||||
return ok(res, users);
|
||||
}
|
||||
|
@ -1,14 +1,21 @@
|
||||
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getActiveVisitors } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface WebsiteActiveRequestQuery {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteActiveRequestQuery>,
|
||||
res: NextApiResponse<WebsiteActive>,
|
||||
@ -16,6 +23,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,14 +1,21 @@
|
||||
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getWebsiteDateRange } from 'queries';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface WebsiteDateRangeRequestQuery {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteDateRangeRequestQuery>,
|
||||
res: NextApiResponse<WebsiteActive>,
|
||||
@ -16,6 +23,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import moment from 'moment-timezone';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
@ -16,9 +16,21 @@ export interface WebsiteEventsRequestQuery {
|
||||
unit: string;
|
||||
timezone: string;
|
||||
url: string;
|
||||
eventName: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
startAt: yup.number().integer().required(),
|
||||
endAt: yup.number().integer().moreThan(yup.ref('startAt')).required(),
|
||||
unit: yup.string().required(),
|
||||
timezone: yup.string().required(),
|
||||
url: yup.string(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteEventsRequestQuery>,
|
||||
res: NextApiResponse<WebsiteMetric>,
|
||||
@ -26,7 +38,10 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const { id: websiteId, timezone, url, eventName } = req.query;
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId, timezone, url } = req.query;
|
||||
const { startDate, endDate, unit } = await parseDateRangeQuery(req);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
@ -44,7 +59,6 @@ export default async (
|
||||
timezone,
|
||||
unit,
|
||||
url,
|
||||
eventName,
|
||||
});
|
||||
|
||||
return ok(res, events);
|
||||
|
@ -2,7 +2,7 @@ import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
|
||||
import { Website, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite, canUpdateWebsite, canDeleteWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { deleteWebsite, getWebsiteById, updateWebsite } from 'queries';
|
||||
import { SHARE_ID_REGEX } from 'lib/constants';
|
||||
|
||||
@ -16,6 +16,13 @@ export interface WebsiteRequestBody {
|
||||
shareId: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteRequestQuery, WebsiteRequestBody>,
|
||||
res: NextApiResponse<Website>,
|
||||
@ -23,6 +30,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -2,10 +2,11 @@ import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { SESSION_COLUMNS, EVENT_COLUMNS, FILTER_COLUMNS } from 'lib/constants';
|
||||
import { getPageviewMetrics, getSessionMetrics } from 'queries';
|
||||
import { parseDateRangeQuery } from 'lib/query';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export interface WebsiteMetricsRequestQuery {
|
||||
id: string;
|
||||
@ -26,6 +27,12 @@ export interface WebsiteMetricsRequestQuery {
|
||||
language: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteMetricsRequestQuery>,
|
||||
res: NextApiResponse<WebsiteMetric[]>,
|
||||
@ -33,6 +40,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
id: websiteId,
|
||||
type,
|
||||
|
@ -3,7 +3,7 @@ import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { getPageviewStats, getSessionStats } from 'queries';
|
||||
import { parseDateRangeQuery } from 'lib/query';
|
||||
|
||||
@ -24,6 +24,13 @@ export interface WebsitePageviewRequestQuery {
|
||||
city?: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsitePageviewRequestQuery>,
|
||||
res: NextApiResponse<WebsitePageviews>,
|
||||
@ -31,6 +38,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
id: websiteId,
|
||||
timezone,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
@ -9,6 +9,13 @@ export interface ReportsRequestQuery extends SearchFilter<ReportSearchFilterType
|
||||
id: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<ReportsRequestQuery, any>,
|
||||
res: NextApiResponse,
|
||||
@ -16,6 +23,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canUpdateWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { resetWebsite } from 'queries';
|
||||
@ -9,6 +9,13 @@ export interface WebsiteResetRequestQuery {
|
||||
id: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
||||
res: NextApiResponse,
|
||||
@ -16,6 +23,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
|
@ -2,7 +2,7 @@ import { subMinutes, differenceInMinutes } from 'date-fns';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, WebsiteStats } from 'lib/types';
|
||||
import { parseDateRangeQuery } from 'lib/query';
|
||||
import { getWebsiteStats } from 'queries';
|
||||
@ -24,6 +24,13 @@ export interface WebsiteStatsRequestQuery {
|
||||
city: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteStatsRequestQuery>,
|
||||
res: NextApiResponse<WebsiteStats>,
|
||||
@ -31,6 +38,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
id: websiteId,
|
||||
url,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { EVENT_COLUMNS, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants';
|
||||
@ -10,6 +10,13 @@ export interface WebsiteResetRequestQuery {
|
||||
id: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
id: yup.string().uuid().required(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
||||
res: NextApiResponse,
|
||||
@ -17,6 +24,9 @@ export default async (
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const { id: websiteId, type } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { canCreateWebsite } from 'lib/auth';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createWebsite } from 'queries';
|
||||
import userWebsites from 'pages/api/users/[id]/websites';
|
||||
import * as yup from 'yup';
|
||||
import { getFilterValidation } from 'lib/yup';
|
||||
|
||||
export interface WebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {}
|
||||
|
||||
@ -15,12 +17,25 @@ export interface WebsitesRequestBody {
|
||||
shareId: string;
|
||||
}
|
||||
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
...getFilterValidation(/All|Name|Domain/i),
|
||||
}),
|
||||
POST: yup.object().shape({
|
||||
name: yup.string().max(100).required(),
|
||||
domain: yup.string().max(500).required(),
|
||||
shareId: yup.string().max(50),
|
||||
}),
|
||||
};
|
||||
|
||||
export default async (
|
||||
req: NextApiRequestQueryBody<WebsitesRequestQuery, WebsitesRequestBody>,
|
||||
res: NextApiResponse,
|
||||
) => {
|
||||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
req.yup = schema;
|
||||
await useValidate(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
@ -30,7 +45,7 @@ export default async (
|
||||
req.query.id = userId;
|
||||
req.query.pageSize = 100;
|
||||
|
||||
return userWebsites(req, res);
|
||||
return userWebsites(req as any, res);
|
||||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
|
Loading…
Reference in New Issue
Block a user