diff --git a/backend/src/utils/tenantFullExport.ts b/backend/src/utils/tenantFullExport.ts index 3bba240..68d9f52 100644 --- a/backend/src/utils/tenantFullExport.ts +++ b/backend/src/utils/tenantFullExport.ts @@ -25,7 +25,9 @@ export type TenantFullExport = { name: string | null mimeType: string | null size: number | null - contentBase64: string + contentBase64: string | null + missing?: boolean + error?: string }[] } @@ -121,18 +123,37 @@ const decryptEntityBankAccountsForExport = (rows: Record[] = []) => }) } +const isMissingObjectError = (err: any) => + err?.Code === "NoSuchKey" || + err?.name === "NoSuchKey" || + err?.$metadata?.httpStatusCode === 404 + const loadObjectAsBase64 = async (path: string) => { - const { Body } = await s3.send(new GetObjectCommand({ - Bucket: secrets.S3_BUCKET, - Key: path, - })) + try { + const { Body } = await s3.send(new GetObjectCommand({ + Bucket: secrets.S3_BUCKET, + Key: path, + })) - const chunks: Buffer[] = [] - for await (const chunk of Body as any) { - chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) + const chunks: Buffer[] = [] + for await (const chunk of Body as any) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) + } + + return { + contentBase64: Buffer.concat(chunks).toString("base64"), + missing: false, + error: null, + } + } catch (err: any) { + if (!isMissingObjectError(err)) throw err + + return { + contentBase64: null, + missing: true, + error: err?.Code || err?.name || "NoSuchKey", + } } - - return Buffer.concat(chunks).toString("base64") } export const buildTenantFullExport = async (server: FastifyInstance, tenantId: number): Promise => { @@ -198,13 +219,25 @@ export const buildTenantFullExport = async (server: FastifyInstance, tenantId: n for (const file of fileRows) { if (!file.path) continue + const object = await loadObjectAsBase64(file.path) + if (object.missing) { + server.log.warn({ + fileId: file.id, + path: file.path, + bucket: secrets.S3_BUCKET, + error: object.error, + }, "Tenant full export skipped missing S3 object") + } + files.push({ id: file.id, path: file.path, name: file.name || null, mimeType: file.mimeType || null, size: file.size || null, - contentBase64: await loadObjectAsBase64(file.path), + contentBase64: object.contentBase64, + missing: object.missing || undefined, + error: object.error || undefined, }) }