// Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import lodash from 'lodash'; import type { AttachmentType } from '../../types/Attachment.std.ts'; import type { ContextType } from '../../types/Message2.preload.ts'; import type { MessageAttributesType } from '../../model-types.d.ts'; const { isFunction, isTypedArray, isUndefined, omit } = lodash; export async function migrateDataToFileSystem( attachment: AttachmentType, { writeNewAttachmentData, getExistingAttachmentDataForReuse, getPlaintextHashForInMemoryAttachment, logger, }: Pick< ContextType, | 'writeNewAttachmentData' | 'getExistingAttachmentDataForReuse' | 'getPlaintextHashForInMemoryAttachment' | 'logger' >, message: Pick ): Promise { if (!isFunction(writeNewAttachmentData)) { throw new TypeError("'writeNewAttachmentData' must be a function"); } const { data } = attachment; const attachmentHasData = !isUndefined(data); const shouldSkipSchemaUpgrade = !attachmentHasData; if (shouldSkipSchemaUpgrade) { return attachment; } // This attachment was already broken by a roundtrip to the database - repair it now if (!isTypedArray(data)) { logger.warn( 'migrateDataToFileSystem: Attachment had non-array `data` field; deleting.' ); return omit({ ...attachment }, ['data']); } const plaintextHash = getPlaintextHashForInMemoryAttachment(data); const existingData = await getExistingAttachmentDataForReuse({ plaintextHash, messageId: message.id, contentType: attachment.contentType, logId: 'migrateDataToFileSystem', }); const attachmentWithoutData = omit(attachment, ['data']); if (existingData) { return { ...attachmentWithoutData, plaintextHash, ...existingData, }; } const newAttachmentData = await writeNewAttachmentData(data); return { ...attachmentWithoutData, ...newAttachmentData }; }