@stamhoofd/models 2.96.3 → 2.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ const EmailRecipient_1 = require("./EmailRecipient");
16
16
  const EmailTemplate_1 = require("./EmailTemplate");
17
17
  const Organization_1 = require("./Organization");
18
18
  const User_1 = require("./User");
19
+ const Platform_1 = require("./Platform");
19
20
  function errorToSimpleErrors(e) {
20
21
  if ((0, simple_errors_1.isSimpleErrors)(e)) {
21
22
  return e;
@@ -27,7 +28,7 @@ function errorToSimpleErrors(e) {
27
28
  return new simple_errors_1.SimpleErrors(new simple_errors_1.SimpleError({
28
29
  code: 'unknown_error',
29
30
  message: ((typeof e === 'object' && e !== null && 'message' in e && typeof e.message === 'string') ? e.message : 'Unknown error'),
30
- human: $t(`Onbekende fout`),
31
+ human: $t(`41db9fc8-77f4-49a7-a77b-40a4ae8c4d8f`),
31
32
  }));
32
33
  }
33
34
  }
@@ -37,6 +38,19 @@ class Email extends sql_1.QueryableModel {
37
38
  organizationId = null;
38
39
  senderId = null;
39
40
  userId = null;
41
+ /**
42
+ * Send the message as an email.
43
+ * You can't edit this after the message has been published.
44
+ *
45
+ * If false, when sending the message, it will switch to 'Sent' directly without adjusting the email_recipients directly.
46
+ */
47
+ sendAsEmail = true;
48
+ /**
49
+ * Show the message in the member portal
50
+ *
51
+ * Note: status should be 'Sent' for the message to be visible
52
+ */
53
+ showInMemberPortal = true;
40
54
  recipientFilter = structures_1.EmailRecipientFilter.create({});
41
55
  /**
42
56
  * Helper to prevent sending too many emails to the same person.
@@ -144,18 +158,50 @@ class Email extends sql_1.QueryableModel {
144
158
  throw new simple_errors_1.SimpleError({
145
159
  code: 'invalid_recipients',
146
160
  message: 'Failed to build recipients (count)',
147
- human: $t(`Er ging iets mis bij het aanmaken van de ontvangers. Probeer je selectie aan te passen. Neem contact op als het probleem zich blijft voordoen.`) + ' ' + this.recipientsErrors.getHuman(),
161
+ human: $t(`457ec920-2867-4d59-bbec-4466677e1b50`) + ' ' + this.recipientsErrors.getHuman(),
148
162
  });
149
163
  }
150
164
  if (this.deletedAt) {
151
165
  throw new simple_errors_1.SimpleError({
152
166
  code: 'invalid_state',
153
167
  message: 'Email is deleted',
154
- human: $t(`Deze e-mail is verwijderd en kan niet verzonden worden.`),
168
+ human: $t(`a0524f41-bdde-4fcc-9a9a-9350905377d8`),
155
169
  });
156
170
  }
157
171
  this.validateAttachments();
158
172
  }
173
+ throwIfNoUnsubscribeButton() {
174
+ if (this.sendAsEmail === false) {
175
+ return;
176
+ }
177
+ if (this.emailType) {
178
+ // System email, no need for unsubscribe button
179
+ return;
180
+ }
181
+ const replacement = '{{unsubscribeUrl}}';
182
+ if (this.html) {
183
+ // Check email contains an unsubscribe button
184
+ if (!this.html.includes(replacement)) {
185
+ throw new simple_errors_1.SimpleError({
186
+ code: 'missing_unsubscribe_button',
187
+ message: 'Missing unsubscribe button',
188
+ human: $t(`dd55e04b-e5d9-4d9a-befc-443eef4175a8`),
189
+ field: 'html',
190
+ });
191
+ }
192
+ }
193
+ if (this.text) {
194
+ // Check email contains an unsubscribe button
195
+ if (!this.text.includes(replacement)) {
196
+ throw new simple_errors_1.SimpleError({
197
+ code: 'missing_unsubscribe_button',
198
+ message: 'Missing unsubscribe button',
199
+ human: $t(`dd55e04b-e5d9-4d9a-befc-443eef4175a8`),
200
+ field: 'text',
201
+ });
202
+ }
203
+ }
204
+ }
159
205
  validateAttachments() {
160
206
  // Validate attachments
161
207
  const size = this.attachments.reduce((value, attachment) => {
@@ -361,6 +407,7 @@ class Email extends sql_1.QueryableModel {
361
407
  }
362
408
  async queueForSending(waitForSending = false) {
363
409
  this.throwIfNotReadyToSend();
410
+ this.throwIfNoUnsubscribeButton();
364
411
  await this.lock(async (upToDate) => {
365
412
  if (upToDate.status === structures_1.EmailStatus.Draft) {
366
413
  upToDate.status = structures_1.EmailStatus.Queued;
@@ -417,6 +464,7 @@ class Email extends sql_1.QueryableModel {
417
464
  let failedCount = 0;
418
465
  try {
419
466
  upToDate.throwIfNotReadyToSend();
467
+ upToDate.throwIfNoUnsubscribeButton();
420
468
  if (!from) {
421
469
  throw new simple_errors_1.SimpleError({
422
470
  code: 'invalid_field',
@@ -454,195 +502,197 @@ class Email extends sql_1.QueryableModel {
454
502
  });
455
503
  }
456
504
  // Create a buffer of all attachments
457
- for (const attachment of upToDate.attachments) {
458
- if (!attachment.content && !attachment.file) {
459
- console.warn('Attachment without content found, skipping', attachment);
460
- continue;
461
- }
462
- let filename = $t('b1291584-d2ad-4ebd-88ed-cbda4f3755b4');
463
- if (attachment.contentType === 'application/pdf') {
464
- // tmp solution for pdf only
465
- filename += '.pdf';
466
- }
467
- if (attachment.file?.name) {
468
- filename = attachment.file.name.toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
469
- }
470
- // Correct file name if needed
471
- if (attachment.filename) {
472
- filename = attachment.filename.toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
473
- }
474
- if (attachment.content) {
475
- attachments.push({
476
- filename: filename,
477
- content: attachment.content,
478
- contentType: attachment.contentType ?? undefined,
479
- encoding: 'base64',
480
- });
481
- }
482
- else {
483
- // Note: because we send lots of emails, we better download the file here so we can reuse it in every email instead of downloading it every time
484
- const withSigned = await attachment.file.withSignedUrl();
485
- if (!withSigned || !withSigned.signedUrl) {
486
- throw new simple_errors_1.SimpleError({
487
- code: 'attachment_not_found',
488
- message: 'Attachment not found',
489
- human: $t(`ce6ddaf0-8347-42c5-b4b7-fbe860c7b7f2`),
490
- });
505
+ if (upToDate.sendAsEmail === true) {
506
+ for (const attachment of upToDate.attachments) {
507
+ if (!attachment.content && !attachment.file) {
508
+ console.warn('Attachment without content found, skipping', attachment);
509
+ continue;
491
510
  }
492
- const filePath = withSigned.signedUrl;
493
- let fileBuffer = null;
494
- try {
495
- const response = await fetch(filePath);
496
- fileBuffer = Buffer.from(await response.arrayBuffer());
511
+ let filename = $t('b1291584-d2ad-4ebd-88ed-cbda4f3755b4');
512
+ if (attachment.contentType === 'application/pdf') {
513
+ // tmp solution for pdf only
514
+ filename += '.pdf';
515
+ }
516
+ if (attachment.file?.name) {
517
+ filename = attachment.file.name.toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
497
518
  }
498
- catch (e) {
499
- throw new simple_errors_1.SimpleError({
500
- code: 'attachment_not_found',
501
- message: 'Attachment not found',
502
- human: $t(`ce6ddaf0-8347-42c5-b4b7-fbe860c7b7f2`),
519
+ // Correct file name if needed
520
+ if (attachment.filename) {
521
+ filename = attachment.filename.toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
522
+ }
523
+ if (attachment.content) {
524
+ attachments.push({
525
+ filename: filename,
526
+ content: attachment.content,
527
+ contentType: attachment.contentType ?? undefined,
528
+ encoding: 'base64',
529
+ });
530
+ }
531
+ else {
532
+ // Note: because we send lots of emails, we better download the file here so we can reuse it in every email instead of downloading it every time
533
+ const withSigned = await attachment.file.withSignedUrl();
534
+ if (!withSigned || !withSigned.signedUrl) {
535
+ throw new simple_errors_1.SimpleError({
536
+ code: 'attachment_not_found',
537
+ message: 'Attachment not found',
538
+ human: $t(`ce6ddaf0-8347-42c5-b4b7-fbe860c7b7f2`),
539
+ });
540
+ }
541
+ const filePath = withSigned.signedUrl;
542
+ let fileBuffer = null;
543
+ try {
544
+ const response = await fetch(filePath);
545
+ fileBuffer = Buffer.from(await response.arrayBuffer());
546
+ }
547
+ catch (e) {
548
+ throw new simple_errors_1.SimpleError({
549
+ code: 'attachment_not_found',
550
+ message: 'Attachment not found',
551
+ human: $t(`ce6ddaf0-8347-42c5-b4b7-fbe860c7b7f2`),
552
+ });
553
+ }
554
+ attachments.push({
555
+ filename: filename,
556
+ contentType: attachment.contentType ?? undefined,
557
+ content: fileBuffer,
503
558
  });
504
559
  }
505
- attachments.push({
506
- filename: filename,
507
- contentType: attachment.contentType ?? undefined,
508
- content: fileBuffer,
509
- });
510
- }
511
- }
512
- // Start actually sending in batches of recipients that are not yet sent
513
- let idPointer = '';
514
- const batchSize = 100;
515
- let isSavingStatus = false;
516
- let lastStatusSave = new Date();
517
- async function saveStatus() {
518
- if (!upToDate) {
519
- return;
520
- }
521
- if (isSavingStatus) {
522
- return;
523
- }
524
- if ((new Date().getTime() - lastStatusSave.getTime()) < 1000 * 5) {
525
- // Save at most every 5 seconds
526
- return;
527
- }
528
- if (succeededCount < upToDate.succeededCount || softFailedCount < upToDate.softFailedCount || failedCount < upToDate.failedCount) {
529
- // Do not update on retries
530
- return;
531
- }
532
- lastStatusSave = new Date();
533
- isSavingStatus = true;
534
- upToDate.succeededCount = succeededCount;
535
- upToDate.softFailedCount = softFailedCount;
536
- upToDate.failedCount = failedCount;
537
- try {
538
- await upToDate.save();
539
- }
540
- finally {
541
- isSavingStatus = false;
542
- }
543
- }
544
- while (true) {
545
- abort.throwIfAborted();
546
- const data = await sql_1.SQL.select()
547
- .from('email_recipients')
548
- .where('emailId', upToDate.id)
549
- .where('id', sql_1.SQLWhereSign.Greater, idPointer)
550
- .orderBy(sql_1.SQL.column('id'), 'ASC')
551
- .limit(batchSize)
552
- .fetch();
553
- const recipients = EmailRecipient_1.EmailRecipient.fromRows(data, 'email_recipients');
554
- if (recipients.length === 0) {
555
- break;
556
560
  }
557
- const sendingPromises = [];
558
- let skipped = 0;
559
- for (const recipient of recipients) {
560
- idPointer = recipient.id;
561
- if (recipient.sentAt) {
562
- succeededCount += 1;
563
- await saveStatus();
564
- skipped++;
565
- continue;
561
+ // Start actually sending in batches of recipients that are not yet sent
562
+ let idPointer = '';
563
+ const batchSize = 100;
564
+ let isSavingStatus = false;
565
+ let lastStatusSave = new Date();
566
+ async function saveStatus() {
567
+ if (!upToDate) {
568
+ return;
566
569
  }
567
- if (!recipient.email) {
568
- skipped++;
569
- continue;
570
+ if (isSavingStatus) {
571
+ return;
570
572
  }
571
- if (recipient.duplicateOfRecipientId) {
572
- skipped++;
573
- continue;
573
+ if ((new Date().getTime() - lastStatusSave.getTime()) < 1000 * 5) {
574
+ // Save at most every 5 seconds
575
+ return;
574
576
  }
575
- let promiseResolve;
576
- const promise = new Promise((resolve) => {
577
- promiseResolve = resolve;
578
- });
579
- const virtualRecipient = recipient.getRecipient();
580
- let resolved = false;
581
- const callback = async (error) => {
582
- if (resolved) {
583
- return;
577
+ if (succeededCount < upToDate.succeededCount || softFailedCount < upToDate.softFailedCount || failedCount < upToDate.failedCount) {
578
+ // Do not update on retries
579
+ return;
580
+ }
581
+ lastStatusSave = new Date();
582
+ isSavingStatus = true;
583
+ upToDate.succeededCount = succeededCount;
584
+ upToDate.softFailedCount = softFailedCount;
585
+ upToDate.failedCount = failedCount;
586
+ try {
587
+ await upToDate.save();
588
+ }
589
+ finally {
590
+ isSavingStatus = false;
591
+ }
592
+ }
593
+ while (true) {
594
+ abort.throwIfAborted();
595
+ const data = await sql_1.SQL.select()
596
+ .from('email_recipients')
597
+ .where('emailId', upToDate.id)
598
+ .where('id', sql_1.SQLWhereSign.Greater, idPointer)
599
+ .orderBy(sql_1.SQL.column('id'), 'ASC')
600
+ .limit(batchSize)
601
+ .fetch();
602
+ const recipients = EmailRecipient_1.EmailRecipient.fromRows(data, 'email_recipients');
603
+ if (recipients.length === 0) {
604
+ break;
605
+ }
606
+ const sendingPromises = [];
607
+ let skipped = 0;
608
+ for (const recipient of recipients) {
609
+ idPointer = recipient.id;
610
+ if (recipient.sentAt) {
611
+ succeededCount += 1;
612
+ await saveStatus();
613
+ skipped++;
614
+ continue;
584
615
  }
585
- resolved = true;
586
- try {
587
- if (error === null) {
588
- // Mark saved
589
- recipient.sentAt = new Date();
590
- // Update repacements that have been generated
591
- recipient.replacements = virtualRecipient.replacements;
592
- succeededCount += 1;
593
- await recipient.save();
594
- await saveStatus();
616
+ if (!recipient.email) {
617
+ skipped++;
618
+ continue;
619
+ }
620
+ if (recipient.duplicateOfRecipientId) {
621
+ skipped++;
622
+ continue;
623
+ }
624
+ let promiseResolve;
625
+ const promise = new Promise((resolve) => {
626
+ promiseResolve = resolve;
627
+ });
628
+ const virtualRecipient = recipient.getRecipient();
629
+ let resolved = false;
630
+ const callback = async (error) => {
631
+ if (resolved) {
632
+ return;
595
633
  }
596
- else {
597
- recipient.failCount += 1;
598
- recipient.failErrorMessage = error.message;
599
- recipient.failError = errorToSimpleErrors(error);
600
- recipient.firstFailedAt = recipient.firstFailedAt ?? new Date();
601
- recipient.lastFailedAt = new Date();
602
- if ((0, structures_1.isSoftEmailRecipientError)(recipient.failError)) {
603
- softFailedCount += 1;
634
+ resolved = true;
635
+ try {
636
+ if (error === null) {
637
+ // Mark saved
638
+ recipient.sentAt = new Date();
639
+ // Update repacements that have been generated
640
+ recipient.replacements = virtualRecipient.replacements;
641
+ succeededCount += 1;
642
+ await recipient.save();
643
+ await saveStatus();
604
644
  }
605
645
  else {
606
- failedCount += 1;
646
+ recipient.failCount += 1;
647
+ recipient.failErrorMessage = error.message;
648
+ recipient.failError = errorToSimpleErrors(error);
649
+ recipient.firstFailedAt = recipient.firstFailedAt ?? new Date();
650
+ recipient.lastFailedAt = new Date();
651
+ if ((0, structures_1.isSoftEmailRecipientError)(recipient.failError)) {
652
+ softFailedCount += 1;
653
+ }
654
+ else {
655
+ failedCount += 1;
656
+ }
657
+ await recipient.save();
658
+ await saveStatus();
607
659
  }
608
- await recipient.save();
609
- await saveStatus();
610
660
  }
611
- }
612
- catch (e) {
613
- console.error(e);
614
- }
615
- promiseResolve();
616
- };
617
- // Do send the email
618
- // Create e-mail builder
619
- const builder = await (0, EmailBuilder_1.getEmailBuilder)(organization ?? null, {
620
- recipients: [
621
- virtualRecipient,
622
- ],
623
- from,
624
- replyTo,
625
- subject: upToDate.subject,
626
- html: upToDate.html,
627
- type: upToDate.emailType ? 'transactional' : 'broadcast',
628
- attachments,
629
- callback(error) {
630
- callback(error).catch(console.error);
631
- },
632
- headers: {
633
- 'X-Email-Id': upToDate.id,
634
- 'X-Email-Recipient-Id': recipient.id,
635
- },
636
- });
637
- abort.throwIfAborted(); // do not schedule if aborted
638
- email_1.Email.schedule(builder);
639
- sendingPromises.push(promise);
640
- }
641
- if (sendingPromises.length > 0 || skipped > 0) {
642
- await Promise.all(sendingPromises);
643
- }
644
- else {
645
- break;
661
+ catch (e) {
662
+ console.error(e);
663
+ }
664
+ promiseResolve();
665
+ };
666
+ // Do send the email
667
+ // Create e-mail builder
668
+ const builder = await (0, EmailBuilder_1.getEmailBuilder)(organization ?? null, {
669
+ recipients: [
670
+ virtualRecipient,
671
+ ],
672
+ from,
673
+ replyTo,
674
+ subject: upToDate.subject,
675
+ html: upToDate.html,
676
+ type: upToDate.emailType ? 'transactional' : 'broadcast',
677
+ attachments,
678
+ callback(error) {
679
+ callback(error).catch(console.error);
680
+ },
681
+ headers: {
682
+ 'X-Email-Id': upToDate.id,
683
+ 'X-Email-Recipient-Id': recipient.id,
684
+ },
685
+ });
686
+ abort.throwIfAborted(); // do not schedule if aborted
687
+ email_1.Email.schedule(builder);
688
+ sendingPromises.push(promise);
689
+ }
690
+ if (sendingPromises.length > 0 || skipped > 0) {
691
+ await Promise.all(sendingPromises);
692
+ }
693
+ else {
694
+ break;
695
+ }
646
696
  }
647
697
  }
648
698
  }
@@ -663,7 +713,7 @@ class Email extends sql_1.QueryableModel {
663
713
  await upToDate.save();
664
714
  throw e;
665
715
  }
666
- if (upToDate.emailRecipientsCount === 0 && upToDate.userId === null) {
716
+ if (upToDate.sendAsEmail && upToDate.emailRecipientsCount === 0 && upToDate.userId === null) {
667
717
  // We only delete automated emails (email type) if they have no recipients
668
718
  console.log('No recipients found for email ', upToDate.id, ' deleting...');
669
719
  await upToDate.delete();
@@ -671,12 +721,12 @@ class Email extends sql_1.QueryableModel {
671
721
  }
672
722
  console.log('Finished sending email', upToDate.id);
673
723
  // Mark email as sent
674
- if ((succeededCount + failedCount + softFailedCount) === 0) {
724
+ if (upToDate.sendAsEmail && !upToDate.showInMemberPortal && (succeededCount + failedCount + softFailedCount) === 0) {
675
725
  upToDate.status = structures_1.EmailStatus.Failed;
676
726
  upToDate.emailErrors = new simple_errors_1.SimpleErrors(new simple_errors_1.SimpleError({
677
727
  code: 'no_recipients',
678
728
  message: 'No recipients',
679
- human: $t(`Geen ontvangers gevonden`),
729
+ human: $t(`9fe3de8e-090c-4949-97da-4810ce9e61c7`),
680
730
  }));
681
731
  }
682
732
  else {
@@ -765,6 +815,15 @@ class Email extends sql_1.QueryableModel {
765
815
  return;
766
816
  }
767
817
  abort.throwIfAborted();
818
+ const organization = upToDate.organizationId ? (await Organization_1.Organization.getByID(upToDate.organizationId) ?? null) : null;
819
+ if (upToDate.organizationId && !organization) {
820
+ throw new simple_errors_1.SimpleError({
821
+ code: 'organization_not_found',
822
+ message: 'Organization not found',
823
+ human: $t(`f3c6e2b1-2f3a-4e2f-8f7a-1e5f3d3c8e2a`),
824
+ });
825
+ }
826
+ const platform = await Platform_1.Platform.getSharedPrivateStruct();
768
827
  console.log('Building recipients for email', id);
769
828
  // If it is already creating -> something went wrong (e.g. server restart) and we can safely try again
770
829
  upToDate.recipientsStatus = structures_1.EmailRecipientsStatus.Creating;
@@ -799,7 +858,25 @@ class Email extends sql_1.QueryableModel {
799
858
  if (!item.email && !item.memberId && !item.userId) {
800
859
  continue;
801
860
  }
802
- item.replacements = (0, EmailBuilder_1.removeUnusedReplacements)(upToDate.html ?? '', item.replacements);
861
+ const recipient = new EmailRecipient_1.EmailRecipient();
862
+ recipient.emailType = upToDate.emailType;
863
+ recipient.objectId = item.objectId;
864
+ recipient.emailId = upToDate.id;
865
+ recipient.email = item.email;
866
+ recipient.firstName = item.firstName;
867
+ recipient.lastName = item.lastName;
868
+ recipient.replacements = item.replacements;
869
+ recipient.memberId = item.memberId ?? null;
870
+ recipient.userId = item.userId ?? null;
871
+ recipient.organizationId = upToDate.organizationId ?? null;
872
+ await (0, EmailBuilder_1.fillRecipientReplacements)(recipient, {
873
+ platform,
874
+ organization,
875
+ from: upToDate.getFromAddress(),
876
+ replyTo: null,
877
+ forPreview: false,
878
+ });
879
+ recipient.replacements = (0, EmailBuilder_1.removeUnusedReplacements)(upToDate.html ?? '', recipient.replacements);
803
880
  let duplicateOfRecipientId = null;
804
881
  if (item.email && emailsSet.has(item.email)) {
805
882
  console.log('Found duplicate email recipient', item.email);
@@ -807,17 +884,18 @@ class Email extends sql_1.QueryableModel {
807
884
  const existing = await EmailRecipient_1.EmailRecipient.select()
808
885
  .where('emailId', upToDate.id)
809
886
  .where('email', item.email)
887
+ .where('duplicateOfRecipientId', null)
810
888
  .fetch();
811
889
  for (const other of existing) {
812
- const merged = (0, EmailBuilder_1.mergeReplacementsIfEqual)(other.replacements, item.replacements);
890
+ const merged = (0, EmailBuilder_1.mergeReplacementsIfEqual)(other.replacements, recipient.replacements);
813
891
  if (merged !== false) {
814
- console.log('Found duplicate email recipient', item.email, other.id);
892
+ console.log('Found mergeable duplicate email recipient', item.email, other.id);
815
893
  duplicateOfRecipientId = other.id;
816
894
  other.replacements = merged;
817
895
  other.firstName = other.firstName || item.firstName;
818
896
  other.lastName = other.lastName || item.lastName;
819
897
  await other.save();
820
- item.replacements = merged;
898
+ recipient.replacements = merged;
821
899
  break;
822
900
  }
823
901
  else {
@@ -825,17 +903,6 @@ class Email extends sql_1.QueryableModel {
825
903
  }
826
904
  }
827
905
  }
828
- const recipient = new EmailRecipient_1.EmailRecipient();
829
- recipient.emailType = upToDate.emailType;
830
- recipient.objectId = item.objectId;
831
- recipient.emailId = upToDate.id;
832
- recipient.email = item.email;
833
- recipient.firstName = item.firstName;
834
- recipient.lastName = item.lastName;
835
- recipient.replacements = item.replacements;
836
- recipient.memberId = item.memberId ?? null;
837
- recipient.userId = item.userId ?? null;
838
- recipient.organizationId = upToDate.organizationId ?? null;
839
906
  recipient.duplicateOfRecipientId = duplicateOfRecipientId;
840
907
  await recipient.save();
841
908
  if (recipient.memberId) {
@@ -1069,6 +1136,12 @@ tslib_1.__decorate([
1069
1136
  tslib_1.__decorate([
1070
1137
  (0, simple_database_1.column)({ type: 'string', nullable: true })
1071
1138
  ], Email.prototype, "userId", void 0);
1139
+ tslib_1.__decorate([
1140
+ (0, simple_database_1.column)({ type: 'boolean' })
1141
+ ], Email.prototype, "sendAsEmail", void 0);
1142
+ tslib_1.__decorate([
1143
+ (0, simple_database_1.column)({ type: 'boolean' })
1144
+ ], Email.prototype, "showInMemberPortal", void 0);
1072
1145
  tslib_1.__decorate([
1073
1146
  (0, simple_database_1.column)({ type: 'json', decoder: structures_1.EmailRecipientFilter })
1074
1147
  ], Email.prototype, "recipientFilter", void 0);