diff --git a/app/workers/reeeper/job/archive-user-local.js b/app/workers/reeeper/job/archive-user-local.js index 6f92f66..e75b636 100644 --- a/app/workers/reeeper/job/archive-user-local.js +++ b/app/workers/reeeper/job/archive-user-local.js @@ -96,6 +96,7 @@ class ArchiveUserLocalJob extends SiteWorkerProcess { await this.archiveUserComments(job); await this.archiveUserStickers(job); await this.archiveUserImages(job); + await this.archiveUserAttachments(job); /* * Archive DTP Sites specific content @@ -124,7 +125,7 @@ class ArchiveUserLocalJob extends SiteWorkerProcess { }); // await User.deleteOne({ _id: job.data.userId }); } catch (error) { - this.log.error('failed to delete attachment', { attachmentId: job.data.attachmentId, error }); + this.log.error('failed to archive user', { userId: job.data.userId, error }); throw error; } finally { if (job.data.workPath) { @@ -335,12 +336,77 @@ class ArchiveUserLocalJob extends SiteWorkerProcess { try { await fs.promises.writeFile(postFilename, JSON.stringify(post, null, 2)); } catch (error) { - this.log.error('failed to write user blog post file', { postFilename, error }); + this.log.error('failed to write user blog post file', { post: { _id: post._id }, error }); // fall through } }); } + async archiveUserAttachments (job) { + const Attachment = mongoose.model('Attachment'); + const { minio: minioService } = this.dtp.services; + + job.data.attachmentPath = path.join(job.data.workPath, 'attachments'); + await fs.promises.mkdir(job.data.attachmentPath, { recursive: true }); + + job.data.originalAttachmentPath = path.join(job.data.attachmentPath, 'original'); + await fs.promises.mkdir(job.data.originalAttachmentPath, { recursive: true }); + + job.data.encodedAttachmentPath = path.join(job.data.attachmentPath, 'encoded'); + await fs.promises.mkdir(job.data.encodedAttachmentPath, { recursive: true }); + + this.log.info('archiving user attachments', { + user: { + _id: job.data.user._id, + username: job.data.user.username, + }, + }); + + await Attachment + .find({ owner: job.data.user._id }) + .cursor() + .eachAsync(async (attachment) => { + try { + /* + * Write the JSON record archive + */ + const metadataFilename = path.join(job.data.attachmentPath, `attachment-${attachment._id}.metadata.json`); + await fs.promises.writeFile(metadataFilename, JSON.stringify(attachment, null, 2)); + + /* + * Download and save the original file (if present) + */ + if (attachment.original && attachment.original.bucket && attachment.original.key) { + let originalExt = mime.getExtension(attachment.original.mime); + const originalFilename = path.join(job.data.originalAttachmentPath, `attachment-${attachment._id}.${originalExt}`); + await minioService.downloadFile({ + bucket: attachment.original.bucket, + key: attachment.original.key, + filePath: originalFilename, + }); + } + + /* + * Download and save the encoded file (if present) + */ + if (attachment.encoded && attachment.encoded.bucket && attachment.encoded.key) { + let encodedExt = mime.getExtension(attachment.encoded.mime); + const encodedFilename = path.join(job.data.encodedAttachmentPath, `attachment-${attachment._id}.${encodedExt}`); + await minioService.downloadFile({ + bucket: attachment.encoded.bucket, + key: attachment.encoded.key, + filePath: encodedFilename, + }); + } + } catch (error) { + this.log.error('failed to archive attachment', { + attachment: { _id: attachment._id }, + error, + }); + } + }); + } + async createArchiveFile (job) { const { minio: minioService } = this.dtp.services; try {