api: move sign up to service & use zod schemas
Parents:
7e976ff6 file(s) changed
- api/src/controllers/users.ts +16 -17
- api/src/lib/jwt.ts +1 -0
- api/src/models/user.ts +0 -30
- api/src/schemas/index.ts +8 -0
- api/src/schemas/user.schema.ts +19 -0
- api/src/services/user.service.ts +18 -0
api/src/controllers/users.ts
@@ -1,6 +1,5 @@
1 1 import { Request, Response, NextFunction } from 'express';
2 2 import {
3 - createUser,
4 3 findUserById,
5 4 findUserByEmail,
6 5 updateUser,
@@ -12,34 +11,34 @@ validateRequiredFields
12 11 } from '@app/utils/validators'
13 12
14 13 import { getQueue, MailJobName } from '@app/lib/queue';
14 + import {
15 + CreateUserSchema
16 + } from '@app/schemas';
17 +
18 + import {
19 + createUser
20 + } from '@app/services/user.service';
15 21
16 22 export const UsersController = {
17 23 create: async (req: Request, res: Response, next: NextFunction) => {
18 24 try {
19 - const { email, password, firstName, lastName } = req.body;
25 + const parsed = CreateUserSchema.safeParse(req.body.user);
20 26
21 - // Validate required fields
22 - const requiredValidation = validateRequiredFields(req.body, [
23 - 'email', 'password', 'firstName'
24 - ]);
25 -
26 - if (!requiredValidation.valid) {
27 - return res.status(400).json({
28 - error: "Campos faltantes",
29 - missing: requiredValidation.missing
30 - });
27 + if (!parsed.success) {
28 + return res.status(422).json({
29 + errors: parsed.error!.issues
30 + })
31 31 }
32 32
33 - const user = await createUser(
34 - email, firstName, lastName, password
35 - );
36 -
33 + const user = await createUser(parsed.data);
37 34 const jwtToken = generateToken(user.id, user.email);
38 35
39 36 const mailQueue = getQueue('mail');
40 37 const job = await mailQueue.add(MailJobName.WelcomeEmail, { userId: user.id });
41 38
42 - res.status(201).json({ token: jwtToken });
39 + res.status(201).json({
40 + token: jwtToken
41 + });
43 42 } catch (err) {
44 43 next(err);
45 44 }
api/src/lib/jwt.ts
@@ -31,4 +31,4 @@ };
31 31
32 32 export const isTokenExpired = (expirationDate: Date): boolean => {
33 33 return new Date() > expirationDate;
34 - };
34 + };
api/src/models/user.ts
@@ -24,35 +24,6 @@ password?: string;
24 24 encryptedPassword?: string;
25 25 };
26 26
27 - const createUser = async (email: string, firstName: string, lastName: string, password: string) => {
28 - // Validate email format
29 - if (!isValidEmail(email)) {
30 - throw new InvalidEmailError()
31 - }
32 -
33 - // Validate password strength
34 - if (!isValidPassword(password)) {
35 - throw new ShortPasswordError()
36 - }
37 -
38 - const encryptedPassword = bcrypt.hashSync(password, 10);
39 -
40 - try {
41 - const user = await prisma.user.create({
42 - data: {
43 - email,
44 - firstName,
45 - lastName,
46 - encryptedPassword
47 - }
48 - });
49 -
50 - return user;
51 - } catch (err: any) {
52 - throw err;
53 - }
54 - }
55 -
56 27 const generateToken = (userId: number, email: string) => {
57 28 return createJWT(userId, email);
58 29 }
@@ -165,7 +136,6 @@ });
165 136 }
166 137
167 138 export {
168 - createUser,
169 139 findUserById,
170 140 generateToken,
171 141 findUserByEmail,
api/src/schemas/index.ts
@@ -6,3 +6,11 @@
6 6 export type {
7 7 CreateMoodInput
8 8 } from '@app/schemas/mood.schema';
9 +
10 + export {
11 + CreateUserSchema
12 + } from '@app/schemas/user.schema';
13 +
14 + export type {
15 + CreateUserInput
16 + } from '@app/schemas/user.schema';
api/src/schemas/user.schema.ts
@@ -0,0 +1,19 @@
1 + import { z } from 'zod';
2 +
3 + export const CreateUserSchema = z.object({
4 + firstName: z.string(),
5 + lastName: z.string().optional(),
6 + email: z.string().email(),
7 + password: z.string().min(6)
8 + });
9 +
10 + export type CreateUserInput = z.infer<typeof CreateUserSchema>;
11 +
12 + export const UpdateUserSchema = z.object({
13 + firstName: z.string(),
14 + lastName: z.string().optional(),
15 + email: z.string().email(),
16 + password: z.string().min(6)
17 + });
18 +
19 + export type UpdateUserInput = z.infer<typeof UpdateUserSchema>;
api/src/services/user.service.ts
@@ -0,0 +1,18 @@
1 + import { prisma } from '@app/lib/prisma';
2 + import { User } from '@prisma/client';
3 + import bcrypt from 'bcryptjs';
4 +
5 + import {
6 + CreateUserInput
7 + } from "@app/schemas";
8 +
9 + export const createUser = async(input: CreateUserInput): Promise<User> => {
10 + const { password, ...data } = input;
11 + const encryptedPassword = bcrypt.hashSync(password, 10);
12 +
13 + return await prisma.user.create({
14 + data: {
15 + ...data, encryptedPassword
16 + }
17 + })
18 + }