feat: adicionar gerenciamento de clínicas e tokens
This commit is contained in:
+24
-114
@@ -5,7 +5,6 @@ const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const cors = require('cors');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const sharp = require('sharp');
|
||||
require('dotenv').config();
|
||||
|
||||
// Importar módulos
|
||||
@@ -13,7 +12,6 @@ const imageProcessor = require('./image-processor');
|
||||
const { authenticateToken, hashPassword, JWT_SECRET } = require('./auth');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const db = require('./database');
|
||||
const storage = require('./storage');
|
||||
const authRoutes = require('./routes/auth');
|
||||
const imageRoutes = require('./routes/images');
|
||||
const patientsRouter = require('./routes/patients');
|
||||
@@ -328,9 +326,9 @@ app.post('/api/install', async (req, res) => {
|
||||
await db.run(
|
||||
`INSERT INTO users (username, email, password_hash, created_at)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
email = EXCLUDED.email,
|
||||
password_hash = EXCLUDED.password_hash`,
|
||||
ON DUPLICATE KEY UPDATE
|
||||
email = VALUES(email),
|
||||
password_hash = VALUES(password_hash)`,
|
||||
[admin.username, admin.email, passwordHash]
|
||||
);
|
||||
console.log('✅ Usuário admin criado:', admin.username);
|
||||
@@ -442,44 +440,8 @@ app.use('/login', express.static('auth'));
|
||||
|
||||
// Serve static files
|
||||
app.use(express.static('public'));
|
||||
|
||||
app.get('/uploads/:filename', async (req, res) => {
|
||||
const filename = req.params.filename;
|
||||
|
||||
// Enviar ETag e Cache-Control imutável
|
||||
res.setHeader('ETag', `"${filename}"`);
|
||||
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
||||
|
||||
const clientEtag = req.headers['if-none-match'];
|
||||
if (clientEtag === `"${filename}"` || clientEtag === filename || clientEtag === `W/"${filename}"`) {
|
||||
return res.status(304).end();
|
||||
}
|
||||
|
||||
// Tentar primeiro obter da pasta de uploads (imagens originais)
|
||||
let buffer = await storage.getImageBuffer(filename, false);
|
||||
|
||||
// Se não encontrar, tentar da pasta de processed (imagens rotacionadas/transformadas)
|
||||
if (!buffer) {
|
||||
buffer = await storage.getImageBuffer(filename, true);
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
return res.status(404).send('Imagem não encontrada');
|
||||
}
|
||||
|
||||
const ext = path.extname(filename).toLowerCase();
|
||||
let contentType = 'image/png';
|
||||
if (ext === '.jpg' || ext === '.jpeg') {
|
||||
contentType = 'image/jpeg';
|
||||
} else if (ext === '.webp') {
|
||||
contentType = 'image/webp';
|
||||
} else if (ext === '.svg') {
|
||||
contentType = 'image/svg+xml';
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', contentType);
|
||||
res.send(buffer);
|
||||
});
|
||||
app.use('/uploads', express.static(UPLOAD_DIR));
|
||||
app.use('/uploads', express.static(PROCESSED_DIR));
|
||||
|
||||
// ================================================================
|
||||
// MIDDLEWARE DE VERIFICAÇÃO DE INSTALAÇÃO (APENAS PARA APIs)
|
||||
@@ -534,29 +496,10 @@ app.use('/api/images', imageRoutes);
|
||||
app.use('/api/gtos', authenticateToken);
|
||||
app.use('/api/gtos', gtosRouter);
|
||||
|
||||
// APIs do Sistema - requerem autenticação
|
||||
app.use('/api/system', authenticateToken);
|
||||
app.use('/api/system', systemRoutes);
|
||||
|
||||
// Endpoint para solicitar a sincronização aos dispositivos conectados
|
||||
app.post('/api/system/trigger-sync', authenticateToken, (req, res) => {
|
||||
try {
|
||||
const windowsClients = connectedClients.filter(c => c.type === 'windows');
|
||||
|
||||
// Envia o evento 'sync-request' com timestamp para todos os dispositivos clientes identificados
|
||||
io.emit('sync-request', { timestamp: Date.now() });
|
||||
console.log(`🔄 [Sync] Sinal de sincronização enviado para ${windowsClients.length} dispositivo(s) Windows.`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
devicesTriggered: windowsClients.length,
|
||||
message: `Sinal de sincronização enviado com sucesso para ${windowsClients.length} dispositivo(s) Windows.`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao solicitar sincronização:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ================================================================
|
||||
// SOCKET.IO - CONEXÕES EM TEMPO REAL
|
||||
// ================================================================
|
||||
@@ -746,37 +689,16 @@ io.on('connection', (socket) => {
|
||||
// Validar imagem
|
||||
await imageProcessor.validateImage(imageBuffer);
|
||||
|
||||
// Detectar formato da imagem e gerar extensão adequada
|
||||
const imgMetadata = await sharp(imageBuffer).metadata();
|
||||
const format = imgMetadata.format || 'png';
|
||||
const ext = format === 'jpeg' ? 'jpg' : format;
|
||||
|
||||
// Salvar imagem original
|
||||
const timestamp = Date.now();
|
||||
const filename = `${timestamp}_${data.metadata.guid}.${ext}`;
|
||||
const filename = `${timestamp}_${data.metadata.guid}.png`;
|
||||
const filePath = path.join(UPLOAD_DIR, filename);
|
||||
|
||||
await fs.writeFile(filePath, imageBuffer);
|
||||
|
||||
// Buscar nome do cliente se identificado
|
||||
const clientInfo = connectedClients.find(c => c.socketId === socket.id);
|
||||
const clientName = clientInfo ? clientInfo.name : 'Cliente não identificado';
|
||||
const patientName = data.patientData.name || 'Paciente não identificado';
|
||||
|
||||
await storage.saveImage(filename, imageBuffer, clientName, patientName, false);
|
||||
|
||||
// Gerar e salvar thumbnail WebP
|
||||
let thumbFilename = null;
|
||||
try {
|
||||
const thumbBuffer = await sharp(imageBuffer)
|
||||
.resize({ width: 400, height: 400, fit: 'inside', withoutEnlargement: true })
|
||||
.webp({ quality: 80, effort: 4 })
|
||||
.toBuffer();
|
||||
|
||||
thumbFilename = `thumb_${timestamp}_${data.metadata.guid}.webp`;
|
||||
await storage.saveImage(thumbFilename, thumbBuffer, clientName, patientName, false);
|
||||
console.log(`✅ Thumbnail gerado e salvo no Wasabi: ${thumbFilename}`);
|
||||
} catch (thumbErr) {
|
||||
console.error('⚠️ Falha ao gerar thumbnail, prosseguindo sem ele:', thumbErr.message);
|
||||
}
|
||||
|
||||
// Determinar data de criação
|
||||
const formatLocalDateTime = (date) => {
|
||||
@@ -797,9 +719,8 @@ io.on('connection', (socket) => {
|
||||
enabled,
|
||||
doctor,
|
||||
remark,
|
||||
thumb_filename,
|
||||
created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
data.metadata.guid,
|
||||
data.patientData.name || 'Paciente não identificado',
|
||||
@@ -809,7 +730,6 @@ io.on('connection', (socket) => {
|
||||
1,
|
||||
data.patientData.doctor || null,
|
||||
data.patientData.remark || null,
|
||||
thumbFilename,
|
||||
createdAt
|
||||
]
|
||||
);
|
||||
@@ -841,7 +761,6 @@ io.on('connection', (socket) => {
|
||||
doctor: data.patientData.doctor || null,
|
||||
remark: data.patientData.remark || null,
|
||||
filename: filename,
|
||||
thumb_filename: thumbFilename,
|
||||
created_at: createdAt
|
||||
});
|
||||
}
|
||||
@@ -969,35 +888,26 @@ async function initDirectories() {
|
||||
}
|
||||
|
||||
async function start() {
|
||||
const fsSync = require('fs');
|
||||
try {
|
||||
// Inicializar diretórios
|
||||
await initDirectories();
|
||||
|
||||
// Inicializar banco se as variáveis já existirem no ambiente
|
||||
if (process.env.DB_TYPE === 'sqlite' || (process.env.DB_HOST && process.env.DB_NAME)) {
|
||||
// Tentar inicializar banco apenas se .env já existe
|
||||
const envPath = path.join(__dirname, '.env');
|
||||
const fsSync = require('fs');
|
||||
|
||||
if (fsSync.existsSync(envPath)) {
|
||||
try {
|
||||
await db.initDatabase();
|
||||
console.log('✅ Banco de dados inicializado a partir do ambiente');
|
||||
await storage.loadConfigFromDb();
|
||||
} catch (error) {
|
||||
console.log('❌ Falha ao inicializar o banco de dados:', error.message);
|
||||
}
|
||||
} else {
|
||||
// Fallback para arquivo .env físico
|
||||
const envPath = path.join(__dirname, '.env');
|
||||
const fsSync = require('fs');
|
||||
if (fsSync.existsSync(envPath)) {
|
||||
try {
|
||||
require('dotenv').config();
|
||||
if (process.env.DB_TYPE === 'sqlite' || (process.env.DB_HOST && process.env.DB_NAME)) {
|
||||
await db.initDatabase();
|
||||
console.log('✅ Banco de dados inicializado a partir do .env');
|
||||
await storage.loadConfigFromDb();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('⚠️ Banco de dados não configurado. Execute a instalação em /install');
|
||||
// Recarregar .env se foi atualizado
|
||||
require('dotenv').config();
|
||||
|
||||
// Inicializar banco de dados apenas se configurações existem
|
||||
if (process.env.DB_TYPE === 'sqlite' || (process.env.DB_HOST && process.env.DB_NAME)) {
|
||||
await db.initDatabase();
|
||||
console.log('✅ Banco de dados inicializado');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('⚠️ Banco de dados não configurado. Execute a instalação em /install');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user