diff --git a/dental-server/routes/images.js b/dental-server/routes/images.js index 0ba068b..911bd44 100644 --- a/dental-server/routes/images.js +++ b/dental-server/routes/images.js @@ -540,23 +540,32 @@ router.post('/backfill-thumbs', async (req, res) => { }); async function runThumbBackfill(rows) { - const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '..', '..', 'uploads'); - const PROCESSED_DIR = process.env.PROCESSED_DIR || path.join(__dirname, '..', '..', 'processed'); let done = 0, failed = 0; for (const row of rows) { try { - // Tenta carregar a imagem do disco local + // Busca a original via storage (disco local → Wasabi com path correto → fallback 'Desconhecido') let imgBuffer = null; - for (const dir of [UPLOAD_DIR, PROCESSED_DIR]) { - try { imgBuffer = await fs.readFile(path.join(dir, row.filename)); break; } catch (_) {} + try { + imgBuffer = await storage.getImageBuffer(row.filename, row.clinic_name, row.client_name, row.patient_name); + } catch (_) {} + + // Fallback legado: uploads antigos foram gravados com patientName='Desconhecido' no Wasabi + if (!imgBuffer && row.patient_name !== 'Desconhecido') { + try { + imgBuffer = await storage.getImageBuffer(row.filename, row.clinic_name, row.client_name, 'Desconhecido'); + } catch (_) {} } + if (!imgBuffer) { failed++; continue; } + const thumbBuffer = await imageProcessor.getThumbnail(imgBuffer); const thumbFilename = `thumb_${row.filename.replace(/\.[^.]+$/, '')}.jpg`; await storage.saveImage(thumbFilename, thumbBuffer, row.clinic_name, row.client_name, row.patient_name, false); await db.run('UPDATE images SET thumb_filename = $1 WHERE id = $2', [thumbFilename, row.id]); done++; + console.log(`[backfill-thumbs] ✅ id ${row.id}: ${thumbFilename}`); } catch (e) { + console.error(`[backfill-thumbs] ❌ id ${row.id}:`, e.message); failed++; } } diff --git a/dental-server/server.js b/dental-server/server.js index 5523d8c..dc1ebdd 100644 --- a/dental-server/server.js +++ b/dental-server/server.js @@ -525,6 +525,24 @@ app.use(checkInstallationMiddleware); // Middleware que pula autenticação para rotas públicas de imagens const publicImageRoutes = express.Router(); +// Tenta ler um arquivo do storage com fallback para o path legado ('Desconhecido'). +// Uploads antigos foram salvos no Wasabi com patientName='Desconhecido' porque o handler +// de presigned-urls usava firstName/lastName (que o client nunca envia) em vez de name. +async function getImageBufferWithFallback(filename, clinicName, clientName, patientName) { + try { + const buf = await storage.getImageBuffer(filename, clinicName, clientName, patientName); + if (buf) return buf; + } catch (_) {} + // Fallback: path legado onde patientName era sempre 'Desconhecido' + if (patientName !== 'Desconhecido') { + try { + const buf = await storage.getImageBuffer(filename, clinicName, clientName, 'Desconhecido'); + if (buf) return buf; + } catch (_) {} + } + return null; +} + // Serve os BYTES da thumbnail diretamente (image/jpeg), para uso direto em . // Se a imagem ainda não tem thumbnail, gera na hora a partir da original, // serve imediatamente e faz backfill (upload pro Wasabi + atualiza banco) em background. @@ -537,13 +555,12 @@ publicImageRoutes.get('/:id/thumbnail', async (req, res) => { if (!image) return res.status(404).json({ error: 'Imagem não encontrada' }); // Thumb de um id nunca muda → cache agressivo no navegador (1 ano, imutável). - // Repetir visita = 0 requisição ao servidor (served from disk cache). res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); - // 1. Já existe thumbnail — serve direto (local ou Wasabi) + // 1. Já existe thumbnail — serve direto (local ou Wasabi, com fallback legado) if (image.thumb_filename) { try { - const buf = await storage.getImageBuffer(image.thumb_filename, image.clinic_name, image.client_name, image.patient_name); + const buf = await getImageBufferWithFallback(image.thumb_filename, image.clinic_name, image.client_name, image.patient_name); if (buf) { res.setHeader('Content-Type', 'image/jpeg'); return res.end(buf); @@ -554,10 +571,10 @@ publicImageRoutes.get('/:id/thumbnail', async (req, res) => { } } - // 2. Sem thumbnail — puxa a original, gera a thumb na hora - const originalBuf = await storage.getImageBuffer(image.filename, image.clinic_name, image.client_name, image.patient_name); + // 2. Sem thumbnail — puxa a original (com fallback legado), gera a thumb na hora + const originalBuf = await getImageBufferWithFallback(image.filename, image.clinic_name, image.client_name, image.patient_name); if (!originalBuf) { - return res.status(404).json({ error: 'Imagem original não encontrada' }); + return res.status(404).json({ error: 'Imagem original não encontrada no storage' }); } const thumbBuf = await imageProcessor.getThumbnail(originalBuf, 500, 500); @@ -566,7 +583,7 @@ publicImageRoutes.get('/:id/thumbnail', async (req, res) => { res.setHeader('Content-Type', 'image/jpeg'); res.end(thumbBuf); - // Backfill em background: salva a thumb no Wasabi e atualiza o banco + // Backfill em background: salva a thumb no Wasabi (path correto) e atualiza o banco const thumbFilename = `thumb_${image.filename.replace(/\.[^.]+$/, '')}.jpg`; storage.saveImage(thumbFilename, thumbBuf, image.clinic_name, image.client_name, image.patient_name, false) .then(() => db.run('UPDATE images SET thumb_filename = $1 WHERE id = $2', [thumbFilename, image.id])) @@ -913,7 +930,7 @@ io.on('connection', (socket) => { const clientInfo = connectedClients.find(c => c.socketId === socket.id); const clientName = clientInfo ? clientInfo.name : 'PC não identificado'; const clinicName = socket.user?.clinicName || (clientInfo ? clientInfo.clinicName : 'Clínica não identificada'); - const patientName = data.patientData ? `${data.patientData.firstName || ''} ${data.patientData.lastName || ''}`.trim() || 'Desconhecido' : 'Desconhecido'; + const patientName = (data.patientData && data.patientData.name && data.patientData.name.trim()) ? data.patientData.name.trim() : 'Desconhecido'; // Salvar imagem original local e na nuvem const timestamp = Date.now(); @@ -1025,7 +1042,7 @@ io.on('connection', (socket) => { const clientInfo = connectedClients.find(c => c.socketId === socket.id); const clientName = clientInfo ? clientInfo.name : 'PC não identificado'; const clinicName = socket.user?.clinicName || (clientInfo ? clientInfo.clinicName : 'Clínica não identificada'); - const patientName = data.patientData ? `${data.patientData.firstName || ''} ${data.patientData.lastName || ''}`.trim() || 'Desconhecido' : 'Desconhecido'; + const patientName = (data.patientData && data.patientData.name && data.patientData.name.trim()) ? data.patientData.name.trim() : 'Desconhecido'; const timestamp = Date.now(); const filename = `${timestamp}_${data.metadata.guid}.png`;