const express = require('express'); const crypto = require('crypto'); const { requireRole } = require('../middleware/auth'); const { canReadUser, canUpdateUser, canManageUserStatus, canChangeUserEmail, canManageUserRoleOrTeam, } = require('../policies/accessPolicy'); const { recordActivity } = require('../services/activityService'); const USER_PUBLIC_FIELDS = 'id, tenant_id, team_id, name, email, slug, role, status, bio, avatar_url, created_at'; const createUsersRouter = ({ pool, upload, transporter, getBaseUrl }) => { const router = express.Router(); router.get('/users', async (req, res) => { try { const { tenantId } = req.query; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; let q = `SELECT ${USER_PUBLIC_FIELDS} FROM users`; const params = []; if (effectiveTenantId && effectiveTenantId !== 'all') { q += ' WHERE tenant_id = ?'; params.push(effectiveTenantId); } if (req.user.role === 'manager') { if (req.user.team_id) { q += (params.length > 0 ? ' AND' : ' WHERE') + ' (team_id = ? OR id = ?)'; params.push(req.user.team_id, req.user.id); } else { q += (params.length > 0 ? ' AND' : ' WHERE') + ' id = ?'; params.push(req.user.id); } } const [rows] = await pool.query(q, params); res.json(rows); } catch (error) { res.status(500).json({ error: error.message }); } }); router.get('/users/:idOrSlug', async (req, res) => { try { const [rows] = await pool.query(`SELECT ${USER_PUBLIC_FIELDS} FROM users WHERE id = ? OR slug = ?`, [req.params.idOrSlug, req.params.idOrSlug]); if (!rows || rows.length === 0) return res.status(404).json({ error: 'Not found' }); if (!req.user || !req.user.role) return res.status(401).json({ error: 'Não autenticado' }); if (!canReadUser(req.user, rows[0])) return res.status(403).json({ error: 'Acesso negado.' }); res.json(rows[0]); } catch (error) { console.error('Error in GET /users/:idOrSlug:', error); res.status(500).json({ error: error.message }); } }); router.post('/users', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, email, role, team_id, tenant_id } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenant_id : req.user.tenant_id; let finalRole = role || 'agent'; let finalTeamId = team_id || null; if (req.user.role === 'manager') { if (!req.user.team_id) return res.status(403).json({ error: 'Gerente sem time não pode criar membros.' }); finalRole = 'agent'; finalTeamId = req.user.team_id; } try { 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'; 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'] ); const token = crypto.randomBytes(32).toString('hex'); await pool.query( 'INSERT INTO password_resets (email, token, expires_at) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 15 MINUTE))', [email, token] ); const setupLink = `${getBaseUrl(req)}/#/reset-password?token=${token}`; await transporter.sendMail({ from: `"Fasto" <${process.env.MAIL_FROM || 'nao-responda@blyzer.com.br'}>`, to: email, subject: 'Bem-vindo ao Fasto - Finalize seu cadastro', html: `

Olá, ${name}!

Você foi convidado para participar da equipe no Fasto.

Clique no botão abaixo para definir sua senha e acessar sua conta:

Finalizar Cadastro

Este link expira em 15 minutos. Se você não esperava este convite, ignore este e-mail.

Desenvolvido por Blyzer

` }); res.status(201).json({ id: uid, message: 'Convite enviado com sucesso.' }); } catch (error) { console.error('Invite error:', error); res.status(500).json({ error: error.message }); } }); router.put('/users/:id', async (req, res) => { const { name, bio, role, team_id, status, email } = req.body; 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.' }); const finalRole = canManageUserRoleOrTeam(req.user) && role !== undefined ? role : existing[0].role; const finalTeamId = canManageUserRoleOrTeam(req.user) && team_id !== undefined ? team_id : existing[0].team_id; const finalStatus = canManageUserStatus(req.user) && status !== undefined ? status : existing[0].status; const finalEmail = canChangeUserEmail(req.user, existing[0]) && email !== undefined ? email : existing[0].email; if (finalEmail !== existing[0].email) { const [emailCheck] = await pool.query('SELECT id FROM users WHERE email = ? AND id != ?', [finalEmail, req.params.id]); if (emailCheck.length > 0) return res.status(400).json({ error: 'E-mail já está em uso.' }); } await pool.query( 'UPDATE users SET name = ?, bio = ?, email = ?, role = ?, team_id = ?, status = ? WHERE id = ?', [name || existing[0].name, bio !== undefined ? bio : existing[0].bio, finalEmail, finalRole, finalTeamId || null, finalStatus, req.params.id] ); if (finalTeamId && finalTeamId !== existing[0].team_id && existing[0].status === 'active') { const [team] = await pool.query('SELECT name FROM teams WHERE id = ?', [finalTeamId]); if (team.length > 0) { await recordActivity(pool, { userId: req.params.id, type: 'info', title: 'Novo Time', message: `Você foi adicionado ao time ${team[0].name}.`, link: '/', }); } } res.json({ message: 'User updated successfully.' }); } catch (error) { console.error('Update user error:', error); res.status(500).json({ error: error.message }); } }); router.delete('/users/:id', requireRole(['admin', 'super_admin']), async (req, res) => { try { const [existing] = await pool.query('SELECT tenant_id FROM users WHERE id = ?', [req.params.id]); if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); if (req.user.role !== 'super_admin' && existing[0].tenant_id !== req.user.tenant_id) { return res.status(403).json({ error: 'Acesso negado.' }); } await pool.query('DELETE FROM users WHERE id = ?', [req.params.id]); res.json({ message: 'User deleted successfully.' }); } catch (error) { console.error('Delete user error:', error); res.status(500).json({ error: error.message }); } }); router.post('/users/:id/avatar', upload.single('avatar'), async (req, res) => { try { if (!req.file) return res.status(400).json({ error: 'Nenhum arquivo enviado.' }); if (req.user.id !== req.params.id && req.user.role !== 'super_admin') { return res.status(403).json({ error: 'Acesso negado.' }); } const avatarUrl = `/uploads/${req.file.filename}`; await pool.query('UPDATE users SET avatar_url = ? WHERE id = ?', [avatarUrl, req.params.id]); res.json({ avatarUrl }); } catch (error) { console.error('Avatar upload error:', error); res.status(500).json({ error: error.message }); } }); return router; }; module.exports = { createUsersRouter };