@social-mail/social-mail-web-server 1.7.85 → 1.7.87

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 (40) hide show
  1. package/dist/common/globalEnv.d.ts +1 -0
  2. package/dist/common/globalEnv.d.ts.map +1 -1
  3. package/dist/common/globalEnv.js +3 -1
  4. package/dist/common/globalEnv.js.map +1 -1
  5. package/dist/protocols/core/ReactionExtractor.d.ts +19 -1
  6. package/dist/protocols/core/ReactionExtractor.d.ts.map +1 -1
  7. package/dist/protocols/core/ReactionExtractor.js +15 -9
  8. package/dist/protocols/core/ReactionExtractor.js.map +1 -1
  9. package/dist/server/model/entities/Mailbox.d.ts +1 -0
  10. package/dist/server/model/entities/Mailbox.d.ts.map +1 -1
  11. package/dist/server/model/entities/Mailbox.js +4 -0
  12. package/dist/server/model/entities/Mailbox.js.map +1 -1
  13. package/dist/server/seed/ui/seed-ui.js +1 -1
  14. package/dist/server/services/cache/DiskCacheService.d.ts.map +1 -1
  15. package/dist/server/services/cache/DiskCacheService.js +2 -5
  16. package/dist/server/services/cache/DiskCacheService.js.map +1 -1
  17. package/dist/server/services/email-logs/EmailLogService.js +1 -1
  18. package/dist/server/services/email-logs/EmailLogService.js.map +1 -1
  19. package/dist/server/smtp/services/EmailPostReceiveService.d.ts +7 -3
  20. package/dist/server/smtp/services/EmailPostReceiveService.d.ts.map +1 -1
  21. package/dist/server/smtp/services/EmailPostReceiveService.js +68 -50
  22. package/dist/server/smtp/services/EmailPostReceiveService.js.map +1 -1
  23. package/dist/server/smtp/services/EmailSerializerService.d.ts.map +1 -1
  24. package/dist/server/smtp/services/EmailSerializerService.js +27 -11
  25. package/dist/server/smtp/services/EmailSerializerService.js.map +1 -1
  26. package/dist/server/workflows/pending/PendingReferencesWorkflow.d.ts.map +1 -1
  27. package/dist/server/workflows/pending/PendingReferencesWorkflow.js +3 -0
  28. package/dist/server/workflows/pending/PendingReferencesWorkflow.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +3 -3
  31. package/src/common/globalEnv.ts +4 -1
  32. package/src/protocols/core/ReactionExtractor.ts +20 -9
  33. package/src/server/model/entities/Mailbox.ts +3 -0
  34. package/src/server/seed/ui/seed-ui.ts +1 -1
  35. package/src/server/services/cache/DiskCacheService.ts +2 -7
  36. package/src/server/services/email-logs/EmailLogService.ts +1 -1
  37. package/src/server/smtp/services/EmailPostReceiveService.ts +90 -54
  38. package/src/server/smtp/services/EmailSerializerService.ts +34 -11
  39. package/src/server/workflows/pending/PendingReferencesWorkflow.ts +4 -0
  40. package/test.js +12 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.7.85",
