const express = require('express'); const crypto = require('crypto'); const { requireRole } = require('../middleware/auth'); const { canReadUser, canUpdateUser, canManageUserStatus, canChangeUserEmail, canManageUserRoleOrTeam, } = require('../policies/accessPolicy'); const USER_PUBLIC_FIELDS = 'id, tenant_id, team_id, name, email, slug, role, status, bio, avatar_url, sound_enabled, 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: `
Você foi convidado para participar da equipe no Fasto.
Clique no botão abaixo para definir sua senha e acessar sua conta:
Este link expira em 15 minutos. Se você não esperava este convite, ignore este e-mail.
Desenvolvido por Blyzer