Implement client authentication and admin clinics panel

This commit is contained in:
VPS 4 Builder
2026-05-26 04:17:12 +02:00
parent 13175f93aa
commit ec3f23c9ea
11 changed files with 2642 additions and 18 deletions
+113
View File
@@ -0,0 +1,113 @@
const express = require('express');
const router = express.Router();
const db = require('../database');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
// Usa a chave JWT existente, ou um valor de fallback seguro se não configurado
const JWT_SECRET = process.env.JWT_SECRET || 'fallback_secret_dental_2026_super_secure';
// ==============================================================
// 1. LOGIN DO CLIENTE (APP DESKTOP)
// ==============================================================
router.post('/login', async (req, res) => {
const { email, password, machine_token } = req.body;
if (!email || !password || !machine_token) {
return res.status(400).json({
success: false,
message: 'Email, Senha e Token da Máquina são obrigatórios'
});
}
try {
// 1. Buscar o dispositivo pelo Token
const device = await db.get(`SELECT * FROM clinics_devices WHERE machine_token = ?`, [machine_token]);
if (!device) {
return res.status(401).json({ success: false, message: 'Token da máquina inválido ou inexistente' });
}
// 2. Verificar se o acesso está ativo
if (!device.is_active) {
return res.status(403).json({ success: false, message: 'O acesso desta máquina foi desativado pelo administrador' });
}
// 3. Verificar o Email
if (device.email !== email) {
return res.status(401).json({ success: false, message: 'Email incorreto para esta máquina' });
}
// 4. Verificar a Senha
const validPassword = await bcrypt.compare(password, device.password_hash);
if (!validPassword) {
return res.status(401).json({ success: false, message: 'Senha incorreta' });
}
// Atualizar último IP conectado
const clientIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
await db.run(`UPDATE clinics_devices SET last_ip = ? WHERE id = ?`, [clientIp, device.id]);
// 5. Gerar Token JWT de Sessão
// O Token vale por 2 horas, obrigando o cliente a re-logar ou pedir refresh
const sessionToken = jwt.sign(
{
clinicId: device.id,
clinicName: device.clinic_name,
pcName: device.pc_name,
role: 'client_device'
},
JWT_SECRET,
{ expiresIn: '2h' }
);
res.json({
success: true,
message: 'Login aprovado',
token: sessionToken,
clinic: {
name: device.clinic_name,
pc: device.pc_name
}
});
} catch (error) {
console.error('Erro no login do cliente:', error);
res.status(500).json({ success: false, message: 'Erro interno de servidor' });
}
});
// ==============================================================
// 2. MIDDLEWARE DE VALIDAÇÃO (Para usar nas rotas de Upload)
// ==============================================================
const requireClientAuth = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ success: false, message: 'Token JWT não fornecido' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, JWT_SECRET);
if (decoded.role !== 'client_device') {
return res.status(403).json({ success: false, message: 'Acesso negado: Perfil inválido' });
}
// Anexar info ao request para uso posterior
req.clientDevice = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ success: false, message: 'Sessão expirada. Faça login novamente.', expired: true });
}
return res.status(401).json({ success: false, message: 'Token JWT inválido' });
}
};
module.exports = {
router,
requireClientAuth,
JWT_SECRET
};