fix(sync): re-upload correto quando arquivo foi deletado do storage
continuous-integration/webhook Deploy concluído (VPS4)

- storage.js: adiciona fileExists() com HeadObject (sem baixar o arquivo)
- check-image-exists: verifica existência física no storage, não só no banco;
  se registro existe mas arquivo não, apaga o registro órfão e retorna exists=false
- send-image: mesma lógica — registro órfão é removido e re-upload prossegue
- Remove migrate-to-wasabi.js do repositório

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Deploy Agent
2026-05-31 02:33:33 +02:00
parent 91881ab7a3
commit 7cdd82eeb4
3 changed files with 78 additions and 119 deletions
+43 -1
View File
@@ -1,4 +1,4 @@
const { S3Client, GetObjectCommand, DeleteObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');
const { S3Client, GetObjectCommand, DeleteObjectCommand, PutObjectCommand, HeadObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const { Upload } = require('@aws-sdk/lib-storage');
const fs = require('fs').promises;
@@ -369,12 +369,54 @@ async function getUploadPresignedUrl(filename, clinicName, clientName, patientNa
}
}
/**
* Verifica se o arquivo existe no storage (local ou Wasabi) sem baixar o conteúdo.
* Retorna true se encontrado em qualquer um dos dois lugares.
*/
async function fileExists(filename, clinicName = null, clientName = null, patientName = null) {
// Verificar local primeiro
const localPaths = [
path.join(UPLOAD_DIR, filename),
path.join(PROCESSED_DIR, filename),
];
for (const p of localPaths) {
if (fsSync.existsSync(p)) return true;
}
if (!s3 || !currentBucket) return false;
try {
let clName = clinicName;
let cName = clientName;
let pName = patientName;
if (!cName || !pName) {
try {
const row = await db.get(
'SELECT clinic_name, client_name, patient_name FROM images WHERE filename = $1 OR thumb_filename = $2 LIMIT 1',
[filename, filename]
);
if (row) { clName = row.clinic_name; cName = row.client_name; pName = row.patient_name; }
} catch (_) {}
}
const key = `${sanitizePathSegment(clName)}/${sanitizePathSegment(cName)}/${sanitizePathSegment(pName)}/${filename}`;
await s3.send(new HeadObjectCommand({ Bucket: currentBucket, Key: key }));
return true;
} catch (e) {
if (e.name === 'NotFound' || e.$metadata?.httpStatusCode === 404) return false;
// Erro de rede/auth — assume não existe para forçar re-upload
return false;
}
}
module.exports = {
loadConfigFromDb,
saveImage,
getImageBuffer,
getImageUrl,
deleteImage,
fileExists,
getDownloadPresignedUrl,
getUploadPresignedUrl,
isWasabiEnabled: () => !!(s3 && currentBucket),