EmailBackupRequestedEventHandler.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { KeyParamsData } from '@standardnotes/responses'
  2. import {
  3. DomainEventHandlerInterface,
  4. DomainEventPublisherInterface,
  5. EmailBackupRequestedEvent,
  6. } from '@standardnotes/domain-events'
  7. import { EmailLevel } from '@standardnotes/domain-core'
  8. import { Logger } from 'winston'
  9. import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface'
  10. import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
  11. import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
  12. import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
  13. import { ItemTransferCalculatorInterface } from '../Item/ItemTransferCalculatorInterface'
  14. import { ItemQuery } from '../Item/ItemQuery'
  15. import { getBody, getSubject } from '../Email/EmailBackupAttachmentCreated'
  16. export class EmailBackupRequestedEventHandler implements DomainEventHandlerInterface {
  17. constructor(
  18. private primaryItemRepository: ItemRepositoryInterface,
  19. private secondaryItemRepository: ItemRepositoryInterface | null,
  20. private authHttpService: AuthHttpServiceInterface,
  21. private itemBackupService: ItemBackupServiceInterface,
  22. private domainEventPublisher: DomainEventPublisherInterface,
  23. private domainEventFactory: DomainEventFactoryInterface,
  24. private emailAttachmentMaxByteSize: number,
  25. private itemTransferCalculator: ItemTransferCalculatorInterface,
  26. private s3BackupBucketName: string,
  27. private logger: Logger,
  28. ) {}
  29. async handle(event: EmailBackupRequestedEvent): Promise<void> {
  30. await this.requestEmailWithBackupFile(event, this.primaryItemRepository)
  31. if (this.secondaryItemRepository) {
  32. await this.requestEmailWithBackupFile(event, this.secondaryItemRepository)
  33. }
  34. this.logger.info(`Email with backup requested for user ${event.payload.userUuid}`)
  35. }
  36. private async requestEmailWithBackupFile(
  37. event: EmailBackupRequestedEvent,
  38. itemRepository: ItemRepositoryInterface,
  39. ): Promise<void> {
  40. let authParams: KeyParamsData
  41. try {
  42. authParams = await this.authHttpService.getUserKeyParams({
  43. uuid: event.payload.userUuid,
  44. authenticated: false,
  45. })
  46. } catch (error) {
  47. this.logger.error(
  48. `Could not get user key params from auth service for user ${event.payload.userUuid}: ${JSON.stringify(error)}`,
  49. )
  50. return
  51. }
  52. const itemQuery: ItemQuery = {
  53. userUuid: event.payload.userUuid,
  54. sortBy: 'updated_at_timestamp',
  55. sortOrder: 'ASC',
  56. deleted: false,
  57. }
  58. const itemContentSizeDescriptors = await itemRepository.findContentSizeForComputingTransferLimit(itemQuery)
  59. const itemUuidBundles = await this.itemTransferCalculator.computeItemUuidBundlesToFetch(
  60. itemContentSizeDescriptors,
  61. this.emailAttachmentMaxByteSize,
  62. )
  63. const backupFileNames: string[] = []
  64. for (const itemUuidBundle of itemUuidBundles) {
  65. const items = await itemRepository.findAll({
  66. uuids: itemUuidBundle,
  67. sortBy: 'updated_at_timestamp',
  68. sortOrder: 'ASC',
  69. })
  70. const bundleBackupFileNames = await this.itemBackupService.backup(
  71. items,
  72. authParams,
  73. this.emailAttachmentMaxByteSize,
  74. )
  75. backupFileNames.push(...bundleBackupFileNames)
  76. }
  77. const dateOnly = new Date().toISOString().substring(0, 10)
  78. let bundleIndex = 1
  79. for (const backupFileName of backupFileNames) {
  80. await this.domainEventPublisher.publish(
  81. this.domainEventFactory.createEmailRequestedEvent({
  82. body: getBody(authParams.identifier as string),
  83. level: EmailLevel.LEVELS.System,
  84. messageIdentifier: 'DATA_BACKUP',
  85. subject: getSubject(bundleIndex++, backupFileNames.length, dateOnly),
  86. userEmail: authParams.identifier as string,
  87. sender: 'backups@standardnotes.org',
  88. attachments: [
  89. {
  90. fileName: backupFileName,
  91. filePath: this.s3BackupBucketName,
  92. attachmentFileName: `SN-Data-${dateOnly}.txt`,
  93. attachmentContentType: 'application/json',
  94. },
  95. ],
  96. }),
  97. )
  98. }
  99. }
  100. }