diff --git a/src/controllers/auth.ts b/src/controllers/auth.ts index 0a75a7a..e3f3eef 100644 --- a/src/controllers/auth.ts +++ b/src/controllers/auth.ts @@ -8,11 +8,14 @@ import { AuthenticatedRequest, LoginParams, } from 'types'; +import { validateLoginSchema, validateUserSchema } from 'data-schemas/auth'; @Route('v1/auth') export class AuthControllerV1 extends Controller { @Post('/register') public async register(@Body() user: CreateUserParams): Promise { + validateUserSchema(user); + const authReturn = await AuthService.register(user); this.setStatus(httpStatus.CREATED); return authReturn; @@ -20,6 +23,8 @@ export class AuthControllerV1 extends Controller { @Post('/login') public async login(@Body() loginParams: LoginParams): Promise { + validateLoginSchema(loginParams); + const authReturn = await AuthService.login(loginParams); this.setStatus(httpStatus.OK); return authReturn; diff --git a/src/data-schemas/auth.ts b/src/data-schemas/auth.ts new file mode 100644 index 0000000..0f8eed6 --- /dev/null +++ b/src/data-schemas/auth.ts @@ -0,0 +1,45 @@ +import { ZodError, z } from 'zod'; + +import { CreateUserParams, LoginParams } from 'types'; +import { ApiError } from 'utils/apiError'; +import { errors } from 'config/errors'; +import { formatZodError } from 'utils/validator'; + +const MINIMUM_PASSWORD_LENGTH = 10; + +const userCreationSchema = z.object({ + email: z.string().email({ message: 'Invalid email' }), + name: z.string(), + password: z + .string() + .min(MINIMUM_PASSWORD_LENGTH, { message: "Can't be an empty password" }), +}); + +export type UserCreationSchema = z.infer; + +const loginSchema = z.object({ + email: z.string().email({ message: 'Invalid email' }), + password: z + .string() + .min(MINIMUM_PASSWORD_LENGTH, { message: "Can't be an empty password" }), +}); + +export type UserLoginSchema = z.infer; + +export const validateUserSchema = (user: CreateUserParams) => { + try { + userCreationSchema.parse(user); + } catch (err) { + const formattedZodError = formatZodError(err as ZodError); + throw new ApiError(errors.VALIDATION_ERROR, true, formattedZodError); + } +}; + +export const validateLoginSchema = (loginParams: LoginParams) => { + try { + loginSchema.parse(loginParams); + } catch (err) { + const formattedZodError = formatZodError(err as ZodError); + throw new ApiError(errors.VALIDATION_ERROR, true, formattedZodError); + } +}; diff --git a/src/middlewares/error.ts b/src/middlewares/error.ts index 37552d3..33dc1be 100644 --- a/src/middlewares/error.ts +++ b/src/middlewares/error.ts @@ -21,7 +21,13 @@ export const errorConverter = ( next(error); }; -export const errorHandler = (err: ApiError, req: Request, res: Response) => { +export const errorHandler = ( + err: ApiError, + req: Request, + res: Response, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + next: NextFunction, +) => { let { httpCode, message } = err; if (!err.isOperational) { httpCode = diff --git a/src/utils/validator.ts b/src/utils/validator.ts new file mode 100644 index 0000000..fee471c --- /dev/null +++ b/src/utils/validator.ts @@ -0,0 +1,11 @@ +import { ZodError } from 'zod'; + +export const formatZodError = (zodError: ZodError) => { + const formattedError = zodError.errors.map((error) => { + return { + field: error.path, + error: error.message, + }; + }); + return formattedError; +}; diff --git a/tsconfig.json b/tsconfig.json index 8e5b07a..4ecccac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], + "data-schemas/*": ["data-schemas/*"], "root/*": ["../*"] } } diff --git a/tsoa.json b/tsoa.json index b48521b..5377a80 100644 --- a/tsoa.json +++ b/tsoa.json @@ -28,6 +28,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], + "data-schemas/*": ["data-schemas/*"], "root/*": ["../*"] } }