feat(auth): token-first — device-info endpoint + pc_name autoritativo
continuous-integration/webhook Deploy concluído (VPS4)

- GET /api/auth/device-info?token=xxx: valida machine_token e retorna
  clinic_name e pc_name sem exigir email/senha. Usado pelo client para
  preview antes de salvar e para validação no save.

- client-identify: quando autenticado via machine_token, usa
  socket.user.pcName como nome autoritativo (ignora o que o client enviou).

- server-identified: clientInfo agora inclui 'pcName' para o client
  manter o nome em sincronia com o cadastro do servidor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Deploy Agent
2026-05-31 15:36:56 +02:00
parent c43e752694
commit 62e3ded5a7
+23 -3
View File
@@ -458,6 +458,24 @@ TRUST_PROXY=true
app.use('/api/auth', authRoutes); app.use('/api/auth', authRoutes);
app.use('/api/client', clientAuthRoutes); app.use('/api/client', clientAuthRoutes);
// GET /api/auth/device-info?token=xxx — valida machine_token e retorna info do dispositivo.
// Usado pelo client para verificar o token antes de salvar (sem precisar de email/senha).
app.get('/api/auth/device-info', async (req, res) => {
try {
const token = (req.query.token || '').trim();
if (!token) return res.status(400).json({ success: false, error: 'Token obrigatório.' });
const device = await db.get(
'SELECT clinic_name, pc_name, email, is_active FROM clinics_devices WHERE machine_token = $1',
[token]
);
if (!device) return res.status(404).json({ success: false, error: 'Token não encontrado.' });
if (!device.is_active) return res.status(403).json({ success: false, error: 'Dispositivo desativado.' });
res.json({ success: true, clinicName: device.clinic_name, pcName: device.pc_name });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
}
});
// ================================================================ // ================================================================
// MIDDLEWARE DE VERIFICAÇÃO DE INSTALAÇÃO (APENAS PARA APIs) // MIDDLEWARE DE VERIFICAÇÃO DE INSTALAÇÃO (APENAS PARA APIs)
@@ -839,13 +857,14 @@ io.on('connection', (socket) => {
console.log('👤 Cliente identificado:', data?.type || 'SEM TYPE', data?.name || 'SEM NAME'); console.log('👤 Cliente identificado:', data?.type || 'SEM TYPE', data?.name || 'SEM NAME');
console.log('👤 Dados completos:', JSON.stringify(data, null, 2)); console.log('👤 Dados completos:', JSON.stringify(data, null, 2));
if (!data || !data.type || !data.name) { if (!data || !data.type) {
console.error('❌ [client-identify] Dados inválidos:', data); console.error('❌ [client-identify] Dados inválidos:', data);
socket.emit('error', { message: 'Dados de identificação inválidos' }); socket.emit('error', { message: 'Dados de identificação inválidos' });
return; return;
} }
const clientName = data.name.trim(); // Se autenticado via machine_token, o pc_name do cadastro é autoritativo
const clientName = (socket.user?.pcName || data.name || 'PC não identificado').trim();
const clientId = data.clientId || ''; const clientId = data.clientId || '';
// 1. Validar unicidade em relação aos clientes já conectados // 1. Validar unicidade em relação aos clientes já conectados
@@ -887,6 +906,7 @@ io.on('connection', (socket) => {
type: data.type || 'unknown', type: data.type || 'unknown',
name: clientName, name: clientName,
clinicName: socket.user?.clinicName || (data.clinicName || '').trim() || 'Clínica não identificada', clinicName: socket.user?.clinicName || (data.clinicName || '').trim() || 'Clínica não identificada',
pcName: socket.user?.pcName || clientName,
version: data.version || '1.0.0', version: data.version || '1.0.0',
system: data.system || 'unknown', system: data.system || 'unknown',
clientId: clientId, clientId: clientId,