diff options
| author | Lucas Faria Mendes <lucas.fariamo08@gmail.com> | 2026-08-16 23:17:42 +0000 |
|---|---|---|
| committer | Lucas Faria Mendes <lucas.fariamo08@gmail.com> | 2026-08-16 23:17:42 +0000 |
| commit | 375482e654a01615683867e732ac9e458c7cbe74 (patch) | |
| tree | 12d1b31b8deb6bf8590bcdbc2cfb52dcc553e006 /api/src/modules/auth | |
| parent | 2c2dfba45357bbd205a59145ae55a2779c911ae0 (diff) | |
| download | yaba-375482e654a01615683867e732ac9e458c7cbe74.tar.gz yaba-375482e654a01615683867e732ac9e458c7cbe74.zip | |
Signed-off-by: Lucas Faria Mendes <lucas.fariamo08@gmail.com>
Diffstat (limited to 'api/src/modules/auth')
| -rw-r--r-- | api/src/modules/auth/auth.routes.ts | 40 | ||||
| -rw-r--r-- | api/src/modules/auth/auth.schema.ts | 16 | ||||
| -rw-r--r-- | api/src/modules/auth/auth.service.ts | 41 |
3 files changed, 97 insertions, 0 deletions
diff --git a/api/src/modules/auth/auth.routes.ts b/api/src/modules/auth/auth.routes.ts new file mode 100644 index 0000000..d388694 --- /dev/null +++ b/api/src/modules/auth/auth.routes.ts @@ -0,0 +1,40 @@ +import type { FastifyPluginAsync } from 'fastify'; +import { ZodError } from 'zod'; +import { loginSchema, registerSchema } from './auth.schema'; +import * as authService from './auth.service'; + +const authRoutes: FastifyPluginAsync = async (fastify) => { + fastify.post('/auth/register', async (request, reply) => { + try { + const body = registerSchema.parse(request.body); + const user = await authService.registerUser(body); + + const token = fastify.jwt.sign({ sub: user.id }); + + return reply.status(201).send({ data: { user, token } }); + } catch (err: any) { + if (err instanceof ZodError) { + return reply.status(400).send({ error: 'Dados inválidos.', details: err.errors }); + } + return reply.status(400).send({ error: err.message }); + } + }); + + fastify.post('/auth/login', async (request, reply) => { + try { + const body = loginSchema.parse(request.body); + const user = await authService.loginUser(body); + + const token = fastify.jwt.sign({ sub: user.id }); + + return reply.send({ data: { user, token } }); + } catch (err: any) { + if (err instanceof ZodError) { + return reply.status(400).send({ error: 'Dados inválidos.', details: err.errors }); + } + return reply.status(401).send({ error: err.message }); + } + }); +}; + +export default authRoutes; diff --git a/api/src/modules/auth/auth.schema.ts b/api/src/modules/auth/auth.schema.ts new file mode 100644 index 0000000..550c670 --- /dev/null +++ b/api/src/modules/auth/auth.schema.ts @@ -0,0 +1,16 @@ +import { z } from 'zod'; + +export const registerSchema = z.object({ + name: z.string().min(2, 'Nome deve ter pelo menos 2 caracteres'), + email: z.string().email('Email inválido'), + password: z.string().min(6, 'Senha deve ter pelo menos 6 caracteres'), + gross_salary: z.coerce.number().min(0).optional(), +}); + +export const loginSchema = z.object({ + email: z.string().email('Email inválido'), + password: z.string().min(6, 'Senha inválida'), +}); + +export type RegisterData = z.infer<typeof registerSchema>; +export type LoginData = z.infer<typeof loginSchema>; diff --git a/api/src/modules/auth/auth.service.ts b/api/src/modules/auth/auth.service.ts new file mode 100644 index 0000000..c874b05 --- /dev/null +++ b/api/src/modules/auth/auth.service.ts @@ -0,0 +1,41 @@ +import bcrypt from 'bcryptjs'; +import { eq } from 'drizzle-orm'; +import { db } from '../../db/client'; +import { users } from '../../db/schema/users'; +import type { LoginData, RegisterData } from './auth.schema'; + +export async function registerUser(data: RegisterData) { + const existingUser = await db.select().from(users).where(eq(users.email, data.email)).limit(1); + if (existingUser.length > 0) { + throw new Error('Email já está em uso.'); + } + + const salt = await bcrypt.genSalt(10); + const passwordHash = await bcrypt.hash(data.password, salt); + + const [newUser] = await db.insert(users).values({ + name: data.name, + email: data.email, + password_hash: passwordHash, + gross_salary: data.gross_salary || 0, + }).returning(); + + const { password_hash, ...userWithoutPassword } = newUser; + return userWithoutPassword; +} + +export async function loginUser(data: LoginData) { + const [user] = await db.select().from(users).where(eq(users.email, data.email)).limit(1); + + if (!user) { + throw new Error('Email ou senha inválidos.'); + } + + const isValid = await bcrypt.compare(data.password, user.password_hash); + if (!isValid) { + throw new Error('Email ou senha inválidos.'); + } + + const { password_hash, ...userWithoutPassword } = user; + return userWithoutPassword; +} |