api: move sign up to service & use zod schemas
Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago
88d7ba76dc54469a8a0bf8d1f5da7ba67d8b612a Browse filesParents
7e976ffChanged 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
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 +
}