From 9a70e4e9fd3f8ad59b453c790da91e7d76e782cf Mon Sep 17 00:00:00 2001 From: apsr23 <apsr23@inf.ufpr.br> Date: Wed, 19 Mar 2025 10:56:19 -0300 Subject: [PATCH] Issue #11 ADD login user route --- src/handlers/user.ts | 145 +++++++++++++++++++++----------- src/index.ts | 7 +- src/validators/userValidator.ts | 23 +++-- 3 files changed, 110 insertions(+), 65 deletions(-) diff --git a/src/handlers/user.ts b/src/handlers/user.ts index 19da118..36041a6 100644 --- a/src/handlers/user.ts +++ b/src/handlers/user.ts @@ -4,7 +4,7 @@ import { db } from "@/db"; import { or, eq } from 'drizzle-orm'; import { usersTable } from "@/db/schema"; import bcrypt from "bcrypt"; -import { z } from 'zod'; +import jwt from 'jsonwebtoken'; export default class User{ static userRequestValidation (req: Request){ //valida a requisição do usuário @@ -76,61 +76,106 @@ export default class User{ return res.status(408).json({ error: "Error during user deletion" }); } } -} - -export const createUser = async (req: Request, res: Response) => { - try { - - const parsedData = userSchema.safeParse(req.body); - - if (!parsedData.success) { - return res.status(400).json({ error: "Dados Inválidos", details: parsedData.error.errors }); + static createUser = async (req: Request, res: Response) => { + try { + + // validar com o Zod + const parsedData = userSchema.safeParse(req.body); + if (!parsedData.success) { + return res.status(400).json({ error: "Dados Inválidos", details: parsedData.error.errors }); + } + + // desestruturação dos dados para inserir no banco + const { name, email, password, birthday, cpf, money, cyberpsychosis, cyberLimit } = parsedData.data; + + const hashedPassword = await bcrypt.hash(password, 10); + + const result = await db.insert(usersTable).values({ + name, + email, + password: hashedPassword, + birthday, + cpf, + money, + cyberpsychosis, + cyberLimit, + }).returning(); + + res.status(201).json({ message: "Usuário criado com sucesso", user: result }); + + } catch (error) { + console.error(error); + return res.status(500).json({ error: "Erro ao inserir o usuário." }); } + } - const { name, email, password, birthday, cpf, money, cyberpsychosis, cyberLimit } = parsedData.data; - - const hashedPassword = await bcrypt.hash(password, 10); - - const result = await db.insert(usersTable).values({ - name, - email, - password: hashedPassword, - birthday, - cpf, - money, - cyberpsychosis, - cyberLimit, - }).returning(); - - res.status(201).json({ message: "Usuário criado com sucesso", user: result }); + static readUser = async (req: Request, res: Response) => { + try { + + // extrair id e nome da requisição + const { id, name } = req.params; + + // converter id para número + const parsedId = parseInt(id, 10); + if (isNaN(parsedId)) { + return res.status(400).json({ error: "ID inválido" }); + } + + // validar com o Zod + const parsedData = userSchema.safeParse({ id: parsedId, name }); + if (!parsedData.success) { + return res.status(400).json({ error: "Dados Inválidos", details: parsedData.error.errors }); + } + + const result = await db.select().from(usersTable) + .where(or(eq(usersTable.id, parsedId), eq(usersTable.name, name))) + .limit(1); + if (result.length === 0) { // array vazio + return res.status(404).json({ error: "Usuário não encontrado" }); + } + + // desestruturar result e remover password + const { password, ...userWithoutPassword } = result[0]; + + return res.status(200).json(userWithoutPassword); - } catch (error) { - console.error(error); - return res.status(500).json({ error: "Erro ao inserir o usuário. Por favor, tente novamente mais tarde." }); - } - }; + } catch (error) { + console.error(error); + return res.status(500).json({ error: "Erro ao buscar o usuário." }); + } + } + + static loginUser = async (req: Request, res: Response) => { + try { - export const readUser = async (req: Request, res: Response) => { - try { - - const { id, nome } = req.params; //extracao da requisicao + const { email, password } = req.body; - const parsedData = userSchema.safeParse({ id, nome }); - - if (!parsedData.success) { - return res.status(400).json({ error: "Dados Inválidos", details: parsedData.error.errors });} - - const result = await db.select().from(usersTable).where(or(eq(usersTable.id, id), eq(usersTable.nome, nome))).limit(1); + // buscar usuário + const user = await db.select().from(usersTable).where(eq(usersTable.email, email)).limit(1); + if (user.length === 0) { // array vazio + return res.status(404).json({ message: 'Usuário não encontrado' }); + } - if (result.length === 0) { //array vazio - return res.status(404).json({ error: "Usuário não encontrado" }); - } + // validar senha + const isPasswordValid = await bcrypt.compare(password, user[0].password); + if (!isPasswordValid) { + return res.status(401).json({ message: 'Senha incorreta' }); + } - delete result[0].password; + // criar chave secreta para assinar e validar tokens, guardar no .env + const jwtSecret = process.env["JWT_SECRET"]; + if (!jwtSecret) { + return res.status(500).json({ error: "Chave secreta do JWT não configurada" }); + } - return res.status(200).json(result[0]); - } catch (error) { - console.error(error); - return res.status(500).json({ error: "Erro ao buscar o usuário. Por favor, tente novamente mais tarde." }); + //gerar token + const token = jwt.sign({ id: user[0].id, email: user[0].email }, jwtSecret, { expiresIn: '1h' }); + + return res.status(200).json({ message: 'Login bem-sucedido', token }); + + } catch (error) { + console.error(error); + return res.status(500).json({ error: "Erro ao realizar login" }); + } } - } \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index b062d03..120d5ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import express, { urlencoded, json } from 'express' import dotenv from 'dotenv' -import { createUser, readUser } from './handlers/user'; +import User from './handlers/user'; import Implant from '@/handlers/implants' import { Auth } from '@/middleware/auth' @@ -14,8 +14,9 @@ app.use(urlencoded({ extended: true })) app.use(json()) // Rotas de usuário -app.post('/addUser', createUser) -app.get('/getUser', readUser) +app.post('/addUser', User.createUser) +app.get('/getUser', User.readUser) +app.post('/loginUser', User.loginUser) // Definir a porta e iniciar o servidor const PORT = process.env['PORT'] || 3000 diff --git a/src/validators/userValidator.ts b/src/validators/userValidator.ts index 79def35..52e52c9 100644 --- a/src/validators/userValidator.ts +++ b/src/validators/userValidator.ts @@ -1,15 +1,14 @@ import { z } from 'zod' export const userSchema = z.object({ - name: z.string().min(3, { message: "O nome deve ter pelo menos 3 caracteres" }), - password: z.string().min(8, { message: "A senha deve ter pelo menos 8 caracteres" }), - email: z.string().email({ message: "Formato de e-mail inválido" }), - birthday: z.string().refine(date => !isNaN(Date.parse(date)), - { message: "Data de nascimento inválida" }), - cpf: z.string().min(11, { message: "O CPF deve ter pelo menos 11 números" }) - .regex(/^\d{3}\.\d{3}\.\d{3}-\d{2}$/, "CPF inválido (use o formato 000.000.000-00)"), - money: z.number().nonnegative(), - cyberpsychosis: z.number(), //a definir restrições de validação - cyberLimit: z.number(), - }); - + name: z.string().min(3, { message: "O nome deve ter pelo menos 3 caracteres" }), + password: z.string().min(8, { message: "A senha deve ter pelo menos 8 caracteres" }), + email: z.string().email({ message: "Formato de e-mail inválido" }), + birthday: z.string().refine(date => !isNaN(Date.parse(date)), + { message: "Data de nascimento inválida" }), + cpf: z.string().min(11, { message: "O CPF deve ter pelo menos 11 números" }) + .regex(/^\d{3}\.\d{3}\.\d{3}-\d{2}$/, "CPF inválido (use o formato 000.000.000-00)"), + money: z.number().nonnegative(), + cyberpsychosis: z.number(), // a definir restrições de validação + cyberLimit: z.number(), +}); \ No newline at end of file -- GitLab