From 02dcbc3faee864cfbeff74a5f3a41890e1c76b1b Mon Sep 17 00:00:00 2001 From: VPS 4 Deploy Agent Date: Sat, 30 May 2026 17:27:44 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20lazy-load=20Wasabi=20URLs=20to=20prevent?= =?UTF-8?q?=20socket.io=20timeout=20=E2=80=94=20check=20local=20files=20fi?= =?UTF-8?q?rst,=20generate=20signed=20URLs=20on-demand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dental-server/routes/images.js | 67 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/dental-server/routes/images.js b/dental-server/routes/images.js index ec426b0..e26d57e 100644 --- a/dental-server/routes/images.js +++ b/dental-server/routes/images.js @@ -159,19 +159,9 @@ router.get('/patients', async (req, res) => { params ); - // Converter thumb_filename para URL completa (Wasabi ou local) + // Lazy-load URLs para thumbnails (carrega em paralelo quando o frontend chamar) for (const row of rows) { - if (row.thumb_filename) { - try { - const thumbUrl = await storage.getImageUrl(row.thumb_filename, row.client_name, row.patient_name); - row.thumb_url = thumbUrl || `/uploads/${row.thumb_filename}`; - } catch (e) { - console.error('Erro ao gerar URL de thumbnail:', e); - row.thumb_url = `/uploads/${row.thumb_filename}`; - } - } else { - row.thumb_url = ''; - } + row.thumb_url = row.thumb_filename ? `/api/images/${row.id}/thumbnail` : ''; } res.json(rows); @@ -201,22 +191,10 @@ router.get('/by-patient', async (req, res) => { [patientName, showDisabled ? 0 : 1] ); - // Converter filenames para URLs completas (Wasabi ou local) + // Adicionar lazy-load URLs (não fazer requisições async aqui) for (const image of images) { - try { - if (image.filename) { - const fileUrl = await storage.getImageUrl(image.filename, image.client_name, image.patient_name); - image.file_url = fileUrl || `/uploads/${image.filename}`; - } - if (image.thumb_filename) { - const thumbUrl = await storage.getImageUrl(image.thumb_filename, image.client_name, image.patient_name); - image.thumb_url = thumbUrl || `/uploads/${image.thumb_filename}`; - } - } catch (e) { - console.error('Erro ao gerar URL para imagem:', e); - image.file_url = image.file_url || `/uploads/${image.filename}`; - image.thumb_url = image.thumb_url || `/uploads/${image.thumb_filename}`; - } + image.file_url = `/api/images/${image.id}/file-url`; + image.thumb_url = `/api/images/${image.id}/thumbnail`; } res.json(images); @@ -489,6 +467,41 @@ router.delete('/:id', async (req, res) => { } }); +// ================================================================ +// GET /api/images/:id/thumbnail — Retorna URL da thumbnail +// (lazy-loaded para não bloquear listagens) +// ================================================================ +router.get('/:id/thumbnail', async (req, res) => { + try { + const image = await db.get('SELECT thumb_filename, filename, client_name, patient_name FROM images WHERE id = $1', [req.params.id]); + if (!image) return res.status(404).json({ error: 'Imagem não encontrada' }); + + const fileToCheck = image.thumb_filename || image.filename; + let url; + + // Tenta URL local primeiro (mais rápido) + const fsSync = require('fs'); + const path = require('path'); + const localPath = path.join(process.env.UPLOAD_DIR || '/app/uploads', fileToCheck); + + if (fsSync.existsSync(localPath)) { + url = `/uploads/${fileToCheck}`; + } else { + // Se não existe localmente, tenta Wasabi + try { + const wasabiUrl = await storage.getImageUrl(fileToCheck, image.client_name, image.patient_name, false); + url = wasabiUrl || `/uploads/${fileToCheck}`; + } catch (e) { + console.error('Erro ao gerar URL Wasabi para thumbnail:', e); + url = `/uploads/${fileToCheck}`; + } + } + res.json({ url }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}); + // ================================================================ // GET /api/images/:id/file-url — Retorna a URL de visualização // full-res de uma imagem (para o painel Original do modal)