Files
VPS 4 Deploy Agent 31022c6a6f feat(rx): multi-tenant por clinica_id + página /clients de clientes ativos
- clinica_id (tenant do scoreodonto, clinicas.id) como eixo de isolamento em
  clinics_devices e images (+índices); clinic_name vira rótulo legível.
- Cadastro de device, socket (machine_token) e uploads (direct + legado) gravam clinica_id.
- Listagens (/images, /patients, /by-patient, /unique-clinics) filtram por clinica_id.
- /clients: lista clientes Windows ativos (inclui autenticados sem client-identify),
  tempo real + polling, exibe a clínica.
- send-image marcado como legado (client real usa Direct Upload).

Inclui trabalho de dev acumulado no working tree (não havia commits desde origin/main).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:17:09 +02:00

146 lines
4.5 KiB
JavaScript

const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const db = require('./database');
const JWT_SECRET = process.env.JWT_SECRET || 'sua-chave-secreta-alterar-em-producao';
const JWT_EXPIRES_IN = '7d';
// ================================================================
// MIDDLEWARE DE AUTENTICAÇÃO
// ================================================================
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
// Identifica se a rota é uma API (via URL ou cabeçalho Accept)
const isApiRoute = req.originalUrl.includes('/api') ||
(req.headers.accept && req.headers.accept.includes('application/json'));
// Se não tem token, verifica se está tentando acessar uma página
if (!token) {
const isLoginRoute = req.originalUrl.includes('/login') || req.originalUrl === '/auth/login.html';
if (isLoginRoute || req.originalUrl === '/status') {
return next();
}
if (isApiRoute) {
return res.status(401).json({ error: 'Token não fornecido' });
}
return res.redirect('/login');
}
// Verificar token via JWT (Fluxo padrão)
jwt.verify(token, JWT_SECRET, async (err, user) => {
if (err) {
// Se não for um JWT válido, vamos verificar se é um Token Estático de API
try {
const apiToken = await db.get('SELECT * FROM api_tokens WHERE token = $1', [token]);
if (apiToken) {
// Token estático encontrado! Atualizar last_used_at em background
db.run('UPDATE api_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE id = $1', [apiToken.id]).catch(() => {});
// Injetar usuário "mock" na requisição com permissão de admin
req.user = {
id: -1,
username: 'api_token',
is_admin: true,
email: 'api@system.local'
};
return next();
}
} catch (dbErr) {
console.error('Erro ao verificar API Token estático:', dbErr);
}
if (isApiRoute) {
return res.status(403).json({ error: 'Token inválido' });
}
return res.redirect('/login');
}
req.user = user;
next();
});
}
// ================================================================
// HASH DE SENHA
// ================================================================
async function hashPassword(password) {
const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
}
async function verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
// ================================================================
// GERAR TOKEN JWT
// ================================================================
function generateToken(user, expiresIn = JWT_EXPIRES_IN) {
return jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
is_admin: user.is_admin
},
JWT_SECRET,
{ expiresIn: expiresIn }
);
}
// ================================================================
// CRIAR USUÁRIO INICIAL
// ================================================================
async function createInitialUser() {
try {
// Verificar se usuário já existe
const existingUser = await db.get('SELECT * FROM users WHERE username = $1', ['rcesar']);
if (existingUser) {
console.log('👤 Usuário admin já existe:', existingUser.username);
return;
}
const initialPassword = process.env.INITIAL_ADMIN_PASSWORD;
if (!initialPassword) {
console.log('⚠️ INITIAL_ADMIN_PASSWORD não definido — usuário admin inicial não será criado automaticamente');
return;
}
const passwordHash = await hashPassword(initialPassword);
await db.run(
`INSERT INTO users (username, email, password_hash, is_admin, created_at)
VALUES ($1, $2, $3, true, CURRENT_TIMESTAMP) RETURNING id`,
['rcesar', 'rcesar@rcesar.com', passwordHash]
);
console.log('✅ Usuário admin criado: rcesar');
} catch (error) {
// Ignorar se tabela não existe ainda
if (error.message.includes('no such table')) {
console.log('⚠️ Tabela users não existe. Será criada pelo database.js');
} else {
console.error('❌ Erro ao criar usuário inicial:', error);
}
}
}
module.exports = {
authenticateToken,
hashPassword,
verifyPassword,
generateToken,
createInitialUser,
JWT_SECRET
};