eletrotupi / tcc/ commit / 88d7ba7

api: move sign up to service & use zod schemas

Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago 88d7ba76dc54469a8a0bf8d1f5da7ba67d8b612a Browse files
Parents 7e976ff
Changed files
6
Additions
+62
Deletions
-47
api/src/controllers/users.ts
1 1
import { Request, Response, NextFunction } from 'express';
2 2
import {
3 -
  createUser,
4 3
  findUserById,
5 4
  findUserByEmail,
6 5
  updateUser,
7 6
  generateToken
8 7
} from '@app/models/user';
10 9
import {
11 10
  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
    }
46 45
  },
47 46

81 80
    } catch (err) {
82 81
      next(err);
83 82
    }
84 83
  }
85 84
};
api/src/lib/jwt.ts modified+1
api/src/lib/jwt.ts
1 1
import jwt from 'jsonwebtoken';
2 2
import crypto from 'crypto';
3 3

4 4
const JWT_SECRET = process.env.JWT_SECRET || 'development-secret-change-in-production';
5 5
const JWT_EXPIRES_IN = '7d';
29 29
  return expirationTime;
30 30
};
31 31

32 32
export const isTokenExpired = (expirationDate: Date): boolean => {
33 33
  return new Date() > expirationDate;
34 -
};
34 +
};
api/src/models/user.ts
1 1
import bcrypt from "bcryptjs";
2 2
import { prisma } from '../lib/prisma'
3 3
import {
4 4
  generateToken as createJWT,
5 5
  generatePasswordResetToken,
22 22
  lastName: string;
23 23
  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
}
59 30

60 31
const findUserById = async (id: number) => {
163 134
    data
164 135
  });
165 136
}
166 137

167 138
export {
168 -
  createUser,
169 139
  findUserById,
170 140
  generateToken,
171 141
  findUserByEmail,
172 142
  authenticateUser,
173 143
  initiatePasswordReset,
174 144
  resetPassword,
175 145
  updateUser
176 146
}
api/src/schemas/index.ts
1 1
export {
2 2
  CreateMoodSchema,
3 3
  MoodComponentSchema
4 4
} from '@app/schemas/mood.schema';
5 5

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';
/dev/null → api/src/schemas/user.schema.ts
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>;
/dev/null → api/src/services/user.service.ts
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 +
}