@stacksjs/email 0.58.59 → 0.58.61
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/package.json +2 -1
- package/src/server/converter.ts +19 -19
- package/src/server/inbound.ts +24 -8
- package/src/server/outbound.ts +17 -17
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/email",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.58.
|
|
4
|
+
"version": "0.58.61",
|
|
5
5
|
"description": "The Stacks Email integration. Painlessly create & manage your inboxes, templates, and send emails.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"license": "MIT",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"@stacksjs/error-handling": "latest",
|
|
72
72
|
"@stacksjs/types": "latest",
|
|
73
73
|
"@types/mailparser": "^3.4.4",
|
|
74
|
+
"aws-sdk": "^2.1555.0",
|
|
74
75
|
"mailparser": "^3.6.7",
|
|
75
76
|
"mime": "^4.0.1",
|
|
76
77
|
"moment": "^2.30.1",
|
package/src/server/converter.ts
CHANGED
|
@@ -12,7 +12,7 @@ const s3 = new AWS.S3({
|
|
|
12
12
|
|
|
13
13
|
// This lambda will read RAW emails and convert them in easy to read formats
|
|
14
14
|
// like: HTML and text
|
|
15
|
-
exports.handler = (event) => {
|
|
15
|
+
exports.handler = (event: any) => {
|
|
16
16
|
// 1. We need to process the path received by S3 since AWS dose escape the
|
|
17
17
|
// string in a special way. They escape the string in a HTML style
|
|
18
18
|
// but for whatever reason they convert spaces in to +ses.
|
|
@@ -57,7 +57,7 @@ exports.handler = (event) => {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Load the email that triggered the function from S3
|
|
60
|
-
function load_the_email(container) {
|
|
60
|
+
function load_the_email(container: any) {
|
|
61
61
|
return new Promise((resolve, reject) => {
|
|
62
62
|
console.info('load_the_email')
|
|
63
63
|
|
|
@@ -68,7 +68,7 @@ function load_the_email(container) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// -> Execute the query
|
|
71
|
-
s3.getObject(params, (error, data) => {
|
|
71
|
+
s3.getObject(params, (error: any, data: any) => {
|
|
72
72
|
// 1. Check for internal errors
|
|
73
73
|
if (error)
|
|
74
74
|
return reject(error)
|
|
@@ -84,7 +84,7 @@ function load_the_email(container) {
|
|
|
84
84
|
|
|
85
85
|
// Since the raw email is saved with an extension we need to remove it
|
|
86
86
|
// before we can save other converted versions.
|
|
87
|
-
function remove_extension(container) {
|
|
87
|
+
function remove_extension(container: any) {
|
|
88
88
|
return new Promise((resolve, reject) => {
|
|
89
89
|
console.info('remove_extension')
|
|
90
90
|
|
|
@@ -105,11 +105,11 @@ function remove_extension(container) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// Convert the raw email in to HTML and Text, and extract also the attachments
|
|
108
|
-
function parse_the_email(container) {
|
|
108
|
+
function parse_the_email(container: any) {
|
|
109
109
|
return new Promise((resolve, reject) => {
|
|
110
|
-
// 1. Parse the email and extract all the
|
|
111
|
-
parser(container.raw_email, (error, parsed) => {
|
|
112
|
-
// 1. Check for internal errors
|
|
110
|
+
// 1. Parse the email and extract all the necessary
|
|
111
|
+
parser(container.raw_email, (error: any, parsed: any) => {
|
|
112
|
+
// 1. Check for internal errors
|
|
113
113
|
if (error)
|
|
114
114
|
return reject(error)
|
|
115
115
|
|
|
@@ -125,7 +125,7 @@ function parse_the_email(container) {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// After the conversion we save the Text version to S3.
|
|
128
|
-
function save_text(container) {
|
|
128
|
+
function save_text(container: any) {
|
|
129
129
|
return new Promise((resolve, reject) => {
|
|
130
130
|
console.info('save_text')
|
|
131
131
|
|
|
@@ -137,7 +137,7 @@ function save_text(container) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// -> Execute the query.
|
|
140
|
-
s3.putObject(params, (error, data) => {
|
|
140
|
+
s3.putObject(params, (error: any, data: any) => {
|
|
141
141
|
// 1. Check for internal errors.
|
|
142
142
|
if (error)
|
|
143
143
|
return reject(error)
|
|
@@ -149,10 +149,10 @@ function save_text(container) {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// Then if we have a HTML version we try to save that.
|
|
152
|
-
function save_html(container) {
|
|
152
|
+
function save_html(container: any) {
|
|
153
153
|
return new Promise((resolve, reject) => {
|
|
154
154
|
// <>> When the body of an email have only one version, meaning it
|
|
155
|
-
//
|
|
155
|
+
// does have only the pure text version and no HTML.
|
|
156
156
|
// Nodemailer won't generate the HTML for you, it just grabs
|
|
157
157
|
// what is in the Email body.
|
|
158
158
|
// So, this value will be false, when there is no HTML content in
|
|
@@ -174,7 +174,7 @@ function save_html(container) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// -> Execute the query.
|
|
177
|
-
s3.putObject(params, (error, data) => {
|
|
177
|
+
s3.putObject(params, (error: any, data: any) => {
|
|
178
178
|
// 1. Check for internal errors.
|
|
179
179
|
if (error)
|
|
180
180
|
return reject(error)
|
|
@@ -187,12 +187,12 @@ function save_html(container) {
|
|
|
187
187
|
|
|
188
188
|
// Last thing to do is to save the attachments if there are any. If the
|
|
189
189
|
// array is empty the loop will skip itself.
|
|
190
|
-
function save_attachments(container) {
|
|
190
|
+
function save_attachments(container: any) {
|
|
191
191
|
return new Promise((resolve, reject) => {
|
|
192
192
|
console.info('save_attachments')
|
|
193
193
|
|
|
194
194
|
// Start the loop which save all the attachments.
|
|
195
|
-
loop(1, (error) => {
|
|
195
|
+
loop(1, (error: any) => {
|
|
196
196
|
// <<> Check if there was an error.
|
|
197
197
|
if (error) {
|
|
198
198
|
// -> Stop everything and surface the error.
|
|
@@ -204,7 +204,7 @@ function save_attachments(container) {
|
|
|
204
204
|
})
|
|
205
205
|
|
|
206
206
|
// This loop will upload all the individual attachments found in a email
|
|
207
|
-
function loop(count, callback) {
|
|
207
|
+
function loop(count: any, callback: any) {
|
|
208
208
|
// 1. Pop the last element in the array if any.
|
|
209
209
|
const file = container.parsed.attachments.pop()
|
|
210
210
|
|
|
@@ -213,7 +213,7 @@ function save_attachments(container) {
|
|
|
213
213
|
return callback()
|
|
214
214
|
|
|
215
215
|
// 3. Get the file name which also contain the file extension.
|
|
216
|
-
file_name = file.filename
|
|
216
|
+
let file_name = file.filename
|
|
217
217
|
|
|
218
218
|
// 4. An email attachment is not required to have a name, this
|
|
219
219
|
// mean we need to check if we have a file name and, if not
|
|
@@ -258,7 +258,7 @@ function save_attachments(container) {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
// 8. Then save the buffer of the attachment.
|
|
261
|
-
file_body = file.content
|
|
261
|
+
const file_body = file.content
|
|
262
262
|
|
|
263
263
|
// 9. Split the S3 Key (path) so we can remove the last element. Since
|
|
264
264
|
// we don't want the object name, we care only about the path.
|
|
@@ -292,7 +292,7 @@ function save_attachments(container) {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
// -> Execute the query.
|
|
295
|
-
s3.putObject(params, (error, data) => {
|
|
295
|
+
s3.putObject(params, (error: any, data: any) => {
|
|
296
296
|
// 1. Check for internal errors.
|
|
297
297
|
if (error)
|
|
298
298
|
return callback(error)
|
package/src/server/inbound.ts
CHANGED
|
@@ -55,7 +55,7 @@ interface S3Event {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// This Lambda will filter all the incoming emails based on their From and To field
|
|
58
|
-
export function handler(event: S3Event): Promise<
|
|
58
|
+
export function handler(event: S3Event): Promise<any> {
|
|
59
59
|
// if (!event.Records.length)
|
|
60
60
|
// throw new Error('No records in the event')
|
|
61
61
|
|
|
@@ -96,6 +96,8 @@ export function handler(event: S3Event): Promise<boolean> {
|
|
|
96
96
|
|
|
97
97
|
return false
|
|
98
98
|
})
|
|
99
|
+
|
|
100
|
+
return Promise.resolve()
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
// List all the emails added to SES, so we can use them to decided where to
|
|
@@ -202,7 +204,8 @@ function parse_the_email(container: Container): Promise<Container> {
|
|
|
202
204
|
// Save the parsed email for the next promise.
|
|
203
205
|
container.date = data.date
|
|
204
206
|
container.from = data.from?.value[0]?.address
|
|
205
|
-
container.to = data.to?.value[0]?.address
|
|
207
|
+
// container.to = data.to?.value[0]?.address
|
|
208
|
+
container.to = data.from?.value[0]?.address
|
|
206
209
|
container.subject = data.subject || 'No Subject'
|
|
207
210
|
container.message_id = data.messageId
|
|
208
211
|
|
|
@@ -236,13 +239,26 @@ function extract_data(container: Container): Promise<Container> {
|
|
|
236
239
|
// Since the email string can come in a form of:
|
|
237
240
|
// Name Last <name@example.com>
|
|
238
241
|
// We have to extract just the email address, and discard the rest
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.
|
|
242
|
+
let tmp_to
|
|
243
|
+
if (container?.to) {
|
|
244
|
+
const matchResult = container.to.match(/(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21-\x5A\x53-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])+)\])/gm);
|
|
245
|
+
if (matchResult) {
|
|
246
|
+
tmp_to = matchResult[0].split('@');
|
|
247
|
+
// Continue with your logic using tmp_to
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let tmp_from
|
|
252
|
+
if (container?.from) {
|
|
253
|
+
const matchResult = container.from.match(/(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21-\x5A\x53-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])+)\])/gm)
|
|
254
|
+
if (matchResult) {
|
|
255
|
+
tmp_from = matchResult[0].split('@');
|
|
256
|
+
// Continue with your logic using tmp_to
|
|
257
|
+
}
|
|
258
|
+
}
|
|
242
259
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
.split('@')
|
|
260
|
+
if (!tmp_to || !tmp_from)
|
|
261
|
+
return reject(Error('No To or From field in the email'))
|
|
246
262
|
|
|
247
263
|
// Get the domain name of the receiving end, so we can group
|
|
248
264
|
// emails by all the domain that were added to SES.
|
package/src/server/outbound.ts
CHANGED
|
@@ -18,7 +18,7 @@ const ses = new AWS.SES({
|
|
|
18
18
|
|
|
19
19
|
// This lambda will read outgoing emails formatted in JSON, convert them
|
|
20
20
|
// in to a raw email, send them using SES, and store them in S3.
|
|
21
|
-
exports.handler = (event) => {
|
|
21
|
+
exports.handler = (event: any) => {
|
|
22
22
|
// This JS object will contain all the data within the chain
|
|
23
23
|
const container = {
|
|
24
24
|
bucket: event.Records[0].s3.bucket.name,
|
|
@@ -56,7 +56,7 @@ exports.handler = (event) => {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Load the JSON email that triggered this Lambda.
|
|
59
|
-
function load_the_email(container) {
|
|
59
|
+
function load_the_email(container: any) {
|
|
60
60
|
return new Promise((resolve, reject) => {
|
|
61
61
|
console.info('load_the_email')
|
|
62
62
|
|
|
@@ -67,7 +67,7 @@ function load_the_email(container) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// -> Execute the query.
|
|
70
|
-
s3.getObject(params, (error, data) => {
|
|
70
|
+
s3.getObject(params, (error: any, data: any) => {
|
|
71
71
|
// 1. Check for internal errors.
|
|
72
72
|
if (error)
|
|
73
73
|
return reject(error)
|
|
@@ -82,7 +82,7 @@ function load_the_email(container) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Extract all the data necessary to organize the incoming emails.
|
|
85
|
-
function extract_data(container) {
|
|
85
|
+
function extract_data(container: any) {
|
|
86
86
|
return new Promise((resolve, reject) => {
|
|
87
87
|
console.info('extract_data')
|
|
88
88
|
|
|
@@ -116,7 +116,7 @@ function extract_data(container) {
|
|
|
116
116
|
|
|
117
117
|
// 6. Create a human readable time stamp that matches the format
|
|
118
118
|
// S3 Provides in its Event.
|
|
119
|
-
date = moment().format('ddd, DD MMM YYYY HH:MM:SS ZZ')
|
|
119
|
+
const date = moment().format('ddd, DD MMM YYYY HH:MM:SS ZZ')
|
|
120
120
|
|
|
121
121
|
// 7. Create the path where the email needs to be moved
|
|
122
122
|
// so it is properly organized.
|
|
@@ -145,7 +145,7 @@ function extract_data(container) {
|
|
|
145
145
|
|
|
146
146
|
// From the JSON file that we loaded in to memory we generate a RAW version that
|
|
147
147
|
// can be used by SES, and later on stored and converted in multiple formats
|
|
148
|
-
function generate_the_raw_email(container
|
|
148
|
+
function generate_the_raw_email(container: any) {
|
|
149
149
|
return new Promise((resolve, reject) => {
|
|
150
150
|
console.info('generate_the_raw_email')
|
|
151
151
|
|
|
@@ -154,7 +154,7 @@ function generate_the_raw_email(container, callback) {
|
|
|
154
154
|
const mail = new MailComposer(container.email.json)
|
|
155
155
|
|
|
156
156
|
// 2. Take the email and compile it down to its text form for storage.
|
|
157
|
-
mail.compile().build((error, raw_message) => {
|
|
157
|
+
mail.compile().build((error: any, raw_message: any) => {
|
|
158
158
|
// 1. Check if there was an error
|
|
159
159
|
if (error)
|
|
160
160
|
return reject(error)
|
|
@@ -169,7 +169,7 @@ function generate_the_raw_email(container, callback) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// Use the newly generated RAW email and send it using SES.
|
|
172
|
-
function send_email(container) {
|
|
172
|
+
function send_email(container: any) {
|
|
173
173
|
return new Promise((resolve, reject) => {
|
|
174
174
|
console.info('send_email')
|
|
175
175
|
|
|
@@ -181,7 +181,7 @@ function send_email(container) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
// -> Send the email out
|
|
184
|
-
ses.sendRawEmail(params, (error, data) => {
|
|
184
|
+
ses.sendRawEmail(params, (error: any, data: any) => {
|
|
185
185
|
// 1. Check if there was an error
|
|
186
186
|
if (error)
|
|
187
187
|
return reject(error)
|
|
@@ -202,7 +202,7 @@ function send_email(container) {
|
|
|
202
202
|
// and then we copy the object to the final destination, which triggers
|
|
203
203
|
// the Convert action, and once the convert action makes a PUT nothing else
|
|
204
204
|
// happens.
|
|
205
|
-
function save_raw_email(container) {
|
|
205
|
+
function save_raw_email(container: any) {
|
|
206
206
|
return new Promise((resolve, reject) => {
|
|
207
207
|
console.info('save_raw_email')
|
|
208
208
|
|
|
@@ -214,7 +214,7 @@ function save_raw_email(container) {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
// -> Execute the query.
|
|
217
|
-
s3.putObject(params, (error, data) => {
|
|
217
|
+
s3.putObject(params, (error: any, data: any) => {
|
|
218
218
|
// 1. Check for internal errors.
|
|
219
219
|
if (error)
|
|
220
220
|
return reject(error)
|
|
@@ -226,7 +226,7 @@ function save_raw_email(container) {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
// Now that we have our RAW email saved we do a copy event so it will trigger the Conversion Lambda
|
|
229
|
-
function copy_raw_email(container) {
|
|
229
|
+
function copy_raw_email(container: any) {
|
|
230
230
|
return new Promise((resolve, reject) => {
|
|
231
231
|
console.info('copy_raw_email')
|
|
232
232
|
|
|
@@ -238,7 +238,7 @@ function copy_raw_email(container) {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
// -> Execute the query.
|
|
241
|
-
s3.copyObject(params, (error, data) => {
|
|
241
|
+
s3.copyObject(params, (error: any, data: any) => {
|
|
242
242
|
// 1. Check for internal errors.
|
|
243
243
|
if (error)
|
|
244
244
|
return reject(error)
|
|
@@ -250,7 +250,7 @@ function copy_raw_email(container) {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// And before we finish we clean up the environment by deleting the JSON email,
|
|
253
|
-
function delete_json_email(container) {
|
|
253
|
+
function delete_json_email(container: any) {
|
|
254
254
|
return new Promise((resolve, reject) => {
|
|
255
255
|
console.info('delete_json_email')
|
|
256
256
|
|
|
@@ -261,7 +261,7 @@ function delete_json_email(container) {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// -> Execute the query.
|
|
264
|
-
s3.deleteObject(params, (error, data) => {
|
|
264
|
+
s3.deleteObject(params, (error: any, data: any) => {
|
|
265
265
|
// 1. Check for internal errors.
|
|
266
266
|
if (error)
|
|
267
267
|
return reject(error)
|
|
@@ -273,7 +273,7 @@ function delete_json_email(container) {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
// And the temporary RAW email.
|
|
276
|
-
function delete_raw_email(container) {
|
|
276
|
+
function delete_raw_email(container: any) {
|
|
277
277
|
return new Promise((resolve, reject) => {
|
|
278
278
|
console.info('delete_raw_email')
|
|
279
279
|
|
|
@@ -284,7 +284,7 @@ function delete_raw_email(container) {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// -> Execute the query.
|
|
287
|
-
s3.deleteObject(params, (error, data) => {
|
|
287
|
+
s3.deleteObject(params, (error: any, data: any) => {
|
|
288
288
|
// 1. Check for internal errors.
|
|
289
289
|
if (error)
|
|
290
290
|
return reject(error)
|