@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.
- package/dist/common/globalEnv.d.ts +1 -0
- package/dist/common/globalEnv.d.ts.map +1 -1
- package/dist/common/globalEnv.js +3 -1
- package/dist/common/globalEnv.js.map +1 -1
- package/dist/protocols/core/ReactionExtractor.d.ts +19 -1
- package/dist/protocols/core/ReactionExtractor.d.ts.map +1 -1
- package/dist/protocols/core/ReactionExtractor.js +15 -9
- package/dist/protocols/core/ReactionExtractor.js.map +1 -1
- package/dist/server/model/entities/Mailbox.d.ts +1 -0
- package/dist/server/model/entities/Mailbox.d.ts.map +1 -1
- package/dist/server/model/entities/Mailbox.js +4 -0
- package/dist/server/model/entities/Mailbox.js.map +1 -1
- package/dist/server/seed/ui/seed-ui.js +1 -1
- package/dist/server/services/cache/DiskCacheService.d.ts.map +1 -1
- package/dist/server/services/cache/DiskCacheService.js +2 -5
- package/dist/server/services/cache/DiskCacheService.js.map +1 -1
- package/dist/server/services/email-logs/EmailLogService.js +1 -1
- package/dist/server/services/email-logs/EmailLogService.js.map +1 -1
- package/dist/server/smtp/services/EmailPostReceiveService.d.ts +7 -3
- package/dist/server/smtp/services/EmailPostReceiveService.d.ts.map +1 -1
- package/dist/server/smtp/services/EmailPostReceiveService.js +68 -50
- package/dist/server/smtp/services/EmailPostReceiveService.js.map +1 -1
- package/dist/server/smtp/services/EmailSerializerService.d.ts.map +1 -1
- package/dist/server/smtp/services/EmailSerializerService.js +27 -11
- package/dist/server/smtp/services/EmailSerializerService.js.map +1 -1
- package/dist/server/workflows/pending/PendingReferencesWorkflow.d.ts.map +1 -1
- package/dist/server/workflows/pending/PendingReferencesWorkflow.js +3 -0
- package/dist/server/workflows/pending/PendingReferencesWorkflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/common/globalEnv.ts +4 -1
- package/src/protocols/core/ReactionExtractor.ts +20 -9
- package/src/server/model/entities/Mailbox.ts +3 -0
- package/src/server/seed/ui/seed-ui.ts +1 -1
- package/src/server/services/cache/DiskCacheService.ts +2 -7
- package/src/server/services/email-logs/EmailLogService.ts +1 -1
- package/src/server/smtp/services/EmailPostReceiveService.ts +90 -54
- package/src/server/smtp/services/EmailSerializerService.ts +34 -11
- package/src/server/workflows/pending/PendingReferencesWorkflow.ts +4 -0
- 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.
|
|
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.
|
|
25
|
-
"@entity-access/server-pages": "^1.1.
|
|
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",
|
package/src/common/globalEnv.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
|
@@ -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 =
|
|
22
|
-
|
|
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, (
|
|
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
|
-
|
|
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,
|
|
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, (
|
|
132
|
+
.update(void 0, () => () => ({
|
|
128
133
|
dateEngaged: Sql.date.now()
|
|
129
134
|
}));
|
|
130
135
|
|
|
131
136
|
switch(ne.engagement) {
|
|
132
137
|
case "✅":
|
|
133
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
// unsubscribe particular thread...
|
|
145
|
+
await this.ets.unsubscribe(senderID, emailID);
|
|
146
|
+
return;
|
|
145
147
|
case "➕":
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 (
|
|
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, (
|
|
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, (
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
70
|
-
|
|
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",
|