From 4489f0a74dd7c79974425cd4cf8cea861b01c17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Thu, 19 Mar 2026 15:33:16 -0300 Subject: [PATCH] refactor: completely remove 'owner' role from RBAC system - The platform now strictly uses 'super_admin', 'admin', 'manager', and 'agent' to simplify permissions and match business requirements. --- backend/index.js | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/backend/index.js b/backend/index.js index 0c5f4a8..4c80264 100644 --- a/backend/index.js +++ b/backend/index.js @@ -203,11 +203,10 @@ apiRouter.post('/auth/verify', async (req, res) => { const tenantId = `tenant_${crypto.randomUUID().split('-')[0]}`; const userId = `u_${crypto.randomUUID().split('-')[0]}`; - await connection.query('INSERT INTO tenants (id, name, slug, admin_email) VALUES (?, ?, ?, ?)', + await connection.query('INSERT INTO tenants (id, name, slug, admin_email) VALUES (?, ?, ?, ?)', [tenantId, data.organization_name, data.organization_name.toLowerCase().replace(/ /g, '-'), email]); await connection.query('INSERT INTO users (id, tenant_id, name, email, password_hash, role) VALUES (?, ?, ?, ?, ?, ?)', - [userId, tenantId, data.full_name, email, data.password_hash, 'owner']); - await connection.query('DELETE FROM pending_registrations WHERE email = ?', [email]); + [userId, tenantId, data.full_name, email, data.password_hash, 'admin']); await connection.query('DELETE FROM pending_registrations WHERE email = ?', [email]); await connection.commit(); res.json({ message: 'Sucesso.' }); @@ -541,7 +540,8 @@ apiRouter.put('/users/:id', async (req, res) => { if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); const isSelf = req.user.id === req.params.id; - const isManagerOrAdmin = ['admin', 'owner', 'manager', 'super_admin'].includes(req.user.role); + const isManagerOrAdmin = ['admin', 'manager', 'super_admin'].includes(req.user.role); + const isAdmin = ['admin', 'super_admin'].includes(req.user.role); if (!isSelf && !isManagerOrAdmin) { return res.status(403).json({ error: 'Acesso negado.' }); @@ -551,8 +551,9 @@ apiRouter.put('/users/:id', async (req, res) => { return res.status(403).json({ error: 'Acesso negado.' }); } - const finalRole = isManagerOrAdmin && role !== undefined ? role : existing[0].role; - const finalTeamId = isManagerOrAdmin && team_id !== undefined ? team_id : existing[0].team_id; + // Only Admins can change roles and teams. Managers can only edit basic info of their team members. + const finalRole = isAdmin && role !== undefined ? role : existing[0].role; + const finalTeamId = isAdmin && team_id !== undefined ? team_id : existing[0].team_id; const finalStatus = isManagerOrAdmin && status !== undefined ? status : existing[0].status; const finalEmail = email !== undefined ? email : existing[0].email; const finalSoundEnabled = isSelf && sound_enabled !== undefined ? sound_enabled : (existing[0].sound_enabled ?? true); @@ -584,7 +585,7 @@ apiRouter.put('/users/:id', async (req, res) => { } }); -apiRouter.delete('/users/:id', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.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' }); @@ -733,7 +734,7 @@ apiRouter.get('/origins', async (req, res) => { } }); -apiRouter.post('/origins', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.post('/origins', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, tenantId } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; try { @@ -745,7 +746,7 @@ apiRouter.post('/origins', requireRole(['admin', 'owner', 'manager', 'super_admi } }); -apiRouter.put('/origins/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.put('/origins/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, teamIds } = req.body; try { if (name) { @@ -763,7 +764,7 @@ apiRouter.put('/origins/:id', requireRole(['admin', 'owner', 'manager', 'super_a } }); -apiRouter.delete('/origins/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.delete('/origins/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { try { await pool.query('DELETE FROM origin_items WHERE origin_group_id = ?', [req.params.id]); await pool.query('UPDATE teams SET origin_group_id = NULL WHERE origin_group_id = ?', [req.params.id]); @@ -774,7 +775,7 @@ apiRouter.delete('/origins/:id', requireRole(['admin', 'owner', 'manager', 'supe } }); -apiRouter.post('/origins/:id/items', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.post('/origins/:id/items', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, color_class } = req.body; try { const oid = `oriitm_${crypto.randomUUID().split('-')[0]}`; @@ -788,7 +789,7 @@ apiRouter.post('/origins/:id/items', requireRole(['admin', 'owner', 'manager', ' } }); -apiRouter.put('/origin_items/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.put('/origin_items/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, color_class } = req.body; try { const [existing] = await pool.query('SELECT * FROM origin_items WHERE id = ?', [req.params.id]); @@ -801,7 +802,7 @@ apiRouter.put('/origin_items/:id', requireRole(['admin', 'owner', 'manager', 'su } }); -apiRouter.delete('/origin_items/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.delete('/origin_items/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { try { await pool.query('DELETE FROM origin_items WHERE id = ?', [req.params.id]); res.json({ message: 'Origin item deleted.' }); @@ -862,7 +863,7 @@ apiRouter.get('/funnels', async (req, res) => { } }); -apiRouter.post('/funnels', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.post('/funnels', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, tenantId } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; try { @@ -874,7 +875,7 @@ apiRouter.post('/funnels', requireRole(['admin', 'owner', 'manager', 'super_admi } }); -apiRouter.put('/funnels/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.put('/funnels/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, teamIds } = req.body; try { if (name) { @@ -892,7 +893,7 @@ apiRouter.put('/funnels/:id', requireRole(['admin', 'owner', 'manager', 'super_a } }); -apiRouter.delete('/funnels/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.delete('/funnels/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { try { await pool.query('DELETE FROM funnel_stages WHERE funnel_id = ?', [req.params.id]); await pool.query('UPDATE teams SET funnel_id = NULL WHERE funnel_id = ?', [req.params.id]); @@ -903,7 +904,7 @@ apiRouter.delete('/funnels/:id', requireRole(['admin', 'owner', 'manager', 'supe } }); -apiRouter.post('/funnels/:id/stages', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.post('/funnels/:id/stages', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, color_class, order_index } = req.body; try { const sid = `stage_${crypto.randomUUID().split('-')[0]}`; @@ -917,7 +918,7 @@ apiRouter.post('/funnels/:id/stages', requireRole(['admin', 'owner', 'manager', } }); -apiRouter.put('/funnel_stages/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.put('/funnel_stages/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { const { name, color_class, order_index } = req.body; try { const [existing] = await pool.query('SELECT * FROM funnel_stages WHERE id = ?', [req.params.id]); @@ -933,7 +934,7 @@ apiRouter.put('/funnel_stages/:id', requireRole(['admin', 'owner', 'manager', 's } }); -apiRouter.delete('/funnel_stages/:id', requireRole(['admin', 'owner', 'manager', 'super_admin']), async (req, res) => { +apiRouter.delete('/funnel_stages/:id', requireRole(['admin', 'manager', 'super_admin']), async (req, res) => { try { await pool.query('DELETE FROM funnel_stages WHERE id = ?', [req.params.id]); res.json({ message: 'Stage deleted.' }); @@ -958,7 +959,7 @@ apiRouter.get('/search', async (req, res) => { if (req.user.role === 'super_admin') { // No extra filters - } else if (req.user.role === 'admin' || req.user.role === 'owner') { + } else if (req.user.role === 'admin') { membersQ += ' AND tenant_id = ?'; membersParams.push(req.user.tenant_id); } else if (req.user.role === 'manager') { @@ -976,7 +977,7 @@ apiRouter.get('/search', async (req, res) => { if (req.user.role === 'super_admin') { // No extra filters - } else if (req.user.role === 'admin' || req.user.role === 'owner') { + } else if (req.user.role === 'admin') { teamsQ += ' AND tenant_id = ?'; teamsParams.push(req.user.tenant_id); } else if (req.user.role === 'manager') { @@ -999,7 +1000,7 @@ apiRouter.get('/search', async (req, res) => { if (req.user.role === 'super_admin') { // No extra filters - } else if (req.user.role === 'admin' || req.user.role === 'owner') { + } else if (req.user.role === 'admin') { attendancesQ += ' AND a.tenant_id = ?'; attendancesParams.push(req.user.tenant_id); } else if (req.user.role === 'manager') { @@ -1090,7 +1091,7 @@ apiRouter.get('/attendances/:id', async (req, res) => { }); // --- API Key Management Routes --- -apiRouter.get('/api-keys', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.get('/api-keys', requireRole(['admin', 'super_admin']), async (req, res) => { try { const { tenantId } = req.query; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; @@ -1103,7 +1104,7 @@ apiRouter.get('/api-keys', requireRole(['admin', 'owner', 'super_admin']), async } }); -apiRouter.post('/api-keys', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.post('/api-keys', requireRole(['admin', 'super_admin']), async (req, res) => { const { name, tenantId } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; try { @@ -1123,7 +1124,7 @@ apiRouter.post('/api-keys', requireRole(['admin', 'owner', 'super_admin']), asyn } }); -apiRouter.delete('/api-keys/:id', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.delete('/api-keys/:id', requireRole(['admin', 'super_admin']), async (req, res) => { try { const [existing] = await pool.query('SELECT tenant_id FROM api_keys WHERE id = ?', [req.params.id]); if (existing.length === 0) return res.status(404).json({ error: 'Chave não encontrada' }); @@ -1301,7 +1302,7 @@ apiRouter.get('/teams', async (req, res) => { } }); -apiRouter.post('/teams', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.post('/teams', requireRole(['admin', 'super_admin']), async (req, res) => { const { name, description, tenantId } = req.body; const effectiveTenantId = req.user.role === 'super_admin' ? tenantId : req.user.tenant_id; try { @@ -1317,7 +1318,7 @@ apiRouter.post('/teams', requireRole(['admin', 'owner', 'super_admin']), async ( } }); -apiRouter.put('/teams/:id', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.put('/teams/:id', requireRole(['admin', 'super_admin']), async (req, res) => { const { name, description } = req.body; try { const [existing] = await pool.query('SELECT tenant_id FROM teams WHERE id = ?', [req.params.id]); @@ -1337,7 +1338,7 @@ apiRouter.put('/teams/:id', requireRole(['admin', 'owner', 'super_admin']), asyn } }); -apiRouter.delete('/teams/:id', requireRole(['admin', 'owner', 'super_admin']), async (req, res) => { +apiRouter.delete('/teams/:id', requireRole(['admin', 'super_admin']), async (req, res) => { try { const [existing] = await pool.query('SELECT tenant_id FROM teams WHERE id = ?', [req.params.id]); if (existing.length === 0) return res.status(404).json({ error: 'Not found' });