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 {
|
import {
|
||||||
createMiddleware,
|
|
||||||
unauthorized,
|
|
||||||
badRequest,
|
badRequest,
|
||||||
|
createMiddleware,
|
||||||
parseSecureToken,
|
parseSecureToken,
|
||||||
tooManyRequest,
|
tooManyRequest,
|
||||||
|
unauthorized,
|
||||||
} from 'next-basics';
|
} 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 { NextApiRequestCollect } from 'pages/api/send';
|
||||||
|
import { getUserById } from '../queries';
|
||||||
|
import { NextApiRequestQueryBody } from './types';
|
||||||
|
|
||||||
const log = debug('umami:middleware');
|
const log = debug('umami:middleware');
|
||||||
|
|
||||||
@ -75,3 +76,15 @@ export const useAuth = createMiddleware(async (req, res, next) => {
|
|||||||
|
|
||||||
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,
|
EVENT_TYPE,
|
||||||
KAFKA_TOPIC,
|
KAFKA_TOPIC,
|
||||||
REPORT_FILTER_TYPES,
|
REPORT_FILTER_TYPES,
|
||||||
|
REPORT_TYPES,
|
||||||
ROLES,
|
ROLES,
|
||||||
TEAM_FILTER_TYPES,
|
TEAM_FILTER_TYPES,
|
||||||
USER_FILTER_TYPES,
|
USER_FILTER_TYPES,
|
||||||
WEBSITE_FILTER_TYPES,
|
WEBSITE_FILTER_TYPES,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
type ObjectValues<T> = T[keyof T];
|
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 EventType = ObjectValues<typeof EVENT_TYPE>;
|
||||||
export type DynamicDataType = ObjectValues<typeof DATA_TYPE>;
|
export type DynamicDataType = ObjectValues<typeof DATA_TYPE>;
|
||||||
export type KafkaTopic = ObjectValues<typeof KAFKA_TOPIC>;
|
export type KafkaTopic = ObjectValues<typeof KAFKA_TOPIC>;
|
||||||
|
export type ReportType = ObjectValues<typeof REPORT_TYPES>;
|
||||||
|
|
||||||
export type ReportSearchFilterType = ObjectValues<typeof REPORT_FILTER_TYPES>;
|
export type ReportSearchFilterType = ObjectValues<typeof REPORT_FILTER_TYPES>;
|
||||||
export type UserSearchFilterType = ObjectValues<typeof USER_FILTER_TYPES>;
|
export type UserSearchFilterType = ObjectValues<typeof USER_FILTER_TYPES>;
|
||||||
export type WebsiteSearchFilterType = ObjectValues<typeof WEBSITE_FILTER_TYPES>;
|
export type WebsiteSearchFilterType = ObjectValues<typeof WEBSITE_FILTER_TYPES>;
|
||||||
@ -47,8 +51,8 @@ export interface ReportSearchFilter extends SearchFilter<ReportSearchFilterType>
|
|||||||
export interface SearchFilter<T> {
|
export interface SearchFilter<T> {
|
||||||
filter?: string;
|
filter?: string;
|
||||||
filterType?: T;
|
filterType?: T;
|
||||||
pageSize?: number;
|
pageSize: number;
|
||||||
page?: number;
|
page: number;
|
||||||
orderBy?: string;
|
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 {
|
export interface NextApiRequestQueryBody<TQuery = any, TBody = any> extends NextApiRequest {
|
||||||
auth?: Auth;
|
auth?: Auth;
|
||||||
query: TQuery & { [key: string]: string | string[] };
|
query: TQuery & { [key: string]: string | string[] };
|
||||||
body: TBody;
|
body: TBody;
|
||||||
headers: any;
|
headers: any;
|
||||||
|
yup: YupRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NextApiRequestAuth extends NextApiRequest {
|
export interface NextApiRequestAuth extends NextApiRequest {
|
||||||
@ -168,7 +180,6 @@ export interface RealtimeUpdate {
|
|||||||
export interface DateRange {
|
export interface DateRange {
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
unit: string;
|
|
||||||
value: 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 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 { NextApiResponse } from 'next';
|
||||||
import {
|
import {
|
||||||
ok,
|
|
||||||
unauthorized,
|
|
||||||
badRequest,
|
|
||||||
checkPassword,
|
checkPassword,
|
||||||
createSecureToken,
|
createSecureToken,
|
||||||
methodNotAllowed,
|
|
||||||
forbidden,
|
forbidden,
|
||||||
|
methodNotAllowed,
|
||||||
|
ok,
|
||||||
|
unauthorized,
|
||||||
} from 'next-basics';
|
} from 'next-basics';
|
||||||
import redis from '@umami/redis-client';
|
|
||||||
import { getUserByUsername } from 'queries';
|
import { getUserByUsername } from 'queries';
|
||||||
import { secret } from 'lib/crypto';
|
import * as yup from 'yup';
|
||||||
import { NextApiRequestQueryBody, User } from 'lib/types';
|
|
||||||
import { setAuthKey } from 'lib/auth';
|
|
||||||
|
|
||||||
const log = debug('umami:auth');
|
const log = debug('umami:auth');
|
||||||
|
|
||||||
@ -27,6 +28,13 @@ export interface LoginResponse {
|
|||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
POST: yup.object().shape({
|
||||||
|
username: yup.string().required(),
|
||||||
|
password: yup.string().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, LoginRequestBody>,
|
req: NextApiRequestQueryBody<any, LoginRequestBody>,
|
||||||
res: NextApiResponse<LoginResponse>,
|
res: NextApiResponse<LoginResponse>,
|
||||||
@ -35,13 +43,12 @@ export default async (
|
|||||||
return forbidden(res);
|
return forbidden(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
if (!username || !password) {
|
|
||||||
return badRequest(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await getUserByUsername(username, { includePassword: true });
|
const user = await getUserByUsername(username, { includePassword: true });
|
||||||
|
|
||||||
if (user && checkPassword(password, user.password)) {
|
if (user && checkPassword(password, user.password)) {
|
||||||
|
@ -1,26 +1,37 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getEventDataEvents } from 'queries';
|
import { getEventDataEvents } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface EventDataEventsRequestQuery {
|
export interface EventDataFieldsRequestQuery {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
dateRange: {
|
startAt: string;
|
||||||
startDate: string;
|
endAt: string;
|
||||||
endDate: string;
|
event: 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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<EventDataEventsRequestQuery>,
|
req: NextApiRequestQueryBody<EventDataFieldsRequestQuery, any>,
|
||||||
res: NextApiResponse<any>,
|
res: NextApiResponse<any>,
|
||||||
) => {
|
) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { websiteId, startAt, endAt, event } = req.query;
|
const { websiteId, startAt, endAt, event } = req.query;
|
||||||
|
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getEventDataFields } from 'queries';
|
import { getEventDataFields } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface EventDataFieldsRequestQuery {
|
export interface EventDataFieldsRequestQuery {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
dateRange: {
|
startAt: string;
|
||||||
startDate: string;
|
endAt: string;
|
||||||
endDate: string;
|
|
||||||
};
|
|
||||||
field?: 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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<EventDataFieldsRequestQuery>,
|
req: NextApiRequestQueryBody<EventDataFieldsRequestQuery>,
|
||||||
res: NextApiResponse<any>,
|
res: NextApiResponse<any>,
|
||||||
@ -21,6 +29,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { websiteId, startAt, endAt, field } = req.query;
|
const { websiteId, startAt, endAt, field } = req.query;
|
||||||
|
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getEventDataStats } from 'queries';
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface EventDataStatsRequestQuery {
|
export interface EventDataStatsRequestQuery {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
dateRange: {
|
startAt: string;
|
||||||
startDate: string;
|
endAt: string;
|
||||||
endDate: 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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<EventDataStatsRequestQuery>,
|
req: NextApiRequestQueryBody<EventDataStatsRequestQuery>,
|
||||||
res: NextApiResponse<any>,
|
res: NextApiResponse<any>,
|
||||||
@ -20,6 +26,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { websiteId, startAt, endAt } = req.query;
|
const { websiteId, startAt, endAt } = req.query;
|
||||||
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, User } from 'lib/types';
|
import { NextApiRequestQueryBody, User } from 'lib/types';
|
||||||
import { useAuth } from 'lib/middleware';
|
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import {
|
import {
|
||||||
badRequest,
|
badRequest,
|
||||||
checkPassword,
|
checkPassword,
|
||||||
|
forbidden,
|
||||||
hashPassword,
|
hashPassword,
|
||||||
methodNotAllowed,
|
methodNotAllowed,
|
||||||
forbidden,
|
|
||||||
ok,
|
ok,
|
||||||
} from 'next-basics';
|
} from 'next-basics';
|
||||||
import { getUserById, updateUser } from 'queries';
|
import { getUserById, updateUser } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface UserPasswordRequestQuery {
|
export interface UserPasswordRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -20,6 +21,14 @@ export interface UserPasswordRequestBody {
|
|||||||
newPassword: string;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<UserPasswordRequestQuery, UserPasswordRequestBody>,
|
req: NextApiRequestQueryBody<UserPasswordRequestQuery, UserPasswordRequestBody>,
|
||||||
res: NextApiResponse<User>,
|
res: NextApiResponse<User>,
|
||||||
@ -30,6 +39,9 @@ export default async (
|
|||||||
|
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { currentPassword, newPassword } = req.body;
|
const { currentPassword, newPassword } = req.body;
|
||||||
const { id } = req.auth.user;
|
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 { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed } from 'next-basics';
|
import { methodNotAllowed } from 'next-basics';
|
||||||
import userTeams from 'pages/api/users/[id]/teams';
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<MyTeamsRequestQuery, any>,
|
req: NextApiRequestQueryBody<MyTeamsRequestQuery, any>,
|
||||||
@ -12,7 +22,12 @@ export default async (
|
|||||||
) => {
|
) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
req.query.id = req.auth.user.id;
|
||||||
|
|
||||||
return userTeams(req, res);
|
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 { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed } from 'next-basics';
|
import { methodNotAllowed } from 'next-basics';
|
||||||
|
|
||||||
import userWebsites from 'pages/api/users/[id]/websites';
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<MyWebsitesRequestQuery, any>,
|
req: NextApiRequestQueryBody<MyWebsitesRequestQuery, any>,
|
||||||
@ -14,6 +23,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
req.query.id = req.auth.user.id;
|
req.query.id = req.auth.user.id;
|
||||||
|
|
||||||
|
@ -1,22 +1,34 @@
|
|||||||
import { subMinutes } from 'date-fns';
|
import { subMinutes } from 'date-fns';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
|
import { NextApiRequestQueryBody, RealtimeInit } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
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';
|
||||||
export interface RealtimeRequestQuery {
|
export interface RealtimeRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
startAt: number;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<RealtimeRequestQuery>,
|
req: NextApiRequestQueryBody<RealtimeRequestQuery>,
|
||||||
res: NextApiResponse<RealtimeInit>,
|
res: NextApiResponse<RealtimeInit>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { id: websiteId, startAt } = req.query;
|
const { id: websiteId, startAt } = req.query;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { canUpdateReport, canViewReport, canDeleteReport } from 'lib/auth';
|
import { canDeleteReport, canUpdateReport, canViewReport } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody, ReportType, YupRequest } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
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 {
|
export interface ReportRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -11,12 +12,34 @@ export interface ReportRequestQuery {
|
|||||||
|
|
||||||
export interface ReportRequestBody {
|
export interface ReportRequestBody {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
type: string;
|
type: ReportType;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
parameters: 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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<ReportRequestQuery, ReportRequestBody>,
|
req: NextApiRequestQueryBody<ReportRequestQuery, ReportRequestBody>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -24,6 +47,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: reportId } = req.query;
|
const { id: reportId } = req.query;
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getFunnel } from 'queries';
|
import { getFunnel } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface FunnelRequestBody {
|
export interface FunnelRequestBody {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
@ -22,6 +23,21 @@ export interface FunnelResponse {
|
|||||||
endAt: number;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, FunnelRequestBody>,
|
req: NextApiRequestQueryBody<any, FunnelRequestBody>,
|
||||||
res: NextApiResponse<FunnelResponse>,
|
res: NextApiResponse<FunnelResponse>,
|
||||||
@ -29,6 +45,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const {
|
const {
|
||||||
websiteId,
|
websiteId,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
|
||||||
import { uuid } from 'lib/crypto';
|
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 { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok } from 'next-basics';
|
||||||
import { createReport, getReportsByUserId, getReportsByWebsiteId } from 'queries';
|
import { createReport, getReportsByUserId } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface ReportsRequestQuery extends SearchFilter<ReportSearchFilterType> {}
|
export interface ReportsRequestQuery extends SearchFilter<ReportSearchFilterType> {}
|
||||||
|
|
||||||
@ -14,11 +15,28 @@ export interface ReportRequestBody {
|
|||||||
type: string;
|
type: string;
|
||||||
description: string;
|
description: string;
|
||||||
parameters: {
|
parameters: {
|
||||||
window: string;
|
[key: string]: any;
|
||||||
urls: string[];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, ReportRequestBody>,
|
req: NextApiRequestQueryBody<any, ReportRequestBody>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -26,6 +44,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
} = req.auth;
|
} = req.auth;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getInsights } from 'queries';
|
import { getInsights } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface InsightsRequestBody {
|
export interface InsightsRequestBody {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
@ -16,6 +17,37 @@ export interface InsightsRequestBody {
|
|||||||
groups: { name: string; type: string }[];
|
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) {
|
function convertFilters(filters) {
|
||||||
return filters.reduce((obj, { name, ...value }) => {
|
return filters.reduce((obj, { name, ...value }) => {
|
||||||
obj[name] = value;
|
obj[name] = value;
|
||||||
@ -31,6 +63,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const {
|
const {
|
||||||
websiteId,
|
websiteId,
|
||||||
|
@ -1,33 +1,43 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useCors, useAuth } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { ok, methodNotAllowed, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getRetention } from 'queries';
|
import { getRetention } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface RetentionRequestBody {
|
export interface RetentionRequestBody {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
dateRange: { window; startDate: string; endDate: string };
|
dateRange: { startDate: string; endDate: string };
|
||||||
timezone: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RetentionResponse {
|
const schema = {
|
||||||
startAt: number;
|
POST: yup.object().shape({
|
||||||
endAt: number;
|
websiteId: yup.string().uuid().required(),
|
||||||
}
|
dateRange: yup
|
||||||
|
.object()
|
||||||
|
.shape({
|
||||||
|
startDate: yup.date().required(),
|
||||||
|
endDate: yup.date().required(),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, RetentionRequestBody>,
|
req: NextApiRequestQueryBody<any, RetentionRequestBody>,
|
||||||
res: NextApiResponse<RetentionResponse>,
|
res: NextApiResponse,
|
||||||
) => {
|
) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const {
|
const {
|
||||||
websiteId,
|
websiteId,
|
||||||
dateRange: { startDate, endDate },
|
dateRange: { startDate, endDate },
|
||||||
timezone,
|
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||||
@ -37,7 +47,6 @@ export default async (
|
|||||||
const data = await getRetention(websiteId, {
|
const data = await getRetention(websiteId, {
|
||||||
startDate: new Date(startDate),
|
startDate: new Date(startDate),
|
||||||
endDate: new Date(endDate),
|
endDate: new Date(endDate),
|
||||||
timezone,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return ok(res, data);
|
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 { Resolver } from 'dns/promises';
|
||||||
import { CollectionType } from 'lib/types';
|
import ipaddr from 'ipaddr.js';
|
||||||
import { COLLECTION_TYPE } from 'lib/constants';
|
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 {
|
export interface CollectRequestBody {
|
||||||
payload: {
|
payload: {
|
||||||
@ -43,8 +44,32 @@ export interface NextApiRequestCollect extends NextApiRequest {
|
|||||||
city: string;
|
city: string;
|
||||||
};
|
};
|
||||||
headers: { [key: string]: any };
|
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) => {
|
export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
|
|
||||||
@ -54,11 +79,8 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
|||||||
|
|
||||||
const { type, payload } = getJsonBody<CollectRequestBody>(req);
|
const { type, payload } = getJsonBody<CollectRequestBody>(req);
|
||||||
|
|
||||||
const error = validateBody({ type, payload });
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
if (error) {
|
|
||||||
return badRequest(res, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await hasBlockedIp(req)) {
|
if (await hasBlockedIp(req)) {
|
||||||
return forbidden(res);
|
return forbidden(res);
|
||||||
@ -118,22 +140,6 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
|||||||
return send(res, token);
|
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) {
|
async function hasBlockedIp(req: NextApiRequestCollect) {
|
||||||
const ignoreIps = process.env.IGNORE_IP;
|
const ignoreIps = process.env.IGNORE_IP;
|
||||||
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
|
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
|
||||||
import { secret } from 'lib/crypto';
|
import { secret } from 'lib/crypto';
|
||||||
|
import { useValidate } from 'lib/middleware';
|
||||||
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
|
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
|
||||||
import { getWebsiteByShareId } from 'queries';
|
import { getWebsiteByShareId } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface ShareRequestQuery {
|
export interface ShareRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -13,10 +15,19 @@ export interface ShareResponse {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<ShareRequestQuery>,
|
req: NextApiRequestQueryBody<ShareRequestQuery>,
|
||||||
res: NextApiResponse<ShareResponse>,
|
res: NextApiResponse<ShareResponse>,
|
||||||
) => {
|
) => {
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: shareId } = req.query;
|
const { id: shareId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Team } from '@prisma/client';
|
import { Team } from '@prisma/client';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
|
||||||
import { canDeleteTeam, canUpdateTeam, canViewTeam } from 'lib/auth';
|
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 { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { deleteTeam, getTeamById, updateTeam } from 'queries';
|
import { deleteTeam, getTeamById, updateTeam } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface TeamRequestQuery {
|
export interface TeamRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -15,12 +16,29 @@ export interface TeamRequestBody {
|
|||||||
accessCode: string;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<TeamRequestQuery, TeamRequestBody>,
|
req: NextApiRequestQueryBody<TeamRequestQuery, TeamRequestBody>,
|
||||||
res: NextApiResponse<Team>,
|
res: NextApiResponse<Team>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: teamId } = req.query;
|
const { id: teamId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,18 +1,28 @@
|
|||||||
import { canDeleteTeamUser } from 'lib/auth';
|
import { canDeleteTeamUser } from 'lib/auth';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { deleteTeamUser } from 'queries';
|
import { deleteTeamUser } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
export interface TeamUserRequestQuery {
|
export interface TeamUserRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
userId: 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) => {
|
export default async (req: NextApiRequestQueryBody<TeamUserRequestQuery>, res: NextApiResponse) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const { id: teamId, userId } = req.query;
|
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 { useAuth } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { createTeamUser, getUserByUsername, getUsersByTeamId } from 'queries';
|
import { getUsersByTeamId } from 'queries';
|
||||||
|
|
||||||
export interface TeamUserRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
export interface TeamUserRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
||||||
id: string;
|
id: string;
|
||||||
@ -38,24 +38,5 @@ export default async (
|
|||||||
return ok(res, users);
|
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);
|
return methodNotAllowed(res);
|
||||||
};
|
};
|
||||||
|
@ -1,21 +1,32 @@
|
|||||||
import { canDeleteTeamWebsite } from 'lib/auth';
|
import { canDeleteTeamWebsite } from 'lib/auth';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { deleteTeamWebsite } from 'queries/admin/teamWebsite';
|
import { deleteTeamWebsite } from 'queries/admin/teamWebsite';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface TeamWebsitesRequestQuery {
|
export interface TeamWebsitesRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
DELETE: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
websiteId: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<TeamWebsitesRequestQuery>,
|
req: NextApiRequestQueryBody<TeamWebsitesRequestQuery>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: teamId, websiteId } = req.query;
|
const { id: teamId, websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { canViewTeam } from 'lib/auth';
|
import { canViewTeam } from 'lib/auth';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
import { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getWebsites, getWebsitesByTeamId } from 'queries';
|
import { getWebsitesByTeamId } from 'queries';
|
||||||
import { createTeamWebsites } from 'queries/admin/teamWebsite';
|
import { createTeamWebsites } from 'queries/admin/teamWebsite';
|
||||||
|
|
||||||
export interface TeamWebsiteRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
export interface TeamWebsiteRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
||||||
@ -14,12 +15,28 @@ export interface TeamWebsiteRequestBody {
|
|||||||
websiteIds?: string[];
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<TeamWebsiteRequestQuery, TeamWebsiteRequestBody>,
|
req: NextApiRequestQueryBody<TeamWebsiteRequestQuery, TeamWebsiteRequestBody>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: teamId } = req.query;
|
const { id: teamId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,23 +1,39 @@
|
|||||||
import { Team } from '@prisma/client';
|
import { Team } from '@prisma/client';
|
||||||
import { canCreateTeam } from 'lib/auth';
|
import { canCreateTeam } from 'lib/auth';
|
||||||
import { uuid } from 'lib/crypto';
|
import { uuid } from 'lib/crypto';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth, useValidate } from 'lib/middleware';
|
||||||
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
import { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { createTeam, getTeamsByUserId } from 'queries';
|
import { createTeam, getTeamsByUserId } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface TeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {}
|
export interface TeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {}
|
||||||
export interface TeamsRequestBody extends SearchFilter<TeamSearchFilterType> {
|
export interface TeamsRequestBody {
|
||||||
name: string;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<TeamsRequestQuery, TeamsRequestBody>,
|
req: NextApiRequestQueryBody<TeamsRequestQuery, TeamsRequestBody>,
|
||||||
res: NextApiResponse<Team[] | Team>,
|
res: NextApiResponse<Team[] | Team>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
} = req.auth;
|
} = req.auth;
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
import { Team } from '@prisma/client';
|
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 { 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 {
|
export interface TeamsJoinRequestBody {
|
||||||
accessCode: string;
|
accessCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
POST: yup.object().shape({
|
||||||
|
accessCode: yup.string().max(50).required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, TeamsJoinRequestBody>,
|
req: NextApiRequestQueryBody<any, TeamsJoinRequestBody>,
|
||||||
res: NextApiResponse<Team>,
|
res: NextApiResponse<Team>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const { accessCode } = req.body;
|
const { accessCode } = req.body;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
|
|
||||||
import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
|
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 { NextApiResponse } from 'next';
|
||||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
|
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface UserRequestQuery {
|
export interface UserRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -15,12 +16,27 @@ export interface UserRequestBody {
|
|||||||
role: Role;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<UserRequestQuery, UserRequestBody>,
|
req: NextApiRequestQueryBody<UserRequestQuery, UserRequestBody>,
|
||||||
res: NextApiResponse<User>,
|
res: NextApiResponse<User>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user: { id: userId, isAdmin },
|
user: { id: userId, isAdmin },
|
||||||
} = req.auth;
|
} = 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 { NextApiRequestQueryBody, SearchFilter, TeamSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getTeamsByUserId } from 'queries';
|
import { getTeamsByUserId } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
export interface UserTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
export interface UserTeamsRequestQuery extends SearchFilter<TeamSearchFilterType> {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
@ -14,6 +15,13 @@ export interface UserTeamsRequestBody {
|
|||||||
shareId: string;
|
shareId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
...getFilterValidation('/All|Name|Owner/i'),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, UserTeamsRequestBody>,
|
req: NextApiRequestQueryBody<any, UserTeamsRequestBody>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -21,6 +29,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { user } = req.auth;
|
const { user } = req.auth;
|
||||||
const { id: userId } = req.query;
|
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 { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getEventDataUsage, getEventUsage, getUserWebsites } from 'queries';
|
import { getEventDataUsage, getEventUsage, getUserWebsites } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface UserUsageRequestQuery {
|
export interface UserUsageRequestQuery {
|
||||||
id: string;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<UserUsageRequestQuery>,
|
req: NextApiRequestQueryBody<UserUsageRequestQuery>,
|
||||||
res: NextApiResponse<UserUsageRequestResponse>,
|
res: NextApiResponse<UserUsageRequestResponse>,
|
||||||
@ -28,6 +37,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { user } = req.auth;
|
const { user } = req.auth;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
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 { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getWebsitesByUserId } from 'queries';
|
import { getWebsitesByUserId } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface UserWebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
export interface UserWebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
includeTeams?: boolean;
|
||||||
export interface UserWebsitesRequestBody {
|
onlyTeams?: boolean;
|
||||||
name: string;
|
|
||||||
domain: string;
|
|
||||||
shareId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<any, UserWebsitesRequestBody>,
|
req: NextApiRequestQueryBody<UserWebsitesRequestQuery>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) => {
|
) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { user } = req.auth;
|
const { user } = req.auth;
|
||||||
const { id: userId, page, filter, pageSize, includeTeams, onlyTeams } = req.query;
|
const { id: userId, page, filter, pageSize, includeTeams, onlyTeams } = req.query;
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { canCreateUser, canViewUsers } from 'lib/auth';
|
import { canCreateUser, canViewUsers } from 'lib/auth';
|
||||||
import { ROLES } from 'lib/constants';
|
import { ROLES } from 'lib/constants';
|
||||||
import { uuid } from 'lib/crypto';
|
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 { NextApiRequestQueryBody, Role, SearchFilter, User, UserSearchFilterType } from 'lib/types';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { createUser, getUserByUsername, getUsers } from 'queries';
|
import { createUser, getUserByUsername, getUsers } from 'queries';
|
||||||
@ -15,12 +16,31 @@ export interface UsersRequestBody {
|
|||||||
role?: Role;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<UsersRequestQuery, UsersRequestBody>,
|
req: NextApiRequestQueryBody<UsersRequestQuery, UsersRequestBody>,
|
||||||
res: NextApiResponse<User[] | User>,
|
res: NextApiResponse<User[] | User>,
|
||||||
) => {
|
) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
if (!(await canViewUsers(req.auth))) {
|
if (!(await canViewUsers(req.auth))) {
|
||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
@ -28,7 +48,7 @@ export default async (
|
|||||||
|
|
||||||
const { page, filter, pageSize } = req.query;
|
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);
|
return ok(res, users);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getActiveVisitors } from 'queries';
|
import { getActiveVisitors } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface WebsiteActiveRequestQuery {
|
export interface WebsiteActiveRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteActiveRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteActiveRequestQuery>,
|
||||||
res: NextApiResponse<WebsiteActive>,
|
res: NextApiResponse<WebsiteActive>,
|
||||||
@ -16,6 +23,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId } = req.query;
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { getWebsiteDateRange } from 'queries';
|
import { getWebsiteDateRange } from 'queries';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface WebsiteDateRangeRequestQuery {
|
export interface WebsiteDateRangeRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteDateRangeRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteDateRangeRequestQuery>,
|
||||||
res: NextApiResponse<WebsiteActive>,
|
res: NextApiResponse<WebsiteActive>,
|
||||||
@ -16,6 +23,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId } = req.query;
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
@ -16,9 +16,21 @@ export interface WebsiteEventsRequestQuery {
|
|||||||
unit: string;
|
unit: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
url: 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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteEventsRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteEventsRequestQuery>,
|
||||||
res: NextApiResponse<WebsiteMetric>,
|
res: NextApiResponse<WebsiteMetric>,
|
||||||
@ -26,7 +38,10 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(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);
|
const { startDate, endDate, unit } = await parseDateRangeQuery(req);
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
@ -44,7 +59,6 @@ export default async (
|
|||||||
timezone,
|
timezone,
|
||||||
unit,
|
unit,
|
||||||
url,
|
url,
|
||||||
eventName,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return ok(res, events);
|
return ok(res, events);
|
||||||
|
@ -2,7 +2,7 @@ import { NextApiResponse } from 'next';
|
|||||||
import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
|
||||||
import { Website, NextApiRequestQueryBody } from 'lib/types';
|
import { Website, NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite, canUpdateWebsite, canDeleteWebsite } from 'lib/auth';
|
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 { deleteWebsite, getWebsiteById, updateWebsite } from 'queries';
|
||||||
import { SHARE_ID_REGEX } from 'lib/constants';
|
import { SHARE_ID_REGEX } from 'lib/constants';
|
||||||
|
|
||||||
@ -16,6 +16,13 @@ export interface WebsiteRequestBody {
|
|||||||
shareId: string;
|
shareId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteRequestQuery, WebsiteRequestBody>,
|
req: NextApiRequestQueryBody<WebsiteRequestQuery, WebsiteRequestBody>,
|
||||||
res: NextApiResponse<Website>,
|
res: NextApiResponse<Website>,
|
||||||
@ -23,6 +30,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId } = req.query;
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -2,10 +2,11 @@ import { NextApiResponse } from 'next';
|
|||||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
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 { SESSION_COLUMNS, EVENT_COLUMNS, FILTER_COLUMNS } from 'lib/constants';
|
||||||
import { getPageviewMetrics, getSessionMetrics } from 'queries';
|
import { getPageviewMetrics, getSessionMetrics } from 'queries';
|
||||||
import { parseDateRangeQuery } from 'lib/query';
|
import { parseDateRangeQuery } from 'lib/query';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
export interface WebsiteMetricsRequestQuery {
|
export interface WebsiteMetricsRequestQuery {
|
||||||
id: string;
|
id: string;
|
||||||
@ -26,6 +27,12 @@ export interface WebsiteMetricsRequestQuery {
|
|||||||
language: string;
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteMetricsRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteMetricsRequestQuery>,
|
||||||
res: NextApiResponse<WebsiteMetric[]>,
|
res: NextApiResponse<WebsiteMetric[]>,
|
||||||
@ -33,6 +40,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id: websiteId,
|
id: websiteId,
|
||||||
type,
|
type,
|
||||||
|
@ -3,7 +3,7 @@ import { NextApiResponse } from 'next';
|
|||||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { getPageviewStats, getSessionStats } from 'queries';
|
import { getPageviewStats, getSessionStats } from 'queries';
|
||||||
import { parseDateRangeQuery } from 'lib/query';
|
import { parseDateRangeQuery } from 'lib/query';
|
||||||
|
|
||||||
@ -24,6 +24,13 @@ export interface WebsitePageviewRequestQuery {
|
|||||||
city?: string;
|
city?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsitePageviewRequestQuery>,
|
req: NextApiRequestQueryBody<WebsitePageviewRequestQuery>,
|
||||||
res: NextApiResponse<WebsitePageviews>,
|
res: NextApiResponse<WebsitePageviews>,
|
||||||
@ -31,6 +38,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id: websiteId,
|
id: websiteId,
|
||||||
timezone,
|
timezone,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { canViewWebsite } from 'lib/auth';
|
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 { NextApiRequestQueryBody, ReportSearchFilterType, SearchFilter } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
@ -9,6 +9,13 @@ export interface ReportsRequestQuery extends SearchFilter<ReportSearchFilterType
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<ReportsRequestQuery, any>,
|
req: NextApiRequestQueryBody<ReportsRequestQuery, any>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -16,6 +23,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId } = req.query;
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canUpdateWebsite } from 'lib/auth';
|
import { canUpdateWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { resetWebsite } from 'queries';
|
import { resetWebsite } from 'queries';
|
||||||
@ -9,6 +9,13 @@ export interface WebsiteResetRequestQuery {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -16,6 +23,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId } = req.query;
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
|
@ -2,7 +2,7 @@ import { subMinutes, differenceInMinutes } from 'date-fns';
|
|||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
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 { NextApiRequestQueryBody, WebsiteStats } from 'lib/types';
|
||||||
import { parseDateRangeQuery } from 'lib/query';
|
import { parseDateRangeQuery } from 'lib/query';
|
||||||
import { getWebsiteStats } from 'queries';
|
import { getWebsiteStats } from 'queries';
|
||||||
@ -24,6 +24,13 @@ export interface WebsiteStatsRequestQuery {
|
|||||||
city: string;
|
city: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteStatsRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteStatsRequestQuery>,
|
||||||
res: NextApiResponse<WebsiteStats>,
|
res: NextApiResponse<WebsiteStats>,
|
||||||
@ -31,6 +38,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id: websiteId,
|
id: websiteId,
|
||||||
url,
|
url,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NextApiRequestQueryBody } from 'lib/types';
|
import { NextApiRequestQueryBody } from 'lib/types';
|
||||||
import { canViewWebsite } from 'lib/auth';
|
import { canViewWebsite } from 'lib/auth';
|
||||||
import { useAuth, useCors } from 'lib/middleware';
|
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { EVENT_COLUMNS, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants';
|
import { EVENT_COLUMNS, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants';
|
||||||
@ -10,6 +10,13 @@ export interface WebsiteResetRequestQuery {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import * as yup from 'yup';
|
||||||
|
const schema = {
|
||||||
|
GET: yup.object().shape({
|
||||||
|
id: yup.string().uuid().required(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
req: NextApiRequestQueryBody<WebsiteResetRequestQuery>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
@ -17,6 +24,9 @@ export default async (
|
|||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const { id: websiteId, type } = req.query;
|
const { id: websiteId, type } = req.query;
|
||||||
|
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { canCreateWebsite } from 'lib/auth';
|
import { canCreateWebsite } from 'lib/auth';
|
||||||
import { uuid } from 'lib/crypto';
|
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 { NextApiRequestQueryBody, SearchFilter, WebsiteSearchFilterType } from 'lib/types';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
import { createWebsite } from 'queries';
|
import { createWebsite } from 'queries';
|
||||||
import userWebsites from 'pages/api/users/[id]/websites';
|
import userWebsites from 'pages/api/users/[id]/websites';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
import { getFilterValidation } from 'lib/yup';
|
||||||
|
|
||||||
export interface WebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {}
|
export interface WebsitesRequestQuery extends SearchFilter<WebsiteSearchFilterType> {}
|
||||||
|
|
||||||
@ -15,12 +17,25 @@ export interface WebsitesRequestBody {
|
|||||||
shareId: string;
|
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 (
|
export default async (
|
||||||
req: NextApiRequestQueryBody<WebsitesRequestQuery, WebsitesRequestBody>,
|
req: NextApiRequestQueryBody<WebsitesRequestQuery, WebsitesRequestBody>,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) => {
|
) => {
|
||||||
await useCors(req, res);
|
await useCors(req, res);
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
req.yup = schema;
|
||||||
|
await useValidate(req, res);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
@ -30,7 +45,7 @@ export default async (
|
|||||||
req.query.id = userId;
|
req.query.id = userId;
|
||||||
req.query.pageSize = 100;
|
req.query.pageSize = 100;
|
||||||
|
|
||||||
return userWebsites(req, res);
|
return userWebsites(req as any, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
|
Loading…
Reference in New Issue
Block a user