Files
NoIdle/backend/routes/users.js
root 6086c13be7 feat: Implementação completa do NoIdle - Cliente, Backend e Scripts
- Cliente Windows com modo silencioso e auto-start robusto
- Backend Node.js + API REST
- Frontend Next.js + Dashboard
- Scripts PowerShell de configuração e diagnóstico
- Documentação completa
- Build scripts para Windows e Linux
- Solução de auto-start após reinicialização

Resolução do problema: Cliente não voltava ativo após reboot
Solução: Registro do Windows + Task Scheduler + Modo silencioso
2025-11-16 22:56:35 +00:00

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;