@social-mail/social-mail-web-server 1.7.91 → 1.7.95

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.
Files changed (36) hide show
  1. package/dist/protocols/core/ReactionExtractor.d.ts +4 -1
  2. package/dist/protocols/core/ReactionExtractor.d.ts.map +1 -1
  3. package/dist/protocols/core/ReactionExtractor.js +9 -3
  4. package/dist/protocols/core/ReactionExtractor.js.map +1 -1
  5. package/dist/server/model/entities/MailboxContactEmail.d.ts +0 -1
  6. package/dist/server/model/entities/MailboxContactEmail.d.ts.map +1 -1
  7. package/dist/server/model/entities/MailboxContactEmail.js +0 -4
  8. package/dist/server/model/entities/MailboxContactEmail.js.map +1 -1
  9. package/dist/server/seed/ui/seed-ui.js +1 -1
  10. package/dist/server/smtp/services/CachedEmailService.d.ts +1 -4
  11. package/dist/server/smtp/services/CachedEmailService.d.ts.map +1 -1
  12. package/dist/server/smtp/services/CachedEmailService.js +4 -23
  13. package/dist/server/smtp/services/CachedEmailService.js.map +1 -1
  14. package/dist/server/smtp/services/EmailReferenceService.d.ts.map +1 -1
  15. package/dist/server/smtp/services/EmailReferenceService.js +25 -28
  16. package/dist/server/smtp/services/EmailReferenceService.js.map +1 -1
  17. package/dist/server/smtp/services/EmailSerializerService.d.ts +13 -12
  18. package/dist/server/smtp/services/EmailSerializerService.d.ts.map +1 -1
  19. package/dist/server/smtp/services/EmailSerializerService.js +11 -3
  20. package/dist/server/smtp/services/EmailSerializerService.js.map +1 -1
  21. package/dist/server/workflows/pending/PendingReferencesWorkflow.js +1 -1
  22. package/dist/server/workflows/pending/PendingReferencesWorkflow.js.map +1 -1
  23. package/dist/server/workflows/smtp/DispatchEmailToMX.d.ts +2 -4
  24. package/dist/server/workflows/smtp/DispatchEmailToMX.d.ts.map +1 -1
  25. package/dist/server/workflows/smtp/DispatchEmailToMX.js +89 -91
  26. package/dist/server/workflows/smtp/DispatchEmailToMX.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +1 -1
  29. package/src/protocols/core/ReactionExtractor.ts +10 -4
  30. package/src/server/model/entities/MailboxContactEmail.ts +0 -3
  31. package/src/server/seed/ui/seed-ui.ts +1 -1
  32. package/src/server/smtp/services/CachedEmailService.ts +6 -31
  33. package/src/server/smtp/services/EmailReferenceService.ts +28 -33
  34. package/src/server/smtp/services/EmailSerializerService.ts +30 -17
  35. package/src/server/workflows/pending/PendingReferencesWorkflow.ts +1 -1
  36. package/src/server/workflows/smtp/DispatchEmailToMX.ts +104 -95
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.7.91",
3
+ "version": "1.7.95",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -14,14 +14,20 @@ export const ReactionExtractor = {
14
14
  headers: string | {[key: string]: any}
15
15
  }, attachmentsLoader: () => Promise<{ contentType: string }[]>, forceFullMatch = true) {
16
16
  try {
17
- let inReplyTo = null;
17
+ let inReplyTo = null as string;
18
+ let emailID = null as string;
18
19
  let engagement: string;
19
- const index = email.subject.indexOf(":");
20
+ let index = email.subject.indexOf(":");
20
21
  if (index !== -1 && index <= 50) {
21
22
  engagement = email.subject.substring(0, index);
22
23
  inReplyTo = email.subject.substring(index + 1);
24
+ index = inReplyTo.indexOf(":");
25
+ if (index !== -1) {
26
+ emailID = inReplyTo.substring(0, index);
27
+ inReplyTo = inReplyTo.substring(index+1);
28
+ }
23
29
  if (/\p{Emoji}/u.test(engagement)) {
24
- return { engagement, inReplyTo };
30
+ return { engagement, inReplyTo, emailID };
25
31
  }
26
32
  }
27
33
 
@@ -40,7 +46,7 @@ export const ReactionExtractor = {
40
46
  engagement = matches[0];
41
47
 
42
48
  if (engagement === textBody) {
43
- return { engagement };
49
+ return { engagement, inReplyTo, emailID };
44
50
  }
45
51
 
46
52
  /**
@@ -62,9 +62,6 @@ export default class MailboxContactEmail {
62
62
  @Column({ dataType: "Char", length: 200, nullable: true})
63
63
  engagement: string;
64
64
 
65
- @Column({ dataType: "BigInt", nullable: true })
66
- signedFileLogID: number;
67
-
68
65
  email: Email;
69
66
 
70
67
  mailbox: Mailbox;
@@ -3,7 +3,7 @@ import semver from "semver";
3
3
 
4
4
  export default async function seedUI(config: DBConfig) {
5
5
 
6
- const version = "1.4.111";
6
+ const version = "1.4.115";
7
7
 
8
8
  const existing = await config.getValue(UIPackageConfig, false);
9
9
  if (existing) {
@@ -25,11 +25,8 @@ export default class CachedEmailService {
25
25
  @Inject
26
26
  encryptionService: EncryptionService;
27
27
 
28
- async getHtmlForRecipient({ emailID, emailAddressID, mailboxID, signedFileLogID }: {
29
- emailID: number,
30
- emailAddressID?: number,
31
- signedFileLogID?: number,
32
- mailboxID?: number
28
+ async getHtmlForRecipient({ emailID }: {
29
+ emailID: number
33
30
  }) {
34
31
 
35
32
  const factory = async () => {
@@ -38,32 +35,10 @@ export default class CachedEmailService {
38
35
 
39
36
  let logFile: EmailLog;
40
37
 
41
- if (signedFileLogID) {
38
+ if (!logFile) {
42
39
  logFile = await this.db.emailLogs
43
- .where({ signedFileLogID }, (p) => (x) => x.logID === p.signedFileLogID)
44
- .first();
45
- } else {
46
-
47
- if (emailAddressID) {
48
-
49
- const mce = await this.db.mailboxContactEmails.where({ emailID, emailAddressID, mailboxID},
50
- (p) => (x) => x.emailAddressID === p.emailAddressID
51
- && x.emailID === p.emailID
52
- && x.mailboxID === p.mailboxID
53
- && x.signedFileLogID !== null
54
- ).first();
55
- if (mce) {
56
- signedFileLogID = mce.signedFileLogID;
57
- logFile = await this.db.emailLogs
58
- .where({ signedFileLogID }, (p) => (x) => x.logID === p.signedFileLogID)
59
- .first();
60
- }
61
- }
62
- if (!logFile) {
63
- logFile = await this.db.emailLogs
64
- .where({ emailID }, (p) => (x) => x.mimeFileEmails.some((mf) => mf.emailID === p.emailID))
65
- .first();
66
- }
40
+ .where({ emailID }, (p) => (x) => x.mimeFileEmails.some((mf) => mf.emailID === p.emailID))
41
+ .first();
67
42
  }
68
43
 
69
44
  let html = "";
@@ -101,7 +76,7 @@ export default class CachedEmailService {
101
76
  };
102
77
 
103
78
  const ef = await this.cacheService.get({
104
- key: `cached-email-html-a9-${emailID}-${emailAddressID}-${signedFileLogID}`,
79
+ key: `cached-email-html-b1-${emailID}`,
105
80
  factory,
106
81
  ttl: TimeSpan.fromDays(30)
107
82
  });
@@ -364,10 +364,7 @@ export default class EmailReferenceService {
364
364
 
365
365
  const senderMailbox = email.senderEmail?.mailbox;
366
366
 
367
- const saved = new Set();
368
-
369
-
370
- if (senderMailbox) {
367
+ if (senderMailbox && !email.received) {
371
368
  for (const { emailAddressID, emailAddress } of email.recipients) {
372
369
  if (!emailAddress) {
373
370
  console.warn(`EmailAddressID ${emailAddressID} in Recipient of EmailID ${emailID} not registered`);
@@ -375,7 +372,6 @@ export default class EmailReferenceService {
375
372
  }
376
373
  const { mailbox } = emailAddress;
377
374
  if (emailAddressID !== senderID) {
378
- saved.add(senderID);
379
375
  // Sender's Thread - Outbox
380
376
  await this.updateThread({
381
377
  engagement,
@@ -399,33 +395,33 @@ export default class EmailReferenceService {
399
395
  // emailID
400
396
  // });
401
397
 
402
- if (mailbox) {
403
-
404
- // this is the case when email was sent from the system
405
- // we will insert in respective mailboxes here...
406
- // await db.mailboxEmails.statements.selectOrInsert({
407
- // mailboxID: emailAddressID,
408
- // emailID
409
- // });
410
-
411
- saved.add(emailAddressID);
412
-
413
- // Recipient's thread - Inbox
414
- await this.updateThread({
415
- engagement,
416
- mailbox,
417
- mailboxID: emailAddressID,
418
- emailAddressID: senderID,
419
- lastEmailID: emailID,
420
- silent,
421
- timeRead,
422
- dateUpdated,
423
- tagIDs,
424
- email,
425
- newOpenThread,
426
- newThread,
427
- });
428
- }
398
+ // if (mailbox) {
399
+
400
+ // // this is the case when email was sent from the system
401
+ // // we will insert in respective mailboxes here...
402
+ // // await db.mailboxEmails.statements.selectOrInsert({
403
+ // // mailboxID: emailAddressID,
404
+ // // emailID
405
+ // // });
406
+
407
+ // saved.add(emailAddressID);
408
+
409
+ // // Recipient's thread - Inbox
410
+ // await this.updateThread({
411
+ // engagement,
412
+ // mailbox,
413
+ // mailboxID: emailAddressID,
414
+ // emailAddressID: senderID,
415
+ // lastEmailID: emailID,
416
+ // silent,
417
+ // timeRead,
418
+ // dateUpdated,
419
+ // tagIDs,
420
+ // email,
421
+ // newOpenThread,
422
+ // newThread,
423
+ // });
424
+ // }
429
425
  }
430
426
  } else {
431
427
 
@@ -436,7 +432,6 @@ export default class EmailReferenceService {
436
432
  for (const { emailAddressID, emailAddress: { mailbox } } of email.recipients) {
437
433
  // this is the case when email is received from outside
438
434
  // we will only store in mailboxes that exist on the system
439
- saved.add(emailAddressID);
440
435
  if (mailbox) {
441
436
  // await db.mailboxEmails.statements.selectOrInsert({
442
437
  // mailboxID: emailAddressID,
@@ -2,7 +2,7 @@ import Inject, { RegisterScoped } from "@entity-access/entity-access/dist/di/di.
2
2
  import TempFileService from "../../storage/TempFileService.js";
3
3
  import { AddressObject } from "mailparser";
4
4
  import BaseStorage from "../../storage/BaseStorage.js";
5
- import { emailStatusType } from "../../model/entities/Email.js";
5
+ import { EmailLog, emailStatusType } from "../../model/entities/Email.js";
6
6
  import EmailAddressService from "../../services/EmailAddressService.js";
7
7
  import SocialMailContext from "../../model/SocialMailContext.js";
8
8
  import type { contentDispositionTypes } from "../../model/entities/AppFile.js";
@@ -27,6 +27,7 @@ import { ReactionExtractor } from "../../../protocols/core/ReactionExtractor.js"
27
27
  import { stripBrackets } from "../../../common/stripBrackets.js";
28
28
  import SmtpError from "./SmtpError.js";
29
29
  import { SmtpStatusCodes } from "../client/SmtpStatusCodes.js";
30
+ import { Base36 } from "../../../common/Base36.js";
30
31
  // import PostReceiveWorkflow from "../../workflows/post-receive/PostReceiveWorkflow.js";
31
32
  // import UpdateEmailReferences from "../../workflows/incoming/UpdateEmailReferences.js";
32
33
 
@@ -91,6 +92,19 @@ export interface IEmailInfo {
91
92
  localEmailID?: number;
92
93
  };
93
94
 
95
+ export interface ISaveEmailInfo {
96
+ sender?: string,
97
+ localEmailID?: number,
98
+ mailboxes: Mailbox[],
99
+ received?: boolean,
100
+ silent?: boolean,
101
+ isDraft?: boolean,
102
+ ars?: any,
103
+ log?: string,
104
+ labels?: string[],
105
+ externalEmailID?: string
106
+ }
107
+
94
108
  @RegisterScoped
95
109
  export default class EmailSerializerService {
96
110
 
@@ -186,18 +200,7 @@ export default class EmailSerializerService {
186
200
  localEmailID,
187
201
  externalEmailID,
188
202
  labels
189
- }: {
190
- sender?: string,
191
- localEmailID?: number,
192
- mailboxes: Mailbox[],
193
- received?: boolean,
194
- silent?: boolean,
195
- isDraft?: boolean,
196
- ars?: any,
197
- log?: string,
198
- labels?: string[],
199
- externalEmailID?: string
200
- }) {
203
+ }: ISaveEmailInfo) {
201
204
 
202
205
  const { tempFileService, db } = this;
203
206
  db.verifyFilters = false;
@@ -360,12 +363,10 @@ export default class EmailSerializerService {
360
363
  tags = labels.map((x) => x.startsWith("#") ? x : `#${x}`).join(",");
361
364
  }
362
365
 
363
- // console.log(`Saving Log ${file.path}`);
364
- const mimeFile = await this.emailLogService.save(0, file);
365
- // console.log(`Saved Log ${file.path}`);
366
-
367
366
  let engagement = null;
368
367
 
368
+ let verifyParentID = null;
369
+
369
370
  if (received) {
370
371
  // check for engagements...
371
372
  const e1 = await ReactionExtractor.extractWithInReplyTo({
@@ -377,6 +378,7 @@ export default class EmailSerializerService {
377
378
  engagement = e1.engagement;
378
379
  if (!inReplyTo) {
379
380
  inReplyTo = e1.inReplyTo;
381
+ verifyParentID = Base36.decode(e1.emailID) as any;
380
382
  }
381
383
  }
382
384
 
@@ -386,11 +388,22 @@ export default class EmailSerializerService {
386
388
  .orderBy(void 0,(p) => (x) => x.emailID)
387
389
  .first();
388
390
  if (original) {
391
+
392
+ if (verifyParentID) {
393
+ if(verifyParentID !== original.emailID) {
394
+ throw new SmtpError("Unable to process this email.", SmtpStatusCodes.DeliveryNotPermitted);
395
+ }
396
+ }
397
+
389
398
  parentID = original.emailID;
390
399
  inReplyTo = null;
391
400
  }
392
401
  }
393
402
 
403
+ // console.log(`Saving Log ${file.path}`);
404
+ const mimeFile = await this.emailLogService.save(0, file);
405
+ // console.log(`Saved Log ${file.path}`);
406
+
394
407
  let emailID = void 0;
395
408
 
396
409
  if (localEmailID) {
@@ -13,7 +13,7 @@ export default class PendingReferencesWorkflow extends Workflow {
13
13
  static taskGroup = "default";
14
14
 
15
15
  static queue(context: WorkflowContext) {
16
- return context.queue(PendingReferencesWorkflow, {}, { id: `update-references`} );
16
+ return context.queue(PendingReferencesWorkflow, {}, { id: `update-references-${Date.now()}`, throttle: { group: "update-ref", maxPerSecond: 1 }} );
17
17
  }
18
18
 
19
19
  async run() {
@@ -18,6 +18,7 @@ import EmailReferenceService from "../../smtp/services/EmailReferenceService.js"
18
18
  import EmailPostReceiveService from "../../smtp/services/EmailPostReceiveService.js";
19
19
  import TagService from "../../services/tags/TagService.js";
20
20
  import { IMessageEnvelope } from "./IMessageEnvelope.js";
21
+ import EmailSerializerService from "../../smtp/services/EmailSerializerService.js";
21
22
 
22
23
  export const smtpPool = new NamedObjectPool<SMTPClient>((host) => ({
23
24
  asyncFactory: () => new SMTPClient({ host }).connect(),
@@ -148,113 +149,121 @@ export default class DispatchEmailToMX extends Workflow<IMessageEnvelope> {
148
149
  attempt: number,
149
150
  @Inject els?: EmailLogService,
150
151
  @Inject eas?: EmailAddressService,
151
- @Inject db?: SocialMailContext,
152
- @Inject eps?: EmailPostReceiveService,
153
- @Inject ts?: TagService,
154
- @Inject ers?: EmailReferenceService
152
+ @Inject ess?: EmailSerializerService,
155
153
  ) {
156
154
 
157
155
  // if it is local...
158
156
  // save signedFileLogID to MailboxContactEmail
159
157
  const { senderID, emailID, recipientAddressID, external } = this.input;
160
158
  const { signedFileLogID } = this;
161
- const mailboxID = await eas.getHostedMailboxID(this.input.to[0]);
162
-
163
- if (!external && mailboxID) {
164
-
165
- const dateReceived = DateTime.now;
166
- try {
167
-
168
- // internal broad casting is not working
169
- // we either need to create an email or add the recipient
170
-
171
- const emailAddressID = senderID;
172
-
173
- const email = await db.emails.loadByKeys({ emailID });
174
- await db.changeSet.getEntry(email).loadNavigationAsync((x) => x.emailTags);
175
-
176
- const tagIDs = email.emailTags?.map((x) => x.tagID);
177
-
178
- // await ers.updateThread({
179
- // email,
180
- // tagIDs,
181
-
182
- // });
183
-
184
- const folderTagID = await ts.getSystemTagID("New");
185
-
186
- let addFolder = true;
187
-
188
- await db.mailboxContacts.statements.upsert({
189
- mailboxID,
190
- emailAddressID,
191
- folderTagID,
192
- newRequest: true,
193
- }, (x) => {
194
- addFolder = false;
195
- x.unread++;
196
- x.total++;
197
- x.dateUpdated = dateReceived;
198
- if (!email.threadID) {
199
- x.openThreads++;
200
- x.totalThreads++;
201
- }
202
- x.lastEmailID = emailID;
203
- return x;
204
- }, {
205
- mailboxID,
206
- emailAddressID
207
- });
208
-
209
- if (addFolder) {
210
- await db.mailboxFolders.statements.upsert({
211
- mailboxID,
212
- tagID: folderTagID,
213
- count: 1
214
- }, (x) => {
215
- x.count++;
216
- return x;
217
- }, {
218
- mailboxID,
219
- tagID: folderTagID
220
- });
221
- }
222
159
 
223
- if (tagIDs) {
224
- for (const tagID of tagIDs) {
225
- await db.mailboxContactTags.statements.selectOrInsert({
226
- tagID,
227
- emailAddressID,
228
- mailboxID
229
- });
230
- await db.mailboxTags.statements.selectOrInsert({
231
- tagID,
232
- mailboxID
233
- });
234
- }
235
- }
160
+ const mailbox = await eas.getMailbox(this.input.to[0]);
236
161
 
237
- // await db.mailboxEmails.statements.selectOrInsert({
238
- // mailboxID,
239
- // emailID
240
- // });
162
+ if (!external && mailbox) {
241
163
 
242
- await db.mailboxContactEmails.statements.selectOrInsert({
243
- mailboxID,
244
- emailID,
245
- emailAddressID,
246
- signedFileLogID,
247
- dateReceived
248
- });
249
164
 
165
+ const localFile = await els.load({ logID: signedFileLogID });
250
166
 
251
- // await ers.updateReferences({ emailID, dateUpdated: dateReceived, silent: false, timeRead: null });
252
- if (senderID !== emailAddressID) {
253
- await eps.execute({ senderID, emailID, recipientID: emailAddressID });
254
- }
255
- } catch {
167
+ await ess.save(localFile, {
168
+ mailboxes: [mailbox],
169
+ isDraft: false,
170
+ received: true,
171
+ sender: this.input.from
172
+ });
256
173
 
257
- }
174
+ // const dateReceived = DateTime.now;
175
+ // try {
176
+
177
+ // // internal broad casting is not working
178
+ // // we either need to create an email or add the recipient
179
+
180
+ // const emailAddressID = senderID;
181
+
182
+ // const email = await db.emails.loadByKeys({ emailID });
183
+ // await db.changeSet.getEntry(email).loadNavigationAsync((x) => x.emailTags);
184
+
185
+ // const tagIDs = email.emailTags?.map((x) => x.tagID);
186
+
187
+ // // await ers.updateThread({
188
+ // // email,
189
+ // // tagIDs,
190
+
191
+ // // });
192
+
193
+ // const folderTagID = await ts.getSystemTagID("New");
194
+
195
+ // let addFolder = true;
196
+
197
+ // await db.mailboxContacts.statements.upsert({
198
+ // mailboxID,
199
+ // emailAddressID,
200
+ // folderTagID,
201
+ // newRequest: true,
202
+ // }, (x) => {
203
+ // addFolder = false;
204
+ // x.unread++;
205
+ // x.total++;
206
+ // x.dateUpdated = dateReceived;
207
+ // if (!email.threadID) {
208
+ // x.openThreads++;
209
+ // x.totalThreads++;
210
+ // }
211
+ // x.lastEmailID = emailID;
212
+ // return x;
213
+ // }, {
214
+ // mailboxID,
215
+ // emailAddressID
216
+ // });
217
+
218
+ // if (addFolder) {
219
+ // await db.mailboxFolders.statements.upsert({
220
+ // mailboxID,
221
+ // tagID: folderTagID,
222
+ // count: 1
223
+ // }, (x) => {
224
+ // x.count++;
225
+ // return x;
226
+ // }, {
227
+ // mailboxID,
228
+ // tagID: folderTagID
229
+ // });
230
+ // }
231
+
232
+ // if (tagIDs) {
233
+ // for (const tagID of tagIDs) {
234
+ // await db.mailboxContactTags.statements.selectOrInsert({
235
+ // tagID,
236
+ // emailAddressID,
237
+ // mailboxID
238
+ // });
239
+ // await db.mailboxTags.statements.selectOrInsert({
240
+ // tagID,
241
+ // mailboxID
242
+ // });
243
+ // }
244
+ // }
245
+
246
+ // // await db.mailboxEmails.statements.selectOrInsert({
247
+ // // mailboxID,
248
+ // // emailID
249
+ // // });
250
+
251
+ // await db.mailboxContactEmails.statements.selectOrInsert({
252
+ // mailboxID,
253
+ // emailID,
254
+ // emailAddressID,
255
+ // signedFileLogID,
256
+ // dateReceived
257
+ // });
258
+
259
+
260
+ // // await ers.updateReferences({ emailID, dateUpdated: dateReceived, silent: false, timeRead: null });
261
+ // if (senderID !== emailAddressID) {
262
+ // await eps.execute({ senderID, emailID, recipientID: emailAddressID });
263
+ // }
264
+ // } catch {
265
+
266
+ // }
258
267
 
259
268
  // await PostReceiveWorkflow.queue(this.context, {
260
269
  // senderID: this.input.senderID,