const jwt = require('jsonwebtoken'); const path = require('path'); const db = require('../database'); const authConfig = require('../config/auth'); // ── Rate limit de login (em memória) — proteção anti força bruta ── const LOGIN_MAX = 10; // tentativas falhas permitidas const LOGIN_JANELA_MS = 15 * 60 * 1000; // por janela de 15 minutos const loginTentativas = new Map(); function chaveLogin(req, login) { return (req.ip || '') + '|' + (login || ''); } function loginBloqueado(req, login) { const e = loginTentativas.get(chaveLogin(req, login)); if (!e) return false; if (Date.now() - e.inicio > LOGIN_JANELA_MS) { loginTentativas.delete(chaveLogin(req, login)); return false; } return e.count >= LOGIN_MAX; } function registrarFalhaLogin(req, login) { const k = chaveLogin(req, login); const agora = Date.now(); const e = loginTentativas.get(k); if (!e || agora - e.inicio > LOGIN_JANELA_MS) loginTentativas.set(k, { count: 1, inicio: agora }); else e.count++; } function limparTentativasLogin(req, login) { loginTentativas.delete(chaveLogin(req, login)); } class AuthController { /** * Exibe a página de login * Endpoint: GET /app/:alias/login */ static async loginPage(req, res) { const loginPath = path.resolve(__dirname, '../public/login.html'); res.sendFile(loginPath); } /** * Login do usuário no aplicativo * Endpoint: POST /app/:alias/login * Body: { USU_LOGIN, USU_SENHA } */ static async login(req, res) { try { const { alias } = req.params; const { USU_LOGIN, USU_SENHA } = req.body; // Validação dos campos obrigatórios if (!USU_LOGIN || !USU_SENHA) { return res.status(400).json({ success: false, error: 'Os campos USU_LOGIN e USU_SENHA são obrigatórios.', }); } if (loginBloqueado(req, USU_LOGIN)) { return res.status(429).json({ success: false, error: 'Muitas tentativas de login. Aguarde alguns minutos e tente novamente.', }); } // Busca o usuário pelo login const sql = ` SELECT USU_CODIGO_ID, USU_NIVEL_ID, USU_NOME, USU_LOGIN, USU_EMAIL, USU_STATUS, USU_ACESSO_WEB, USU_CAMINHO_FOTO, USU_DT_ULTIMO_ACESSO, USU_TIPO, USU_TIPO_CHAT FROM USUARIOS WHERE USU_LOGIN = ? AND (USU_SENHA = ? OR USU_SENHA_WEB = ?) AND COALESCE(USU_ACESSO_WEB, '0') IN ('1', 'S') AND USU_STATUS = 'A' `; const result = await db.query(alias, sql, [USU_LOGIN, USU_SENHA, USU_SENHA]); if (result.length === 0) { registrarFalhaLogin(req, USU_LOGIN); return res.status(401).json({ success: false, error: 'Credenciais inválidas ou usuário sem permissão de acesso.', }); } // Login bem-sucedido: zera o contador de tentativas limparTentativasLogin(req, USU_LOGIN); const user = result[0]; // Busca as empresas que o usuário tem acesso const empresas = await db.query(alias, 'SELECT USE_EMPRESA_ID FROM USUARIOS_EMPRESA WHERE USE_USUARIO_ID = ?', [user.USU_CODIGO_ID] ); const empresasIds = empresas.map(e => Number(e.USE_EMPRESA_ID)).filter(n => !isNaN(n)); // Atualiza a data do último acesso await db.execute( alias, `UPDATE USUARIOS SET USU_DT_ULTIMO_ACESSO = CURRENT_DATE WHERE USU_CODIGO_ID = ?`, [user.USU_CODIGO_ID] ); // Dados do usuário para o token const payload = { id: user.USU_CODIGO_ID, nivelId: user.USU_NIVEL_ID, nome: user.USU_NOME.trim(), login: user.USU_LOGIN.trim(), email: user.USU_EMAIL ? user.USU_EMAIL.trim() : null, tipo: user.USU_TIPO ? user.USU_TIPO.trim() : null, tipoChat: user.USU_TIPO_CHAT ? user.USU_TIPO_CHAT.trim() : null, alias, empresas: empresasIds, // empresas que o usuário pode acessar }; // Gera o token JWT const token = jwt.sign(payload, authConfig.secret, { expiresIn: authConfig.expiresIn, issuer: authConfig.issuer, }); // Retorna o token e dados do usuário return res.json({ success: true, message: 'Login realizado com sucesso.', alias, token, expiresIn: authConfig.expiresIn, user: { ...payload, foto: user.USU_CAMINHO_FOTO ? user.USU_CAMINHO_FOTO.trim() : null, ultimoAcesso: user.USU_DT_ULTIMO_ACESSO, }, }); } catch (err) { console.error('Erro no login:', err); return res.status(500).json({ success: false, error: `Erro interno ao realizar login: ${err.message}`, }); } } /** * Valida o token atual e retorna dados atualizados do usuário * Endpoint: GET /app/:alias/me */ static async me(req, res) { try { return res.json({ success: true, alias: req.params.alias, user: { ...req.user, authType: req.authType || 'jwt', }, }); } catch (err) { console.error('Erro ao buscar usuário:', err); return res.status(500).json({ success: false, error: `Erro ao buscar dados do usuário: ${err.message}`, }); } } /** * Dashboard do app * Endpoint: GET /app/:alias/dashboard */ static async dashboard(req, res) { const dashboardPath = path.resolve(__dirname, '../public/dashboard.html'); res.sendFile(dashboardPath); } } module.exports = AuthController;