3
+ "version": "1.7.87",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -21,8 +21,8 @@
21
21
  "dependencies": {
22
22
  "@aws-sdk/client-s3": "^3.717.0",
23
23
  "@azure/storage-blob": "^12.25.0",
24
- "@entity-access/entity-access": "^1.0.401",
25
- "@entity-access/server-pages": "^1.1.400",
24
+ "@entity-access/entity-access": "^1.0.402",
25
+ "@entity-access/server-pages": "^1.1.401",
26
26
  "@ffmpeg/core-mt": "^0.12.6",
27
27
  "@ffmpeg/ffmpeg": "^0.12.10",
28
28
  "@ffmpeg/util": "^0.12.1",
@@ -16,6 +16,8 @@ if (Number.isNaN(port)) {
16
16
  port = process.env.PORT as unknown as number;
17
17
  }
18
18
 
19
+ const serverID = process.env.SOCIAL_MAIL_SERVER_ID || "0";
20
+
19
21
  export const globalEnv = {
20
22
  protocol: void 0,
21
23
  strictSSL: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0",
@@ -48,7 +50,7 @@ export const globalEnv = {
48
50
  smtp2http: {
49
51
  publicKey: process.env.SOCIAL_MAIL_SMTP2HTTP_PUBLIC_KEY
50
52
  },
51
- serverID: process.env.SOCIAL_MAIL_SERVER_ID,
53
+ serverID,
52
54
  parentApiToken: process.env.SOCIAL_MAIL_PARENT_API_TOKEN,
53
55
  forceHttp1: false,
54
56
  isTestMode,
@@ -103,6 +105,7 @@ export const globalEnv = {
103
105
  },
104
106
  tmpDir: process.env.SOCIAL_MAIL_TMP_PATH || tmpdir(),
105
107
  tmpSize: process.env.SOCIAL_MAIL_TMP_SIZE || "7gb",
108
+ cacheDir: join((process.env.SOCIAL_MAIL_CACHE_PATH || "/cache"), serverID),
106
109
  serverPackage: process.env.npm_package_name,
107
110
  serverPackageVersion: process.env.npm_package_version,
108
111
  push: {
@@ -1,36 +1,46 @@
1
1
  export const ReactionExtractor = {
2
2
 
3
- async extract(email: {
3
+ extract(email: {
4
+ subject: string,
5
+ textBody: string,
6
+ headers: string | {[key: string]: any}
7
+ }, attachmentsLoader: () => Promise<{ contentType: string }[]>, forceFullMatch = true) {
8
+ return this.extractWithInReplyTo(email, attachmentsLoader, forceFullMatch).then(({ engagement } = { engagement: void 0 }) => engagement);
9
+ },
10
+
11
+ async extractWithInReplyTo(email: {
4
12
  subject: string,
5
13
  textBody: string,
6
14
  headers: string | {[key: string]: any}
7
15
  }, attachmentsLoader: () => Promise<{ contentType: string }[]>, forceFullMatch = true) {
8
16
  try {
17
+ let inReplyTo = null;
9
18
  let engagement: string;
10
19
  const index = email.subject.indexOf(":");
11
20
  if (index !== -1 && index <= 50) {
12
21
  engagement = email.subject.substring(0, index);
22
+ inReplyTo = email.subject.substring(index + 1);
13
23
  if (/\p{Emoji}/u.test(engagement)) {
14
- return engagement;
24
+ return { engagement, inReplyTo };
15
25
  }
16
26
  }
17
27
 
18
28
  let { textBody } = email;
19
29
  textBody = textBody?.trim();
20
30
  if (!textBody) {
21
- return;
31
+ return {};
22
32
  }
23
33
 
24
34
  const matches = /^\p{Emoji}/u.exec(textBody);
25
35
 
26
36
  if(!matches) {
27
- return;
37
+ return {};
28
38
  }
29
39
 
30
40
  engagement = matches[0];
31
41
 
32
42
  if (engagement === textBody) {
33
- return engagement;
43
+ return { engagement };
34
44
  }
35
45
 
36
46
  /**
@@ -51,26 +61,27 @@ export const ReactionExtractor = {
51
61
  : {};
52
62
  const jsonEngagement = jsonHeaders["x-engagement"] ?? jsonHeaders["x-reaction"];
53
63
  if (jsonEngagement) {
54
- return jsonEngagement;
64
+ return { engagement: jsonEngagement };
55
65
  }
56
66
 
57
67
  const attachments = await attachmentsLoader();
58
68
  if (attachments) {
59
69
  for (const iterator of attachments) {
60
70
  if (/reaction/i.test(iterator.contentType) && /json/i.test(iterator.contentType)) {
61
- return engagement;
71
+ return { engagement };
62
72
  }
63
73
  }
64
74
  }
65
75
 
66
76
  if (forceFullMatch) {
67
- return null;
77
+ return { engagement: null };
68
78
  }
69
- return engagement;
79
+ return { engagement };
70
80
  } catch (error) {
71
81
  // eslint-disable-next-line no-console
72
82
  console.error(error);
73
83
  }
84
+ return {};
74
85
  }
75
86
 
76
87
  };
@@ -164,6 +164,9 @@ export default class Mailbox {
164
164
  @Column({ dataType: "Boolean", default: () => false })
165
165
  public isPrivateChannel: boolean;
166
166
 
167
+ @Column({ dataType: "Boolean", default: () => false })
168
+ public membersCanPostTopLevel: boolean;
169
+
167
170
  @Column({ dataType: "Boolean", default: () => false })
168
171
  public nonMembersCanPost: boolean;
169
172
 
@@ -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.106";
6
+ const version = "1.4.107";
7
7
 
8
8
  const existing = await config.getValue(UIPackageConfig, false);
9
9
  if (existing) {
@@ -15,14 +15,9 @@ import sleep from "../../../common/sleep.js";
15
15
  import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
16
16
  import { parentPort } from "worker_threads";
17
17
  import { unlink } from "fs/promises";
18
- import { isTestMode } from "../../../globalServices.js";
19
- import { tmpdir} from "node:os";
20
18
 
21
- const cacheRoot = isTestMode
22
- ? join(globalEnv.tmpDir, "tc_" + Date.now().toString())
23
- : "/cache";
24
-
25
- const serverCache = join(cacheRoot, globalEnv.serverID ?? "0");
19
+ const cacheRoot = globalEnv.cacheDir;
20
+ const serverCache = globalEnv.cacheDir;
26
21
 
27
22
  ensureDir(serverCache);
28
23
 
@@ -44,7 +44,7 @@ export default class EmailLogService {
44
44
  await this.db.emailLogs.statements.delete({ logID });
45
45
  await tx.commit();
46
46
 
47
- await this.updateStorage({ emailID: log, size: -log.fileSize});
47
+ await this.updateStorage({ emailID: log.emailID, size: -log.fileSize});
48
48
  }
49
49
 
50
50
  async save(emailID: number, file: LocalFile, compression: string = "gz") {
@@ -15,6 +15,7 @@ import CachedEmailService from "./CachedEmailService.js";
15
15
  import QueueInvokeWebHookWorkflow from "../../workflows/web-hooks/QueueInvokeWebHookWorkflow.js";
16
16
  import EmailThreadService from "../../services/threads/EmailThreadService.js";
17
17
  import ChannelModerationService from "../../services/channels/ChannelModerationService.js";
18
+ import Mailbox from "../../model/entities/Mailbox.js";
18
19
 
19
20
  @RegisterScoped
20
21
  export default class EmailPostReceiveService {
@@ -40,14 +41,11 @@ export default class EmailPostReceiveService {
40
41
  @Inject
41
42
  ets?: EmailThreadService;
42
43
 
43
- @Inject
44
- cms?: ChannelModerationService;
45
-
46
44
  async executeAll({ senderID, emailID }) {
47
45
  const { db } = this;
48
46
  const mcs = await db.mailboxContactEmails.where({ emailID, senderID}, (p) => (x) => x.emailID === p.emailID
49
47
  && x.mailboxID !== p.senderID )
50
- .select(void 0, (p) => ({ mailboxID }) => ({ mailboxID }))
48
+ .select(void 0, () => ({ mailboxID }) => ({ mailboxID }))
51
49
  .toArray();
52
50
 
53
51
  for (const { mailboxID } of mcs) {
@@ -61,7 +59,17 @@ export default class EmailPostReceiveService {
61
59
  }
62
60
 
63
61
  async execute(input: { senderID, emailID, recipientID }) {
64
- await this.dispatchChannel(input);
62
+
63
+ const { eas } = this;
64
+
65
+ let channel = await eas.getHostedMailbox(input.recipientID);
66
+ channel = channel?.isChannel ? channel : void 0;
67
+
68
+ if (channel) {
69
+ await this.dispatchChannel(channel, input);
70
+ } else {
71
+ await this.dispatchMailbox(input);
72
+ }
65
73
  await this.queueWebHooks(input);
66
74
  }
67
75
 
@@ -100,9 +108,9 @@ export default class EmailPostReceiveService {
100
108
  }
101
109
  }
102
110
 
103
- async dispatchChannel(input: { senderID, emailID, recipientID }) {
111
+ async dispatchChannel(channel: Mailbox, input: { senderID, emailID, recipientID }) {
104
112
 
105
- const { db, eas, channelService, tfs, ces } = this;
113
+ const { db, channelService, tfs, ces } = this;
106
114
 
107
115
  const { senderID, emailID, recipientID } = input;
108
116
 
@@ -118,53 +126,45 @@ export default class EmailPostReceiveService {
118
126
  return;
119
127
  }
120
128
 
121
- let channel = await eas.getHostedMailbox(recipientID);
122
- channel = channel?.isChannel ? channel : void 0;
123
-
124
129
  if (ne.engagement) {
125
130
 
126
131
  await db.mailboxes.where(input, (p) => (x) => x.mailboxID === p.recipientID)
127
- .update(void 0, (p) => (x) => ({
132
+ .update(void 0, () => () => ({
128
133
  dateEngaged: Sql.date.now()
129
134
  }));
130
135
 
131
136
  switch(ne.engagement) {
132
137
  case "✅":
133
- if (channel) {
134
- return await channelService.approveSubscriber(emailID);
135
- }
136
- return;
138
+ return await channelService.approveSubscriber(emailID);
137
139
  case "🔔":
138
140
  case "📈":
139
141
  return;
140
142
  case "🔕":
141
143
  case "❌":
142
- // unsubscribe particular thread...
143
- await this.ets.unsubscribe(senderID, emailID);
144
- return;
144
+ // unsubscribe particular thread...
145
+ await this.ets.unsubscribe(senderID, emailID);
146
+ return;
145
147
  case "➕":
146
- if (channel) {
147
- await BroadcastEmail.broadcast(this.context, {
148
- mailboxID: recipientID,
149
- emailID,
150
- senderID,
151
- subscribe: true
152
- });
153
- }
148
+ await BroadcastEmail.broadcast(this.context, {
149
+ mailboxID: recipientID,
150
+ emailID,
151
+ senderID,
152
+ subscribe: true
153
+ });
154
154
  return;
155
155
  case "💬":
156
156
  // following will make comment appear publicly
157
- if (channel && !channel.isPrivateChannel && channel.requiresCommentModeration) {
157
+ if (!channel.isPrivateChannel && channel.requiresCommentModeration) {
158
158
  await db.emails.where(input, (p) => (x) => x.emailID === p.emailID
159
159
  && x.parent.status === "published"
160
160
  )
161
- .update(void 0, (p) => (x) => ({ status: "pending-approval" }));
161
+ .update(void 0, () => () => ({ status: "pending-approval" }));
162
162
  return;
163
163
  }
164
164
  await db.emails.where(input, (p) => (x) => x.emailID === p.emailID
165
165
  && x.parent.status === "published"
166
166
  )
167
- .update(void 0, (p) => (x) => ({ status: "published" }));
167
+ .update(void 0, () => () => ({ status: "published" }));
168
168
  await this.ets.saveParticipant(senderID, emailID);
169
169
  return;
170
170
  default:
@@ -178,33 +178,14 @@ export default class EmailPostReceiveService {
178
178
  && x.emailAddressID === p.senderID)
179
179
  .first();
180
180
 
181
- if(!channel) {
182
-
183
- // send push notification here...
184
- if (mc?.blocked) {
185
- return;
186
- }
181
+ if (!channel.isPrivateChannel && channel.requiresPostModeration && !mc.isOwner) {
187
182
 
188
- const mn = ServiceProvider.resolve(this, MailboxNamespace);
189
- mn.newMail(input.recipientID, input.senderID, input.emailID);
190
-
191
- // verify if anyone is listening...
192
- const hasSubscriptions = await db.userPushSubscriptions.where(input, (p) => (x) => x.mailboxID === p.recipientID).some();
193
- if (hasSubscriptions) {
194
- const id = `send-push-${input.emailID}-${input.recipientID}`;
195
- await this.context.queue(SendWebPushNotification, {
196
- mailboxID: ne.mailboxID,
197
- emailID: ne.emailID,
198
- emailAddressID: ne.emailAddressID
199
- }, { id, throwIfExists: false });
200
- }
201
- return;
202
- }
183
+ await this.db.emails.statements.update({
184
+ status: "pending-approval"
185
+ }, {
186
+ emailID
187
+ });
203
188
 
204
- if (!channel.isPrivateChannel && channel.requiresPostModeration) {
205
- if(await this.cms.addToQueue(channel, emailID, mc)) {
206
- return;
207
- }
208
189
  }
209
190
 
210
191
  // can send...
@@ -261,4 +242,59 @@ export default class EmailPostReceiveService {
261
242
  });
262
243
  }
263
244
 
245
+ async dispatchMailbox(input: { senderID, emailID, recipientID }) {
246
+
247
+ const { db } = this;
248
+
249
+ const { senderID, emailID } = input;
250
+
251
+ // check if it is not an engagement and can send an email
252
+ const ne = await db.mailboxContactEmails.where(input, (p) =>
253
+ (x) => x.emailID === p.emailID
254
+ && x.mailboxID === p.recipientID
255
+ && x.emailAddressID === p.senderID
256
+ && x.email.status !== "published")
257
+ .first();
258
+
259
+ if (!ne) {
260
+ return;
261
+ }
262
+
263
+ await this.ets.saveParticipant(senderID, emailID);
264
+
265
+ if (ne.engagement) {
266
+
267
+ await db.mailboxes.where(input, (p) => (x) => x.mailboxID === p.recipientID)
268
+ .update(void 0, () => () => ({
269
+ dateEngaged: Sql.date.now()
270
+ }));
271
+ }
272
+
273
+ const mc = await db.mailboxContacts.where(input, (p) =>
274
+ (x) => x.mailboxID === p.recipientID
275
+ && x.emailAddressID === p.senderID)
276
+ .first();
277
+
278
+
279
+ // send push notification here...
280
+ if (mc?.blocked) {
281
+ return;
282
+ }
283
+
284
+ const mn = ServiceProvider.resolve(this, MailboxNamespace);
285
+ mn.newMail(input.recipientID, input.senderID, input.emailID);
286
+
287
+ // verify if anyone is listening...
288
+ const hasSubscriptions = await db.userPushSubscriptions.where(input, (p) => (x) => x.mailboxID === p.recipientID).some();
289
+ if (hasSubscriptions) {
290
+ const id = `send-push-${input.emailID}-${input.recipientID}`;
291
+ await this.context.queue(SendWebPushNotification, {
292
+ mailboxID: ne.mailboxID,
293
+ emailID: ne.emailID,
294
+ emailAddressID: ne.emailAddressID
295
+ }, { id, throwIfExists: false });
296
+ }
297
+
298
+ }
299
+
264
300
  }
@@ -25,6 +25,8 @@ import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
25
25
  import EmailRecipient from "../../model/entities/EmailRecipient.js";
26
26
  import { ReactionExtractor } from "../../../protocols/core/ReactionExtractor.js";
27
27
  import { stripBrackets } from "../../../common/stripBrackets.js";
28
+ import SmtpError from "./SmtpError.js";
29
+ import { SmtpStatusCodes } from "../client/SmtpStatusCodes.js";
28
30
  // import PostReceiveWorkflow from "../../workflows/post-receive/PostReceiveWorkflow.js";
29
31
  // import UpdateEmailReferences from "../../workflows/incoming/UpdateEmailReferences.js";
30
32
 
@@ -349,16 +351,6 @@ export default class EmailSerializerService {
349
351
 
350
352
  // console.log("Recipients Saved");
351
353
 
352
- if (inReplyTo) {
353
- // find the id..
354
- const original = await db.emails.where({ inReplyTo }, (p) => (x) => x.headerMessageID === p.inReplyTo)
355
- .orderBy(void 0,(p) => (x) => x.emailID)
356
- .first();
357
- if (original) {
358
- parentID = original.emailID;
359
- inReplyTo = null;
360
- }
361
- }
362
354
 
363
355
  const dateReceived = DateTime.from(parsedMail.date ?? DateTime.now);
364
356
  const dateSent = dateReceived;
@@ -376,11 +368,27 @@ export default class EmailSerializerService {
376
368
 
377
369
  if (received) {
378
370
  // check for engagements...
379
- engagement = await ReactionExtractor.extract({
371
+ const e1 = await ReactionExtractor.extractWithInReplyTo({
380
372
  subject,
381
373
  textBody,
382
374
  headers: mailHeaders
383
375
  }, () => Promise.resolve(parsedMail.attachments), true);
376
+
377
+ engagement = e1.engagement;
378
+ if (!inReplyTo) {
379
+ inReplyTo = e1.inReplyTo;
380
+ }
381
+ }
382
+
383
+ if (inReplyTo) {
384
+ // find the id..
385
+ const original = await db.emails.where({ inReplyTo }, (p) => (x) => x.headerMessageID === p.inReplyTo)
386
+ .orderBy(void 0,(p) => (x) => x.emailID)
387
+ .first();
388
+ if (original) {
389
+ parentID = original.emailID;
390
+ inReplyTo = null;
391
+ }
384
392
  }
385
393
 
386
394
  let emailID = void 0;
@@ -395,6 +403,21 @@ export default class EmailSerializerService {
395
403
  // add recipient from mailboxes
396
404
  if (mailboxes) {
397
405
  for (const mailbox of mailboxes) {
406
+
407
+ // check if it is a channel
408
+ // verify
409
+
410
+ if (mailbox.isChannel && !mailbox.membersCanPostTopLevel) {
411
+
412
+ if (!parentID) {
413
+ const me = await this.db.mailboxContacts.statements.select({}, { mailboxID: mailbox.mailboxID, emailAddressID: senderID});
414
+ if (!(me.canContribute || me.isOwner)) {
415
+ throw new SmtpError("Only active members of channel can create new posts to channel", SmtpStatusCodes.DeliveryNotPermitted);
416
+ }
417
+ }
418
+
419
+ }
420
+
398
421
  if(recipients.some((e) => e.emailAddressID === mailbox.mailboxID)) {
399
422
  continue;
400
423
  }
@@ -5,6 +5,8 @@ import SocialMailContext from "../../model/SocialMailContext.js";
5
5
  import EmailReferenceService from "../../smtp/services/EmailReferenceService.js";
6
6
  import WorkflowContext from "@entity-access/entity-access/dist/workflows/WorkflowContext.js";
7
7
  import { globalEnv } from "../../../common/globalEnv.js";
8
+ import PendingPostReceiveWorkflow from "./PendingPostReceiveWorkflow.js";
9
+ import { randomUUID } from "node:crypto";
8
10
 
9
11
  export default class PendingReferencesWorkflow extends Workflow {
10
12
 
@@ -19,6 +21,8 @@ export default class PendingReferencesWorkflow extends Workflow {
19
21
  do {
20
22
  start = await this.processPendingReferences(start);
21
23
  } while(BigInt(start));
24
+
25
+ await this.context.queue(PendingPostReceiveWorkflow, "run", { id: `post-receive-${randomUUID()}`, throttle: { group: "post-receive", maxPerSecond: 1 } } );
22
26
  }
23
27
 
24
28
  @UniqueActivity
package/test.js CHANGED
@@ -5,6 +5,13 @@ import { tmpdir } from "os";
5
5
  import { existsSync, mkdirSync } from "fs";
6
6
  import { spawnPromise } from "./dist/common/spawnPromise.js";
7
7
 
8
+ const ensureDir = (d) => {
9
+ if(!existsSync(d)) {
10
+ mkdirSync(d, { recursive: true });
11
+ }
12
+ return d;
13
+ }
14
+
8
15
  const host = process.env.POSTGRES_HOST ?? "localhost";
9
16
  const postGresPort = Number(process.env.POSTGRES_PORT ?? 5432);
10
17
  process.env.SOCIAL_MAIL_SECRET_KEY = "abcd123";
@@ -35,13 +42,10 @@ let start = Date.now();
35
42
  process.env.SOCIAL_MAIL_DB_SSL = "false";
36
43
  process.env.SOCIAL_MAIL_TEST = "true";
37
44
  process.env.SOCIAL_MAIL_AV = "none";
38
- const tmpPath = process.env.SOCIAL_MAIL_TMP_PATH = `${tmpdir()}/t/${Date.now().toString(36)}`;
45
+ const tmpPath = process.env.SOCIAL_MAIL_TMP_PATH = ensureDir(`${tmpdir()}/t-${Date.now().toString(36)}/t`);
46
+ process.env.SOCIAL_MAIL_CACHE_PATH = ensureDir(`${tmpdir()}/tc-${Date.now().toString(36)}/tc`);
39
47
  process.env.SOCIAL_MAIL_DB_DATABASE = "SM" + start;
40
48
 
41
- if(!existsSync(tmpPath)) {
42
- mkdirSync(tmpPath, { recursive: true });
43
- }
44
-
45
49
  export default class TestRunner {
46
50
 
47
51
  static get drivers() {
@@ -66,10 +70,8 @@ export default class TestRunner {
66
70
  if (!testFile) {
67
71
  const dbID = start++;
68
72
  let db = `social-mail-test-${dbID}`;
69
- const tPath = `${tmpdir()}/t/${dbID}`;
70
- if(!existsSync(tPath)) {
71
- mkdirSync(tPath, { recursive: true });
72
- }
73
+ const tPath = ensureDir(`${tmpdir()}/t-${dbID}/t`);
74
+ const tcPath = ensureDir(`${tmpdir()}/t-${dbID}/tc`);
73
75
 
74
76
  const t1 = setTimeout(() => console.warn(`Test ${name} ran for more than 30 seconds`), 30000);
75
77
  try {
@@ -83,6 +85,7 @@ export default class TestRunner {
83
85
  SOCIAL_MAIL_TEST: "true",
84
86
  SOCIAL_MAIL_AV: "none",
85
87
  SOCIAL_MAIL_TMP_PATH: tPath,
88
+ SOCIAL_MAIL_CACHE_PATH: tcPath,
86
89
  SOCIAL_MAIL_DB_DATABASE: db,
87
90
  SOCIAL_MAIL_DB_USER: "postgres",
88
91
  SOCIAL_MAIL_DB_PASSWORD: "abcd123",