Files
rx.scoreodonto.com/dental-server/routes/auth.js
T
VPS 4 Builder dc1801f4a7
continuous-integration/webhook Falha no deploy (rx.scoreodonto.com)
Ajuste de cadastro/gerenciamento de usuarios e seguranca de endpoints
2026-05-26 20:48:21 +02:00

297 lines
9.2 KiB
JavaScript

const express = require('express');
const router = express.Router();
const db = require('../database');
const { verifyPassword, generateToken, hashPassword, authenticateToken } = require('../auth');
// ================================================================
// POST /api/auth/login - Login
// ================================================================
router.post('/login', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({
error: 'Usuário e senha são obrigatórios'
});
}
// Buscar usuário
const user = await db.get(
'SELECT * FROM users WHERE username = ? OR email = ?',
[username, username]
);
if (!user) {
return res.status(401).json({
error: 'Usuário ou senha inválidos'
});
}
// Verificar senha
const validPassword = await verifyPassword(password, user.password_hash);
if (!validPassword) {
return res.status(401).json({
error: 'Usuário ou senha inválidos'
});
}
// Gerar token
const token = generateToken(user);
// Resposta
res.json({
success: true,
token: token,
user: {
id: user.id,
username: user.username,
email: user.email,
clinic_name: user.clinic_name,
pc_name: user.pc_name,
is_admin: user.is_admin
}
});
} catch (error) {
console.error('Erro no login:', error);
res.status(500).json({ error: 'Erro interno do servidor' });
}
});
// ================================================================
// GET /api/auth/verify - Verificar token
// ================================================================
router.get('/verify', async (req, res) => {
try {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token não fornecido' });
}
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, require('../auth').JWT_SECRET);
const user = await db.get('SELECT * FROM users WHERE id = ?', [decoded.id]);
if (!user) {
return res.status(404).json({ error: 'Usuário não encontrado' });
}
res.json({
valid: true,
user: {
id: user.id,
username: user.username,
email: user.email,
clinic_name: user.clinic_name,
pc_name: user.pc_name,
is_admin: user.is_admin
}
});
} catch (error) {
res.status(401).json({ error: 'Token inválido' });
}
});
// ================================================================
// POST /api/auth/logout - Logout
// ================================================================
router.post('/logout', (req, res) => {
res.json({
success: true,
message: 'Logout realizado com sucesso'
});
});
// ================================================================
// POST /api/auth/create-user - Criar usuário (admin only)
// ================================================================
router.post('/create-user', authenticateToken, async (req, res) => {
try {
if (!req.user.is_admin) {
return res.status(403).json({ error: 'Apenas administradores podem criar usuários' });
}
const { username, email, password, clinic_name, pc_name, is_admin } = req.body;
if (!username || !email || !password) {
return res.status(400).json({
error: 'Username, email e senha são obrigatórios'
});
}
// Verificar se usuário já existe
const existing = await db.get(
'SELECT * FROM users WHERE username = ? OR email = ?',
[username, email]
);
if (existing) {
return res.status(409).json({
error: 'Usuário ou email já cadastrado'
});
}
// Hash da senha
const passwordHash = await hashPassword(password);
// Criar usuário
const result = await db.run(
`INSERT INTO users (username, email, password_hash, clinic_name, pc_name, is_admin, created_at)
VALUES (?, ?, ?, ?, ?, ?, NOW())`,
[username, email, passwordHash, clinic_name || null, pc_name || null, !!is_admin]
);
res.json({
success: true,
message: 'Usuário criado com sucesso',
userId: result.lastID
});
} catch (error) {
console.error('Erro ao criar usuário:', error);
res.status(500).json({ error: 'Erro interno do servidor' });
}
});
// ================================================================
// GERENCIAMENTO DE USUÁRIOS/CLÍNICAS (ADMIN)
// ================================================================
router.get('/users', authenticateToken, async (req, res) => {
try {
if (!req.user.is_admin) {
return res.status(403).json({ error: 'Acesso negado' });
}
const users = await db.all('SELECT id, username, email, clinic_name, pc_name, is_admin, created_at FROM users ORDER BY created_at DESC');
res.json({ success: true, users });
} catch (error) {
res.status(500).json({ error: 'Erro ao buscar usuários' });
}
});
router.delete('/users/:id', authenticateToken, async (req, res) => {
try {
if (!req.user.is_admin) {
return res.status(403).json({ error: 'Acesso negado' });
}
const userId = req.params.id;
if (parseInt(userId) === req.user.id) {
return res.status(400).json({ error: 'Não é possível excluir o próprio usuário admin' });
}
await db.run('DELETE FROM users WHERE id = ?', [userId]);
res.json({ success: true, message: 'Usuário excluído com sucesso' });
} catch (error) {
res.status(500).json({ error: 'Erro ao excluir usuário' });
}
});
router.post('/users/:id/token', authenticateToken, async (req, res) => {
try {
if (!req.user.is_admin) {
return res.status(403).json({ error: 'Acesso negado' });
}
const user = await db.get('SELECT * FROM users WHERE id = ?', [req.params.id]);
if (!user) return res.status(404).json({ error: 'Usuário não encontrado' });
// Gera um token com duração longa para uso no desktop (ex: 3650 dias = ~10 anos)
const token = generateToken(user, '3650d');
res.json({ success: true, token });
} catch (error) {
res.status(500).json({ error: 'Erro ao gerar token' });
}
});
// ================================================================
// PUT /api/auth/update-credentials - Alterar usuário/senha
// ================================================================
router.put('/update-credentials', authenticateToken, async (req, res) => {
try {
const { currentPassword, newUsername, newEmail, newPassword } = req.body;
const userId = req.user.id;
if (!currentPassword) {
return res.status(400).json({ error: 'A senha atual é obrigatória' });
}
if (!newUsername && !newEmail && !newPassword) {
return res.status(400).json({ error: 'Nenhum dado novo foi fornecido para atualização' });
}
// Buscar usuário atual para verificar a senha
const user = await db.get('SELECT * FROM users WHERE id = ?', [userId]);
if (!user) {
return res.status(404).json({ error: 'Usuário não encontrado' });
}
// Verificar senha atual
const validPassword = await verifyPassword(currentPassword, user.password_hash);
if (!validPassword) {
return res.status(401).json({ error: 'A senha atual está incorreta' });
}
let finalUsername = user.username;
let finalEmail = user.email;
let finalPasswordHash = user.password_hash;
// Verificar e aplicar novo username se fornecido
if (newUsername && newUsername !== user.username) {
const existing = await db.get('SELECT * FROM users WHERE username = ? AND id != ?', [newUsername, userId]);
if (existing) {
return res.status(409).json({ error: 'Este nome de usuário já está em uso' });
}
finalUsername = newUsername;
}
// Verificar e aplicar novo e-mail se fornecido
if (newEmail && newEmail !== user.email) {
const existing = await db.get('SELECT * FROM users WHERE email = ? AND id != ?', [newEmail, userId]);
if (existing) {
return res.status(409).json({ error: 'Este e-mail já está em uso' });
}
finalEmail = newEmail;
}
// Verificar e aplicar nova senha se fornecido
if (newPassword) {
if (newPassword.length < 4) {
return res.status(400).json({ error: 'A nova senha deve ter pelo menos 4 caracteres' });
}
finalPasswordHash = await hashPassword(newPassword);
}
// Atualizar no banco de dados
await db.run(
'UPDATE users SET username = ?, email = ?, password_hash = ? WHERE id = ?',
[finalUsername, finalEmail, finalPasswordHash, userId]
);
// Gerar novo token pois o username ou e-mail podem ter mudado
const updatedUser = { id: userId, username: finalUsername, email: finalEmail, is_admin: user.is_admin };
const newToken = generateToken(updatedUser);
res.json({
success: true,
message: 'Credenciais atualizadas com sucesso',
token: newToken,
user: updatedUser
});
} catch (error) {
console.error('Erro ao atualizar credenciais:', error);
res.status(500).json({ error: 'Erro interno do servidor' });
}
});
module.exports = router;