Fix tenant archive S3 upload
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 26s
Build and Push Docker Images / build-frontend (push) Successful in 58s
Build and Push Docker Images / build-website (push) Successful in 13s
Build and Push Docker Images / build-docs (push) Successful in 12s

This commit is contained in:
2026-07-05 12:41:23 +02:00
parent 3c4a75a858
commit c54e9a51a5

View File

@@ -1,7 +1,10 @@
import { FastifyInstance } from "fastify" import { FastifyInstance } from "fastify"
import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3" import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"
import archiver from "archiver" import archiver from "archiver"
import { PassThrough } from "stream" import { createReadStream, createWriteStream } from "fs"
import { stat, unlink } from "fs/promises"
import os from "os"
import path from "path"
import { BlobReader, TextWriter, Uint8ArrayWriter, ZipReader } from "@zip.js/zip.js" import { BlobReader, TextWriter, Uint8ArrayWriter, ZipReader } from "@zip.js/zip.js"
import { pool } from "../../db" import { pool } from "../../db"
@@ -382,23 +385,21 @@ export const createTenantFullExportArchive = async (
const storagePath = options.storagePath || `tenant-exports/${tenantId}/${Date.now()}-${filename}` const storagePath = options.storagePath || `tenant-exports/${tenantId}/${Date.now()}-${filename}`
const fileRows = exportData.tables.files || [] const fileRows = exportData.tables.files || []
const archive = archiver("zip", { zlib: { level: 6 } }) const archive = archiver("zip", { zlib: { level: 6 } })
const output = new PassThrough() const tempPath = path.join(os.tmpdir(), `fedeo-tenant-export-${tenantId}-${Date.now()}-${filename}`)
let size = 0 const output = createWriteStream(tempPath)
const archiveComplete = new Promise<void>((resolve, reject) => {
output.on("data", (chunk: Buffer) => { output.on("close", resolve)
size += chunk.length output.on("error", reject)
archive.on("error", reject)
archive.on("warning", (err) => {
if ((err as NodeJS.ErrnoException).code === "ENOENT") return
reject(err)
})
}) })
const uploadPromise = s3.send(new PutObjectCommand({
Bucket: secrets.S3_BUCKET,
Key: storagePath,
Body: output,
ContentType: "application/zip",
ContentDisposition: `attachment; filename="${filename}"`,
}))
archive.pipe(output) archive.pipe(output)
try {
const tables = Object.entries(exportData.tables) const tables = Object.entries(exportData.tables)
.sort(([a], [b]) => a.localeCompare(b)) .sort(([a], [b]) => a.localeCompare(b))
.map(([table, rows]) => { .map(([table, rows]) => {
@@ -456,7 +457,17 @@ export const createTenantFullExportArchive = async (
archive.append(jsonBuffer(manifest), { name: "manifest.json" }) archive.append(jsonBuffer(manifest), { name: "manifest.json" })
await archive.finalize() await archive.finalize()
await uploadPromise await archiveComplete
const { size } = await stat(tempPath)
await s3.send(new PutObjectCommand({
Bucket: secrets.S3_BUCKET,
Key: storagePath,
Body: createReadStream(tempPath),
ContentLength: size,
ContentType: "application/zip",
ContentDisposition: `attachment; filename="${filename}"`,
}))
return { return {
filename, filename,
@@ -466,6 +477,9 @@ export const createTenantFullExportArchive = async (
filesTotal, filesTotal,
filesDone, filesDone,
} }
} finally {
await unlink(tempPath).catch(() => undefined)
}
} }
const restoreFiles = async (exportData: TenantFullExport) => { const restoreFiles = async (exportData: TenantFullExport) => {