422 lines
15 KiB
JavaScript
422 lines
15 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const router = express.Router();
|
||
|
|
const bcrypt = require('bcrypt');
|
||
|
|
const { query } = require('../config/database');
|
||
|
|
const { authenticateToken } = require('../middleware/auth');
|
||
|
|
|
||
|
|
router.get('/', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
console.log('GET /api/users - User:', req.user);
|
||
|
|
console.log('GET /api/users - Company ID:', req.user.company_id);
|
||
|
|
|
||
|
|
// Buscar usuários
|
||
|
|
const usersResult = await query(
|
||
|
|
`SELECT
|
||
|
|
id,
|
||
|
|
email,
|
||
|
|
name,
|
||
|
|
role,
|
||
|
|
is_active,
|
||
|
|
created_at
|
||
|
|
FROM users
|
||
|
|
WHERE company_id = $1
|
||
|
|
ORDER BY created_at DESC`,
|
||
|
|
[req.user.company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
// Buscar contagem de dispositivos por usuário
|
||
|
|
const devicesCountResult = await query(
|
||
|
|
`SELECT
|
||
|
|
user_id,
|
||
|
|
COUNT(*) as devices_count
|
||
|
|
FROM devices
|
||
|
|
WHERE company_id = $1 AND user_id IS NOT NULL
|
||
|
|
GROUP BY user_id`,
|
||
|
|
[req.user.company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
// Criar mapa de contagem de dispositivos
|
||
|
|
const devicesCountMap = {};
|
||
|
|
devicesCountResult.rows.forEach(row => {
|
||
|
|
devicesCountMap[row.user_id] = parseInt(row.devices_count) || 0;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Adicionar contagem de dispositivos aos usuários
|
||
|
|
const users = usersResult.rows.map(user => ({
|
||
|
|
id: user.id,
|
||
|
|
email: user.email,
|
||
|
|
name: user.name,
|
||
|
|
role: user.role,
|
||
|
|
is_active: user.is_active,
|
||
|
|
created_at: user.created_at,
|
||
|
|
devices_count: devicesCountMap[user.id] || 0
|
||
|
|
}));
|
||
|
|
|
||
|
|
console.log('GET /api/users - Resultados encontrados:', users.length);
|
||
|
|
|
||
|
|
res.json({ success: true, users: users });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao listar usuários:', error);
|
||
|
|
console.error('Erro detalhado:', error.message);
|
||
|
|
console.error('Stack:', error.stack);
|
||
|
|
res.status(500).json({ error: 'Erro ao listar usuários', details: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { email, name, password } = req.body;
|
||
|
|
|
||
|
|
if (!email || !name || !password) {
|
||
|
|
return res.status(400).json({ error: 'Email, nome e senha são obrigatórios' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verificar se o email já existe
|
||
|
|
const existingUser = await query(
|
||
|
|
'SELECT id FROM users WHERE email = $1',
|
||
|
|
[email.toLowerCase()]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (existingUser.rows.length > 0) {
|
||
|
|
return res.status(409).json({ error: 'Este email já está cadastrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||
|
|
|
||
|
|
const result = await query(
|
||
|
|
'INSERT INTO users (email, name, password, company_id, is_active) VALUES ($1, $2, $3, $4, true) RETURNING id, email, name, role, is_active',
|
||
|
|
[email.toLowerCase(), name, hashedPassword, req.user.company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
res.status(201).json({ success: true, user: result.rows[0] });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao criar usuário:', error);
|
||
|
|
|
||
|
|
// Tratamento específico para erros conhecidos
|
||
|
|
if (error.code === '23505') { // Violação de constraint única
|
||
|
|
return res.status(409).json({ error: 'Este email já está cadastrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
res.status(500).json({ error: 'Erro ao criar usuário', details: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Rota para obter o perfil do usuário logado (admin)
|
||
|
|
router.get('/me', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const result = await query(
|
||
|
|
'SELECT id, email, name, role, company_id, is_active, created_at FROM admin_users WHERE id = $1',
|
||
|
|
[req.user.id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({ success: true, user: result.rows[0] });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao obter perfil:', error);
|
||
|
|
res.status(500).json({ error: 'Erro ao obter perfil' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Rota para alterar a senha do próprio usuário (admin)
|
||
|
|
router.put('/me/password', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { currentPassword, newPassword } = req.body;
|
||
|
|
|
||
|
|
if (!currentPassword || !newPassword) {
|
||
|
|
return res.status(400).json({ error: 'Senha atual e nova senha são obrigatórias' });
|
||
|
|
}
|
||
|
|
|
||
|
|
if (newPassword.length < 6) {
|
||
|
|
return res.status(400).json({ error: 'A nova senha deve ter pelo menos 6 caracteres' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Buscar o usuário atual
|
||
|
|
const userResult = await query(
|
||
|
|
'SELECT id, password FROM admin_users WHERE id = $1',
|
||
|
|
[req.user.id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (userResult.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const user = userResult.rows[0];
|
||
|
|
|
||
|
|
// Verificar senha atual
|
||
|
|
const validPassword = await bcrypt.compare(currentPassword, user.password);
|
||
|
|
if (!validPassword) {
|
||
|
|
return res.status(401).json({ error: 'Senha atual incorreta' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Hash da nova senha
|
||
|
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||
|
|
|
||
|
|
// Atualizar senha
|
||
|
|
await query(
|
||
|
|
'UPDATE admin_users SET password = $1 WHERE id = $2',
|
||
|
|
[hashedPassword, req.user.id]
|
||
|
|
);
|
||
|
|
|
||
|
|
res.json({ success: true, message: 'Senha alterada com sucesso' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao alterar senha:', error);
|
||
|
|
res.status(500).json({ error: 'Erro ao alterar senha' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Rota para atualizar perfil do próprio usuário (admin)
|
||
|
|
router.put('/me', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { name, email } = req.body;
|
||
|
|
|
||
|
|
if (!name) {
|
||
|
|
return res.status(400).json({ error: 'Nome é obrigatório' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Se email foi fornecido, verificar se não está em uso por outro usuário
|
||
|
|
if (email) {
|
||
|
|
const existingUser = await query(
|
||
|
|
'SELECT id FROM admin_users WHERE email = $1 AND id != $2',
|
||
|
|
[email.toLowerCase(), req.user.id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (existingUser.rows.length > 0) {
|
||
|
|
return res.status(409).json({ error: 'Este email já está em uso' });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Atualizar perfil
|
||
|
|
const updateFields = [];
|
||
|
|
const updateValues = [];
|
||
|
|
let paramCount = 1;
|
||
|
|
|
||
|
|
if (name) {
|
||
|
|
updateFields.push(`name = $${paramCount++}`);
|
||
|
|
updateValues.push(name);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (email) {
|
||
|
|
updateFields.push(`email = $${paramCount++}`);
|
||
|
|
updateValues.push(email.toLowerCase());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (updateFields.length === 0) {
|
||
|
|
return res.status(400).json({ error: 'Nenhum campo para atualizar' });
|
||
|
|
}
|
||
|
|
|
||
|
|
updateValues.push(req.user.id);
|
||
|
|
const queryText = `UPDATE admin_users SET ${updateFields.join(', ')} WHERE id = $${paramCount} RETURNING id, email, name, role, company_id, is_active`;
|
||
|
|
|
||
|
|
const result = await query(queryText, updateValues);
|
||
|
|
|
||
|
|
res.json({ success: true, user: result.rows[0] });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao atualizar perfil:', error);
|
||
|
|
res.status(500).json({ error: 'Erro ao atualizar perfil' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// TROCAR SENHA DE UM USUÁRIO (admin pode trocar senha de qualquer usuário)
|
||
|
|
// IMPORTANTE: Esta rota deve vir ANTES de /:id para não ser capturada pela rota genérica
|
||
|
|
router.put('/:id/password', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const { newPassword } = req.body;
|
||
|
|
const company_id = req.user.company_id;
|
||
|
|
const loggedUserId = req.user.id; // ID do usuário logado (da tabela admin_users)
|
||
|
|
|
||
|
|
console.log(`🔵 PUT /api/users/${id}/password - Requisição recebida`);
|
||
|
|
console.log(`🔵 Body recebido:`, JSON.stringify({ newPassword: newPassword ? '***' : null }, null, 2));
|
||
|
|
console.log(`🔵 Company ID:`, company_id);
|
||
|
|
console.log(`🔵 ID do usuário logado:`, loggedUserId);
|
||
|
|
console.log(`🔵 ID do usuário a alterar:`, id);
|
||
|
|
|
||
|
|
if (!newPassword) {
|
||
|
|
return res.status(400).json({ error: 'Nova senha é obrigatória' });
|
||
|
|
}
|
||
|
|
|
||
|
|
if (newPassword.length < 6) {
|
||
|
|
return res.status(400).json({ error: 'A senha deve ter pelo menos 6 caracteres' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// IMPORTANTE: Verificar se o usuário a alterar é o próprio usuário logado
|
||
|
|
// Se for, alterar na tabela admin_users. Caso contrário, alterar na tabela users
|
||
|
|
const idNum = parseInt(id);
|
||
|
|
const loggedIdNum = parseInt(loggedUserId);
|
||
|
|
|
||
|
|
// Buscar email do usuário logado
|
||
|
|
const loggedUserResult = await query(
|
||
|
|
'SELECT email FROM admin_users WHERE id = $1',
|
||
|
|
[loggedIdNum]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (loggedUserResult.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário logado não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const loggedUserEmail = loggedUserResult.rows[0].email;
|
||
|
|
|
||
|
|
// Buscar email do usuário a ser alterado
|
||
|
|
const userToChangeResult = await query(
|
||
|
|
'SELECT id, email FROM users WHERE id = $1 AND company_id = $2',
|
||
|
|
[idNum, company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (userToChangeResult.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const userToChangeEmail = userToChangeResult.rows[0].email;
|
||
|
|
|
||
|
|
// Se o email corresponde ao usuário logado, alterar na tabela admin_users
|
||
|
|
if (userToChangeEmail.toLowerCase() === loggedUserEmail.toLowerCase()) {
|
||
|
|
console.log(`🔵 Email corresponde ao usuário logado - alterando na tabela admin_users`);
|
||
|
|
|
||
|
|
const adminCheck = await query(
|
||
|
|
'SELECT id FROM admin_users WHERE email = $1',
|
||
|
|
[loggedUserEmail]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (adminCheck.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário admin não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const adminId = adminCheck.rows[0].id;
|
||
|
|
|
||
|
|
// Hash da nova senha
|
||
|
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||
|
|
|
||
|
|
// Atualizar senha na tabela admin_users
|
||
|
|
await query(
|
||
|
|
'UPDATE admin_users SET password = $1 WHERE id = $2',
|
||
|
|
[hashedPassword, adminId]
|
||
|
|
);
|
||
|
|
|
||
|
|
console.log(`✅ Senha do admin_user ${adminId} (${loggedUserEmail}) alterada com sucesso na tabela admin_users`);
|
||
|
|
} else {
|
||
|
|
// Alterar senha de outro usuário (users)
|
||
|
|
console.log(`🔵 Alterando senha de outro usuário na tabela users`);
|
||
|
|
|
||
|
|
// Hash da nova senha
|
||
|
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
||
|
|
|
||
|
|
// Atualizar senha na tabela users
|
||
|
|
await query(
|
||
|
|
'UPDATE users SET password = $1 WHERE id = $2 AND company_id = $3',
|
||
|
|
[hashedPassword, idNum, company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
console.log(`✅ Senha do usuário ${idNum} (${userToChangeEmail}) alterada com sucesso na tabela users`);
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({ success: true, message: 'Senha alterada com sucesso' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao alterar senha do usuário:', error);
|
||
|
|
res.status(500).json({ error: 'Erro ao alterar senha', details: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// EDITAR USUÁRIO
|
||
|
|
router.put('/:id', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const { name, email, role, is_active } = req.body;
|
||
|
|
const company_id = req.user.company_id;
|
||
|
|
|
||
|
|
console.log(`🔵 PUT /api/users/${id} - Requisição recebida`);
|
||
|
|
console.log(`🔵 Body recebido:`, JSON.stringify({ name, email, role, is_active }, null, 2));
|
||
|
|
console.log(`🔵 Company ID:`, company_id);
|
||
|
|
console.log(`🔵 User ID do token:`, req.user.id);
|
||
|
|
|
||
|
|
// Verificar se o usuário existe e pertence à company
|
||
|
|
const userCheck = await query(
|
||
|
|
'SELECT id FROM users WHERE id = $1 AND company_id = $2',
|
||
|
|
[id, company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (userCheck.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Se email foi fornecido, verificar se não está em uso por outro usuário
|
||
|
|
if (email) {
|
||
|
|
const existingUser = await query(
|
||
|
|
'SELECT id FROM users WHERE email = $1 AND id != $2 AND company_id = $3',
|
||
|
|
[email.toLowerCase(), id, company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (existingUser.rows.length > 0) {
|
||
|
|
return res.status(409).json({ error: 'Este email já está em uso' });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Construir query dinamicamente
|
||
|
|
const updateFields = [];
|
||
|
|
const updateValues = [];
|
||
|
|
let paramCount = 1;
|
||
|
|
|
||
|
|
if (name !== undefined) {
|
||
|
|
updateFields.push(`name = $${paramCount++}`);
|
||
|
|
updateValues.push(name);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (email !== undefined) {
|
||
|
|
updateFields.push(`email = $${paramCount++}`);
|
||
|
|
updateValues.push(email.toLowerCase());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (role !== undefined) {
|
||
|
|
updateFields.push(`role = $${paramCount++}`);
|
||
|
|
updateValues.push(role);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_active !== undefined) {
|
||
|
|
updateFields.push(`is_active = $${paramCount++}`);
|
||
|
|
updateValues.push(is_active);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (updateFields.length === 0) {
|
||
|
|
return res.status(400).json({ error: 'Nenhum campo para atualizar' });
|
||
|
|
}
|
||
|
|
|
||
|
|
updateValues.push(id, company_id);
|
||
|
|
const queryText = `UPDATE users
|
||
|
|
SET ${updateFields.join(', ')}
|
||
|
|
WHERE id = $${paramCount++} AND company_id = $${paramCount}
|
||
|
|
RETURNING id, email, name, role, is_active, created_at`;
|
||
|
|
|
||
|
|
const result = await query(queryText, updateValues);
|
||
|
|
|
||
|
|
console.log(`✅ Usuário ${id} atualizado com sucesso`);
|
||
|
|
|
||
|
|
res.json({ success: true, user: result.rows[0] });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao atualizar usuário:', error);
|
||
|
|
console.error('Erro detalhado:', error.message);
|
||
|
|
res.status(500).json({ error: 'Erro ao atualizar usuário', details: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.delete('/:id', authenticateToken, async (req, res) => {
|
||
|
|
try {
|
||
|
|
const result = await query(
|
||
|
|
'DELETE FROM users WHERE id = $1 AND company_id = $2 RETURNING *',
|
||
|
|
[req.params.id, req.user.company_id]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result.rows.length === 0) {
|
||
|
|
return res.status(404).json({ error: 'Usuário não encontrado' });
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({ success: true, message: 'Usuário deletado' });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro ao deletar usuário:', error);
|
||
|
|
res.status(500).json({ error: 'Erro ao deletar usuário' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|