aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/auth/auth.controller.ts
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
commitef55d9bb24430610b60c1b77821307cd72c08c55 (patch)
treee6935a810b34370b0a4c821e55f6397aac50c832 /api/src/modules/auth/auth.controller.ts
downloadhelpdesk-master.tar.gz
helpdesk-master.zip
feat: upload of the projectHEADmaster
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'api/src/modules/auth/auth.controller.ts')
-rw-r--r--api/src/modules/auth/auth.controller.ts114
1 files changed, 114 insertions, 0 deletions
diff --git a/api/src/modules/auth/auth.controller.ts b/api/src/modules/auth/auth.controller.ts
new file mode 100644
index 0000000..8aec643
--- /dev/null
+++ b/api/src/modules/auth/auth.controller.ts
@@ -0,0 +1,114 @@
+/**
+ * auth.controller.ts
+ * Lógica de negócio de autenticação.
+ */
+
+import bcrypt from 'bcryptjs';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+import {
+ findUserByEmail,
+ findUserById,
+ createUser,
+} from './auth.repository.js';
+import type { RegisterBody, LoginBody } from './auth.schema.js';
+
+const SALT_ROUNDS = 12;
+
+// ─── POST /auth/register ──────────────────────────────────────────────────────
+
+export async function registerHandler(
+ request: FastifyRequest<{ Body: RegisterBody }>,
+ reply: FastifyReply,
+) {
+ const { name, email, password, role } = request.body;
+ const db = request.server.db;
+
+ // Verifica duplicidade de e-mail
+ const existing = await findUserByEmail(db, email);
+ if (existing) {
+ return reply.conflict('Este e-mail já está cadastrado.');
+ }
+
+ // Apenas admins podem criar agentes ou outros admins
+ if ((role === 'agent' || role === 'admin')) {
+ const callerUser = request.user as { role?: string } | undefined;
+ if (!callerUser || callerUser.role !== 'admin') {
+ return reply.forbidden('Apenas administradores podem criar agentes ou admins.');
+ }
+ }
+
+ // Hash da senha (bcrypt com salt 12 ≈ ~250ms — seguro e aceitável)
+ const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
+
+ const user = await createUser(db, {
+ name,
+ email,
+ password: hashedPassword,
+ role,
+ });
+
+ // Assina o JWT com o payload mínimo necessário
+ const token = await reply.jwtSign({
+ id: user.id,
+ email: user.email,
+ role: user.role,
+ });
+
+ return reply.code(201).send({ token, user });
+}
+
+// ─── POST /auth/login ─────────────────────────────────────────────────────────
+
+export async function loginHandler(
+ request: FastifyRequest<{ Body: LoginBody }>,
+ reply: FastifyReply,
+) {
+ const { email, password } = request.body;
+ const db = request.server.db;
+
+ const user = await findUserByEmail(db, email);
+
+ // Mensagem genérica: não revela se o e-mail existe ou não (segurança)
+ const INVALID_MSG = 'E-mail ou senha incorretos.';
+
+ if (!user) {
+ // Executa bcrypt mesmo sem usuário para evitar timing attacks
+ await bcrypt.compare(password, '$2b$12$invalidhashplaceholderthatnevermatchesanypassword');
+ return reply.unauthorized(INVALID_MSG);
+ }
+
+ if (!user.isActive) {
+ return reply.forbidden('Conta desativada. Entre em contato com o administrador.');
+ }
+
+ const isPasswordValid = await bcrypt.compare(password, user.password);
+ if (!isPasswordValid) {
+ return reply.unauthorized(INVALID_MSG);
+ }
+
+ const token = await reply.jwtSign({
+ id: user.id,
+ email: user.email,
+ role: user.role,
+ });
+
+ // Retorna o user sem o campo password
+ const { password: _pwd, ...safeUser } = user;
+ return reply.send({ token, user: safeUser });
+}
+
+// ─── GET /auth/me ─────────────────────────────────────────────────────────────
+
+export async function meHandler(
+ request: FastifyRequest,
+ reply: FastifyReply,
+) {
+ // request.user foi injetado pelo decorator authenticate
+ const user = await findUserById(request.server.db, request.user.id);
+
+ if (!user) {
+ return reply.unauthorized('Usuário não encontrado.');
+ }
+
+ return reply.send(user);
+}