From 70309b2f9ded0aa2a81946ca48c9bea843d4876b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Tue, 9 Jun 2026 16:39:43 -0300 Subject: [PATCH] Reapply password and date filter updates --- backend/index.js | 33 ++++- index.html | 2 +- src/components/DateRangePicker.tsx | 216 +++++++++++++++++++++++------ src/pages/Login.tsx | 7 +- src/pages/TeamManagement.tsx | 39 +++++- 5 files changed, 238 insertions(+), 59 deletions(-) diff --git a/backend/index.js b/backend/index.js index 65aaf30..6dc0a92 100644 --- a/backend/index.js +++ b/backend/index.js @@ -23,6 +23,7 @@ const { const app = express(); const USER_PUBLIC_FIELDS = 'id, tenant_id, team_id, name, email, slug, role, status, bio, avatar_url, sound_enabled, created_at'; +const MIN_PASSWORD_LENGTH = 6; app.use(createCorsMiddleware({ allowedOrigins, isProduction })); app.use(express.json()); @@ -447,8 +448,9 @@ apiRouter.get('/users/:idOrSlug', async (req, res) => { // Convidar Novo Membro (Admin criando usuário) apiRouter.post('/users', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { - const { name, email, role, team_id, tenant_id } = req.body; + const { name, email, role, team_id, tenant_id, password } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenant_id : req.user.tenant_id; + const shouldSetPassword = typeof password === 'string' && password.length > 0; // Strict RBAC: Managers can only create agents and assign them to their own team let finalRole = role || 'agent'; @@ -460,20 +462,30 @@ apiRouter.post('/users', requireRole(['admin', 'manager', 'super_admin']), async finalTeamId = req.user.team_id; // Force assignment to manager's team } try { + if (shouldSetPassword && req.user.role !== 'admin' && req.user.role !== 'super_admin') { + return res.status(403).json({ error: 'Apenas admins podem definir senha de usuários.' }); + } + if (shouldSetPassword && password.length < MIN_PASSWORD_LENGTH) { + return res.status(400).json({ error: `A senha deve ter pelo menos ${MIN_PASSWORD_LENGTH} caracteres.` }); + } // 1. Verificar se e-mail já existe const [existing] = await pool.query('SELECT id FROM users WHERE email = ?', [email]); if (existing.length > 0) return res.status(400).json({ error: 'E-mail já cadastrado.' }); const uid = `u_${crypto.randomUUID().split('-')[0]}`; const slug = `${name.toLowerCase().replace(/[^a-z0-9]+/g, '-')}-${crypto.randomBytes(4).toString('hex')}`; - const placeholderHash = 'pending_setup'; // Usuário não pode logar com isso + const passwordHash = shouldSetPassword ? await bcrypt.hash(password, 10) : 'pending_setup'; // 2. Criar Usuário await pool.query( 'INSERT INTO users (id, tenant_id, team_id, name, email, password_hash, slug, role, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', - [uid, effectiveTenantId, finalTeamId, name, email, placeholderHash, slug, finalRole, 'active'] + [uid, effectiveTenantId, finalTeamId, name, email, passwordHash, slug, finalRole, 'active'] ); + if (shouldSetPassword) { + return res.status(201).json({ id: uid, message: 'Membro criado com senha definida.' }); + } + // 3. Gerar Token de Setup de Senha (reusando lógica de reset) const token = crypto.randomBytes(32).toString('hex'); await pool.query( @@ -511,12 +523,19 @@ apiRouter.post('/users', requireRole(['admin', 'manager', 'super_admin']), async }); apiRouter.put('/users/:id', async (req, res) => { - const { name, bio, role, team_id, status, email, sound_enabled } = req.body; + const { name, bio, role, team_id, status, email, sound_enabled, password } = req.body; + const shouldUpdatePassword = typeof password === 'string' && password.length > 0; try { const [existing] = await pool.query('SELECT * FROM users WHERE id = ?', [req.params.id]); if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); if (!canUpdateUser(req.user, existing[0])) return res.status(403).json({ error: 'Acesso negado.' }); + if (shouldUpdatePassword && req.user.role !== 'admin' && req.user.role !== 'super_admin') { + return res.status(403).json({ error: 'Apenas admins podem alterar senha de usuários.' }); + } + if (shouldUpdatePassword && password.length < MIN_PASSWORD_LENGTH) { + return res.status(400).json({ error: `A senha deve ter pelo menos ${MIN_PASSWORD_LENGTH} caracteres.` }); + } // Only Admins can change roles and teams. Managers can only edit basic info of their team members. const finalRole = canManageUserRoleOrTeam(req.user) && role !== undefined ? role : existing[0].role; @@ -535,6 +554,12 @@ apiRouter.put('/users/:id', async (req, res) => { [name || existing[0].name, bio !== undefined ? bio : existing[0].bio, finalEmail, finalRole, finalTeamId || null, finalStatus, finalSoundEnabled, req.params.id] ); + if (shouldUpdatePassword) { + const passwordHash = await bcrypt.hash(password, 10); + await pool.query('UPDATE users SET password_hash = ? WHERE id = ?', [passwordHash, req.params.id]); + await pool.query('DELETE FROM password_resets WHERE email = ?', [finalEmail]); + } + // Trigger Notification for Team Change if (finalTeamId && finalTeamId !== existing[0].team_id && existing[0].status === 'active') { const [team] = await pool.query('SELECT name FROM teams WHERE id = ?', [finalTeamId]); diff --git a/index.html b/index.html index ec0e148..342819f 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - Fasto | Management + Fasto