@strapi/data-transfer
Version:
Data transfer capabilities for Strapi
1 lines • 9.51 kB
Source Map (JSON)
{"version":3,"file":"assets-destination-writable.mjs","sources":["../../../../src/strapi/providers/local-destination/assets-destination-writable.ts"],"sourcesContent":["import { Writable, Readable } from 'stream';\nimport type { Core } from '@strapi/types';\n\nimport type { IAsset, IFile } from '../../../types';\nimport type { Transaction } from '../../../types/utils';\n\nexport interface CreateAssetsDestinationWritableOptions {\n strapi: Core.Strapi;\n transaction: Transaction;\n resolveUploadFileId: (metadata: { id: number }) => number | undefined;\n restoreMediaEntitiesContent: boolean;\n removeAssetsBackup: () => Promise<void>;\n}\n\n/**\n * Writable for restoring upload assets during a local push destination transfer.\n *\n * Design constraints:\n * 1. The `write()` callback must return **before** `uploadStream` finishes so the remote push\n * handler can continue feeding chunks to the PassThrough stream in the same WebSocket batch\n * (avoids deadlock — see `streamAsset` in the remote push handler).\n * 2. `uploadStream` is only called **after** the PassThrough has been fully drained (all chunks\n * received and the stream ended). This gives the upload provider a pre-filled synchronous\n * Readable rather than a lazy async wrapper, which avoids `Buffer.from(undefined)` crashes\n * in upload providers that call `stream.read()` before any data has been buffered.\n */\nexport function createAssetsDestinationWritable(\n options: CreateAssetsDestinationWritableOptions\n): Writable {\n const {\n strapi,\n transaction,\n resolveUploadFileId,\n restoreMediaEntitiesContent,\n removeAssetsBackup,\n } = options;\n\n let pendingUploads = 0;\n\n return new Writable({\n objectMode: true,\n async final(next) {\n while (pendingUploads > 0) {\n await new Promise<void>((resolve) => {\n setImmediate(resolve);\n });\n }\n await removeAssetsBackup();\n next();\n },\n write(chunk: IAsset, _encoding, callback) {\n const provider = strapi.config.get<{ provider: string }>('plugin::upload').provider;\n\n const fileId = resolveUploadFileId(chunk.metadata);\n if (!fileId) {\n callback(new Error(`File ID not found for ID: ${chunk.metadata.id}`));\n return;\n }\n\n if (!transaction) {\n callback(new Error('Transaction not available for asset upload'));\n return;\n }\n\n // Accumulate all binary chunks from the PassThrough as they arrive (flowing mode).\n // uploadStream is only started once the stream ends — see constraint (2) above.\n const bufferedChunks: Buffer[] = [];\n chunk.stream.on('data', (c: Buffer) => bufferedChunks.push(c));\n\n pendingUploads += 1;\n\n chunk.stream.once('end', () => {\n // Build uploadData here so the stream is fully populated before the provider reads it.\n const uploadData = {\n ...chunk.metadata,\n stream: Readable.from(bufferedChunks),\n ...(chunk.buffer != null ? { buffer: chunk.buffer } : {}),\n };\n\n transaction\n .attach(async () => {\n try {\n await strapi.plugin('upload').provider.uploadStream(uploadData);\n\n if (!restoreMediaEntitiesContent) {\n return;\n }\n\n if (uploadData?.type) {\n const entry: IFile = await strapi.db.query('plugin::upload.file').findOne({\n where: { id: fileId },\n });\n if (!entry) {\n throw new Error('file not found');\n }\n const specificFormat = entry?.formats?.[uploadData.type];\n if (specificFormat) {\n specificFormat.url = uploadData.url;\n }\n await strapi.db.query('plugin::upload.file').update({\n where: { id: entry.id },\n data: {\n formats: entry.formats,\n provider,\n },\n });\n return;\n }\n\n const entry: IFile = await strapi.db.query('plugin::upload.file').findOne({\n where: { id: fileId },\n });\n if (!entry) {\n throw new Error('file not found');\n }\n entry.url = uploadData.url;\n await strapi.db.query('plugin::upload.file').update({\n where: { id: entry.id },\n data: {\n url: entry.url,\n provider,\n },\n });\n } catch (error) {\n throw new Error(`Error while uploading asset ${chunk.filename} ${error}`);\n }\n })\n .finally(() => {\n pendingUploads -= 1;\n })\n .catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n process.nextTick(() => {\n this.destroy(err);\n });\n });\n });\n\n chunk.stream.once('error', (err) => {\n pendingUploads -= 1;\n process.nextTick(() => this.destroy(err));\n });\n\n callback();\n },\n });\n}\n"],"names":["createAssetsDestinationWritable","options","strapi","transaction","resolveUploadFileId","restoreMediaEntitiesContent","removeAssetsBackup","pendingUploads","Writable","objectMode","final","next","Promise","resolve","setImmediate","write","chunk","_encoding","callback","provider","config","get","fileId","metadata","Error","id","bufferedChunks","stream","on","c","push","once","uploadData","Readable","from","buffer","attach","plugin","uploadStream","type","entry","db","query","findOne","where","specificFormat","formats","url","update","data","error","filename","finally","catch","err","String","process","nextTick","destroy"],"mappings":"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"}