leonardo 3 mesi fa
parent
commit
496aa03627
2 ha cambiato i file con 57 aggiunte e 10 eliminazioni
  1. 49 6
      src/controllers/Usuario.Controller.js
  2. 8 4
      src/routes/chat.js

+ 49 - 6
src/controllers/Usuario.Controller.js

@@ -3,7 +3,36 @@ import { db } from "../db/knex.js";
 import { signAccessToken, generateRefreshToken, hashRefreshToken } from "../services/authTokens.js";
 import { config } from "../config/index.js";
 
-async function recordFailedAttempt(_login) {}
+const _failedAttempts = new Map();
+const MAX_ATTEMPTS = 5;
+const BLOCK_MS = 15 * 60 * 1000;
+
+function recordFailedAttempt(login) {
+    const now = Date.now();
+    const entry = _failedAttempts.get(login) ?? { count: 0, blockedUntil: 0 };
+    entry.count += 1;
+    if (entry.count >= MAX_ATTEMPTS) {
+        entry.blockedUntil = now + BLOCK_MS;
+        entry.count = 0;
+    }
+    _failedAttempts.set(login, entry);
+}
+
+function isLoginBlocked(login) {
+    const entry = _failedAttempts.get(login);
+    if (!entry) return false;
+    if (entry.blockedUntil > Date.now()) return true;
+    if (entry.blockedUntil > 0) _failedAttempts.delete(login);
+    return false;
+}
+
+function requireAdmin(req, res) {
+    if (String(req.user?.nivel) !== "3") {
+        res.status(403).send({ status: false, msg: "Acesso negado. Apenas administradores podem realizar esta ação." });
+        return false;
+    }
+    return true;
+}
 
 function formatarUsuario(usuario) {
     return {
@@ -18,7 +47,8 @@ function formatarUsuario(usuario) {
 }
 
 export const UsuarioController = {
-    Listar: async function (_req, res, next) {
+    Listar: async function (req, res, next) {
+        if (!requireAdmin(req, res)) return;
         try {
             const usuarios = await db("usuarios")
                 .select("Id", "Nome", "Login", "Email", "Status", "Nivel", "Setor")
@@ -47,13 +77,17 @@ export const UsuarioController = {
             const loginInformado = String(loginBody).trim();
             const senhaInformada = String(senhaBody);
 
+            if (isLoginBlocked(loginInformado)) {
+                return res.status(429).send({ status: false, msg: "Muitas tentativas incorretas. Tente novamente em 15 minutos." });
+            }
+
             const usuario = await db("usuarios")
                 .where(loginInformado.includes("@") ? { Email: loginInformado } : { Login: loginInformado })
                 .first();
 
             if (!usuario) {
-                await recordFailedAttempt(loginInformado);
-                return res.status(401).send({ status: false, msg: "Usuario não localizado!" });
+                recordFailedAttempt(loginInformado);
+                return res.status(401).send({ status: false, msg: "Combinação de usuário e senha inválida!" });
             }
 
             if (String(usuario.Status) === "0") {
@@ -62,10 +96,12 @@ export const UsuarioController = {
 
             const passwordIsValid = bcrypt.compareSync(senhaInformada, String(usuario.Senha ?? ""));
             if (!passwordIsValid) {
-                await recordFailedAttempt(loginInformado);
-                return res.status(401).send({ status: false, msg: "Combinacao de usuario e senha invalida!" });
+                recordFailedAttempt(loginInformado);
+                return res.status(401).send({ status: false, msg: "Combinação de usuário e senha inválida!" });
             }
 
+            _failedAttempts.delete(loginInformado);
+
             let accessToken = null;
             let refreshToken = null;
 
@@ -140,6 +176,7 @@ export const UsuarioController = {
     },
 
     Criar: async function (req, res, next) {
+        if (!requireAdmin(req, res)) return;
         try {
             const { Nome, Login, Email, Senha, Nivel, Setor } = req.body ?? {};
 
@@ -147,6 +184,10 @@ export const UsuarioController = {
                 return res.status(400).send({ status: false, msg: "Todos os campos são obrigatórios!" });
             }
 
+            if (String(Senha).length < 6) {
+                return res.status(400).send({ status: false, msg: "Senha deve ter pelo menos 6 caracteres!" });
+            }
+
             const loginTrimado = String(Login).trim();
             const existente = await db("usuarios").where({ Login: loginTrimado }).first();
             if (existente) {
@@ -172,6 +213,7 @@ export const UsuarioController = {
     },
 
     Atualizar: async function (req, res, next) {
+        if (!requireAdmin(req, res)) return;
         try {
             const id = Number(req.params.id);
             if (!id) return res.status(400).send({ status: false, msg: "ID inválido!" });
@@ -198,6 +240,7 @@ export const UsuarioController = {
     },
 
     ToggleStatus: async function (req, res, next) {
+        if (!requireAdmin(req, res)) return;
         try {
             const id = Number(req.params.id);
             if (!id) return res.status(400).send({ status: false, msg: "ID inválido!" });

+ 8 - 4
src/routes/chat.js

@@ -28,10 +28,14 @@ chatRouter.post("/", async (req, res, next) => {
 
     const userId = req.user?.sub;
     if (userId && body.conversationId) {
-      Promise.all([
-        addMessage(body.conversationId, { role: "user", content: body.message }),
-        addMessage(body.conversationId, { role: "assistant", content: result.answer, sources: result.sources })
-      ]).catch((err) => console.error("[chat] falha ao salvar mensagem:", err));
+      try {
+        await Promise.all([
+          addMessage(body.conversationId, { role: "user", content: body.message }),
+          addMessage(body.conversationId, { role: "assistant", content: result.answer, sources: result.sources })
+        ]);
+      } catch (err) {
+        console.error("[chat] falha ao salvar mensagem:", err);
+      }
     }
 
     res.json(result);