ac-ses 1.2.4 → 2.0.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.
- package/.eslintrc.js +1 -1
- package/.ncurc.js +5 -0
- package/CHANGELOG.md +22 -0
- package/README.md +21 -32
- package/index.js +78 -189
- package/package.json +5 -7
- package/test/test.js +22 -108
package/.eslintrc.js
CHANGED
package/.ncurc.js
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
<a name="2.0.0"></a>
|
|
2
|
+
|
|
3
|
+
# [2.0.0](https://github.com/admiralcloud/ac-ses/compare/v1.2.4..v2.0.0) (2024-01-06 09:43:38)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fix
|
|
7
|
+
|
|
8
|
+
* **App:** Use async/await and remove some functions | MP | [7f9c4aee85e062dfc08fed09c1f4bf08d37e3673](https://github.com/admiralcloud/ac-ses/commit/7f9c4aee85e062dfc08fed09c1f4bf08d37e3673)
|
|
9
|
+
Package now uses async/await. No more support for blocktime and group messages - use your application logic for that.
|
|
10
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
11
|
+
### Chores
|
|
12
|
+
|
|
13
|
+
* **App:** Some minor updates | MP | [6cdc1d8f9927d5926836b1128e3f84b8ebb1c0f0](https://github.com/admiralcloud/ac-ses/commit/6cdc1d8f9927d5926836b1128e3f84b8ebb1c0f0)
|
|
14
|
+
Some minor updates
|
|
15
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
16
|
+
### Chores
|
|
17
|
+
|
|
18
|
+
* **App:** Updated packages | MP | [21369161d02172ccba704d286dca59b5e0dbbde5](https://github.com/admiralcloud/ac-ses/commit/21369161d02172ccba704d286dca59b5e0dbbde5)
|
|
19
|
+
Updated packages
|
|
20
|
+
Related issues: [undefined/undefined#master](undefined/browse/master)
|
|
21
|
+
## BREAKING CHANGES
|
|
22
|
+
* **App:** See README breaking changes for version 2
|
|
1
23
|
<a name="1.2.4"></a>
|
|
2
24
|
|
|
3
25
|
## [1.2.4](https://github.com/admiralcloud/ac-ses/compare/v1.2.3..v1.2.4) (2023-01-31 11:26:12)
|
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# AC SES
|
|
2
2
|
A helper tool to send emails via AWS SES.
|
|
3
|
-
It also support "convenience" calls, to inform groups (e.g. support with informSupport function)
|
|
4
3
|
|
|
5
|
-
##
|
|
4
|
+
## BREAKING CHANGES VERSION 2
|
|
5
|
+
+ use async/await
|
|
6
|
+
+ no more support for blocktime - use your application logic instead
|
|
7
|
+
+ no more support for group messages - use your application logid instead
|
|
6
8
|
|
|
9
|
+
## Usage
|
|
7
10
|
```
|
|
8
11
|
const acses = require('ac-ses')
|
|
9
12
|
|
|
10
13
|
// Min requirements
|
|
11
14
|
acses.init({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
secretAccessKey: 'xxx',
|
|
15
|
-
region: 'eu-central-1'
|
|
15
|
+
defaultSender: {
|
|
16
|
+
address: 'sender@domain.com
|
|
16
17
|
}
|
|
17
18
|
})
|
|
18
19
|
|
|
@@ -34,47 +35,35 @@ let email = {
|
|
|
34
35
|
html: 'This is my <b>message</b>' // optional
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
acses.sendEmail(email
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
await acses.sendEmail(email)
|
|
39
|
+
// -> Response
|
|
40
|
+
{
|
|
41
|
+
'$metadata': {
|
|
42
|
+
httpStatusCode: 200,
|
|
43
|
+
requestId: '123466-557d-4a75-8d5c-d71e336963ec',
|
|
44
|
+
extendedRequestId: undefined,
|
|
45
|
+
cfId: undefined,
|
|
46
|
+
attempts: 1,
|
|
47
|
+
totalRetryDelay: 0
|
|
48
|
+
},
|
|
49
|
+
MessageId: '12356-2c4f41dd-6f2b-402e-9c26-123355-000000'
|
|
50
|
+
}
|
|
42
51
|
```
|
|
43
52
|
|
|
44
53
|
Full setup
|
|
45
|
-
|
|
46
54
|
```
|
|
47
55
|
acses.init({
|
|
48
|
-
aws: {
|
|
49
|
-
accessKeyId: 'xxx',
|
|
50
|
-
secretAccessKey: 'xxx',
|
|
51
|
-
region: 'eu-central-1'
|
|
52
|
-
},
|
|
53
|
-
redis: REDISINSTANCE,
|
|
54
|
-
defaultBlockTime: BLOCKTIME FOR SAME MESSAGE,
|
|
55
56
|
defaultSender: {
|
|
56
57
|
address: 'defaultSender@admiralcloud.com',
|
|
57
58
|
name: 'AdmiralCloud Sender'
|
|
58
59
|
},
|
|
59
|
-
securityRecipient: {
|
|
60
|
-
address: 'defaultSecurityRecipient@admiralcloud.com',
|
|
61
|
-
name: 'AdmiralCloud Security'
|
|
62
|
-
},
|
|
63
|
-
supportRecipient: {
|
|
64
|
-
address: address: 'defaultSupportRecipient@admiralcloud.com',
|
|
65
|
-
name: 'AdmiralCloud Support'
|
|
66
|
-
},
|
|
67
60
|
environment: ENVIRONMENT // defaults to proces.env.NODE_ENV,
|
|
68
61
|
useEnvironmentPrefixInSubject: TRUE|FALSE // defaults to TRUE - prefixes e-mail subject with environment to avoid confusion during development
|
|
69
62
|
})
|
|
70
|
-
|
|
71
|
-
|
|
72
63
|
```
|
|
73
64
|
|
|
74
65
|
## Links
|
|
75
66
|
- [Website](https://www.admiralcloud.com/)
|
|
76
|
-
- [Twitter (@admiralcloud)](https://twitter.com/admiralcloud)
|
|
77
|
-
- [Facebook](https://www.facebook.com/MediaAssetManagement/)
|
|
78
67
|
|
|
79
68
|
## License
|
|
80
|
-
[MIT License](https://opensource.org/licenses/MIT) Copyright © 2009-present, AdmiralCloud, Mark Poepping
|
|
69
|
+
[MIT License](https://opensource.org/licenses/MIT) Copyright © 2009-present, AdmiralCloud AG, Mark Poepping
|
package/index.js
CHANGED
|
@@ -1,39 +1,21 @@
|
|
|
1
1
|
const _ = require('lodash')
|
|
2
|
-
const async = require('async')
|
|
3
|
-
const aws = require('aws-sdk')
|
|
4
|
-
|
|
5
|
-
const crypto = require('crypto')
|
|
6
2
|
const { v4: uuidV4 } = require('uuid')
|
|
7
3
|
|
|
8
4
|
const quotedPrintable = require('quoted-printable')
|
|
9
5
|
const utf8 = require('utf8')
|
|
10
6
|
|
|
11
|
-
const
|
|
7
|
+
const { SESClient, SendRawEmailCommand } = require("@aws-sdk/client-ses")
|
|
8
|
+
|
|
9
|
+
const acses = () => {
|
|
12
10
|
let ses
|
|
13
|
-
let redis // only required if blockTime should be used - use init to set a redis instance from your main app
|
|
14
11
|
|
|
15
12
|
let defaultSender
|
|
16
|
-
let supportRecipient
|
|
17
|
-
let securityRecipient
|
|
18
|
-
let operationsRecipient
|
|
19
|
-
let defaultBlockTime = 60 // used for support and security
|
|
20
13
|
let testMode // if true, no email is sent
|
|
21
14
|
let environment = process.env.NODE_ENV || 'development'
|
|
22
15
|
let useEnvironmentPrefixInSubject = environment !== 'production'
|
|
23
16
|
|
|
24
17
|
const init = function(options) {
|
|
25
|
-
|
|
26
|
-
accessKeyId: _.get(options, 'aws.accessKeyId', process.env.AWS_ACCESS_KEY),
|
|
27
|
-
secretAccessKey: _.get(options, 'aws.secretAccessKey', process.env.AWS_ACCESS_SECRET),
|
|
28
|
-
region: _.get(options, 'aws.region', process.env.AWS_REGION)
|
|
29
|
-
}
|
|
30
|
-
ses = new aws.SES(awsConfig)
|
|
31
|
-
if (_.get(options, 'redis')) {
|
|
32
|
-
redis = _.get(options, 'redis')
|
|
33
|
-
}
|
|
34
|
-
if (_.get(options, 'defaultBlockTime')) {
|
|
35
|
-
defaultBlockTime = _.get(options, 'defaultBlockTime')
|
|
36
|
-
}
|
|
18
|
+
ses = new SESClient()
|
|
37
19
|
if (_.get(options, 'testMode')) {
|
|
38
20
|
testMode = _.get(options, 'testMode')
|
|
39
21
|
}
|
|
@@ -44,19 +26,11 @@ const acses = function() {
|
|
|
44
26
|
|
|
45
27
|
// helper
|
|
46
28
|
if (_.get(options, 'defaultSender') && prepareEmailAddress(_.get(options, 'defaultSender'))) defaultSender = _.get(options, 'defaultSender')
|
|
47
|
-
|
|
48
|
-
if (_.get(options, 'securityRecipient') && prepareEmailAddress(_.get(options, 'securityRecipient'))) securityRecipient = _.get(options, 'securityRecipient')
|
|
49
|
-
if (_.get(options, 'operationsRecipient') && prepareEmailAddress(_.get(options, 'operationsRecipient'))) operationsRecipient = _.get(options, 'operationsRecipient')
|
|
50
|
-
|
|
29
|
+
|
|
51
30
|
return {
|
|
52
|
-
awsConfig: _.pick(awsConfig, ['accessKeyId', 'region']),
|
|
53
31
|
testMode,
|
|
54
|
-
defaultBlockTime,
|
|
55
32
|
environment,
|
|
56
|
-
defaultSender
|
|
57
|
-
supportRecipient,
|
|
58
|
-
securityRecipient,
|
|
59
|
-
operationsRecipient
|
|
33
|
+
defaultSender
|
|
60
34
|
}
|
|
61
35
|
}
|
|
62
36
|
|
|
@@ -67,10 +41,10 @@ const acses = function() {
|
|
|
67
41
|
* OUT John Doe <john.doe@example.com>
|
|
68
42
|
* @param {*} params
|
|
69
43
|
*/
|
|
70
|
-
const prepareEmailAddress =
|
|
71
|
-
if (!
|
|
72
|
-
if (!_.isString(
|
|
73
|
-
let email =
|
|
44
|
+
const prepareEmailAddress = ({ address, name }) => {
|
|
45
|
+
if (!address) throw new Error({ message: 'ACSES.prepareEmailAddress - address_required' })
|
|
46
|
+
if (!_.isString(address)) throw new Error({ message: 'ACSES.prepareEmailAddress - address_mustBeAString' })
|
|
47
|
+
let email = name ? `${name} <${address}>` : address
|
|
74
48
|
return email
|
|
75
49
|
}
|
|
76
50
|
|
|
@@ -89,14 +63,11 @@ const acses = function() {
|
|
|
89
63
|
*
|
|
90
64
|
* @param attachments ARRAY Optional array of objects with properties: filename, content (as base64) and encoding as 'base64'
|
|
91
65
|
*
|
|
92
|
-
* @param blockTime INT OPTIONAL If set, you cannot send an email to the same recipient for the blockTime (helpful for warning messages - you don't want them every second!)
|
|
93
|
-
* @param redisKey STRING OPTIONAL You can use your own redisKey for the blockTime feature, or let this app create an MD5 hash from the parameters
|
|
94
66
|
* @param encoding STRING OPTIONAL Encoding for multipart alternative parts - defaults to "quoted-printable"
|
|
95
67
|
*
|
|
96
|
-
* @param {*} cb Optional callback
|
|
97
68
|
*/
|
|
98
|
-
const sendEmail = (params
|
|
99
|
-
if (!_.isObject(ses))
|
|
69
|
+
const sendEmail = async(params) => {
|
|
70
|
+
if (!_.isObject(ses)) throw new Error('pleaseUseInitBeforeSendingEmail')
|
|
100
71
|
if (!_.get(params, 'from') && defaultSender) _.set(params, 'from', defaultSender)
|
|
101
72
|
const fieldCheck = [
|
|
102
73
|
{ field: 'from', type: _.isPlainObject, required: true },
|
|
@@ -113,173 +84,91 @@ const acses = function() {
|
|
|
113
84
|
]
|
|
114
85
|
|
|
115
86
|
_.some(fieldCheck, (field) => {
|
|
116
|
-
if (field.required && !_.has(params, field.field))
|
|
117
|
-
if (_.get(params, field.field) && !field.type(_.get(params, field.field)))
|
|
87
|
+
if (field.required && !_.has(params, field.field)) throw new Error(field.field + '_required')
|
|
88
|
+
if (_.get(params, field.field) && !field.type(_.get(params, field.field))) throw new Error(field.field + '_typeInvalid')
|
|
118
89
|
})
|
|
119
90
|
|
|
120
91
|
const boundaryMixed = uuidV4()
|
|
121
92
|
const boundaryAlternative = uuidV4()
|
|
122
93
|
const encoding = _.get(params, 'encoding', 'quoted-printable')
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
text: params.text
|
|
132
|
-
}
|
|
133
|
-
let redisKey = _.get(params, 'redisKey', crypto.createHash('md5').update(JSON.stringify(sesParams)).digest('hex'))
|
|
134
|
-
redis.set(redisKey, 1, 'EX', params.blockTime, 'NX', (err, result) => {
|
|
135
|
-
if (err) return done(err)
|
|
136
|
-
if (result === 'OK') return done()
|
|
137
|
-
return done(423) // the key is already locked
|
|
95
|
+
// sendRawMessage
|
|
96
|
+
let raw = 'From: ' + prepareEmailAddress(_.get(params, 'from')) + '\n'
|
|
97
|
+
// prepare To, Cc, Bcc
|
|
98
|
+
_.forEach(['to', 'cc', 'bcc'], type => {
|
|
99
|
+
if (_.size(_.get(params, type))) {
|
|
100
|
+
let recipients = _.map(_.get(params, type), (recipient) => {
|
|
101
|
+
return prepareEmailAddress(recipient)
|
|
138
102
|
})
|
|
139
|
-
|
|
140
|
-
sendRawMessage: (done) => {
|
|
141
|
-
let raw = 'From: ' + prepareEmailAddress(_.get(params, 'from')) + '\n'
|
|
142
|
-
// prepare To, Cc, Bcc
|
|
143
|
-
_.forEach(['to', 'cc', 'bcc'], type => {
|
|
144
|
-
if (_.size(_.get(params, type))) {
|
|
145
|
-
let recipients = _.map(_.get(params, type), (recipient) => {
|
|
146
|
-
return prepareEmailAddress(recipient)
|
|
147
|
-
})
|
|
148
|
-
raw += _.upperFirst(type) + ': ' + _.join(recipients, ', ') + '\n'
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
if (params.replyTo) {
|
|
152
|
-
let recipients = _.map(_.get(params, 'replyTo'), (recipient) => {
|
|
153
|
-
return prepareEmailAddress(recipient)
|
|
154
|
-
})
|
|
155
|
-
raw += 'Reply-To: ' + _.join(recipients, ', ') + '\n'
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
raw += 'Subject: ' + (useEnvironmentPrefixInSubject ? (_.toUpper(environment) + ' | ') : '') + params.subject + '\n'
|
|
159
|
-
if (params?.debug) {
|
|
160
|
-
console.log('ACSES | DEBUG Headers | %j', raw.split('/n'))
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// announce multipart/mixed
|
|
164
|
-
raw += 'Mime-Version: 1.0\n'
|
|
165
|
-
raw += 'Content-type: multipart/mixed; boundary="' + boundaryMixed + '"\n\n'
|
|
166
|
-
raw += 'This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible.\n\n'
|
|
167
|
-
|
|
168
|
-
// text and HTML are multipart/alternatives with their own boundaries
|
|
169
|
-
raw += '--' + boundaryMixed + '\nContent-Type: multipart/alternative; boundary="' + boundaryAlternative + '"\n\n'
|
|
170
|
-
if (params.text) {
|
|
171
|
-
raw += '--' + boundaryAlternative + '\nContent-Type: text/plain; charset="UTF-8"\nContent-Transfer-Encoding: ' + encoding + '\n\n'
|
|
172
|
-
raw += quotedPrintable.encode(utf8.encode(params.text)) + '\n\n'
|
|
173
|
-
}
|
|
174
|
-
if (params.html) {
|
|
175
|
-
raw += '--' + boundaryAlternative + '\nContent-Type: text/html; charset="UTF-8"\nContent-Transfer-Encoding: ' + encoding + '\n\n'
|
|
176
|
-
raw += quotedPrintable.encode(utf8.encode(params.html)) + '\n\n'
|
|
177
|
-
}
|
|
178
|
-
raw += '--' + boundaryAlternative + '--\n\n'
|
|
179
|
-
|
|
180
|
-
if (params.attachments) {
|
|
181
|
-
_.forEach(params.attachments, attachment => {
|
|
182
|
-
raw += '--' + boundaryMixed + '\n'
|
|
183
|
-
raw += 'Content-Disposition: attachment; filename="' + attachment.filename + '"\n'
|
|
184
|
-
raw += 'Content-Type: ' + attachment.contentType + '; name="' + attachment.filename + '"\nContent-Transfer-Encoding: ' + attachment.encoding + '\n\n'
|
|
185
|
-
raw += attachment.content + '\n\n'
|
|
186
|
-
})
|
|
187
|
-
raw += '--' + boundaryMixed + '--\n'
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const rawParams = {
|
|
191
|
-
RawMessage: { /* required */
|
|
192
|
-
Data: Buffer.from(raw)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (testMode) {
|
|
196
|
-
// return fake response, but do not send message
|
|
197
|
-
let mockResponse = {
|
|
198
|
-
ResponseMetadata: {
|
|
199
|
-
RequestId: uuidV4()
|
|
200
|
-
},
|
|
201
|
-
MessageId: Math.random().toString(36) + '-' + uuidV4() + '-000000',
|
|
202
|
-
testMode: true
|
|
203
|
-
}
|
|
204
|
-
return done(null, mockResponse)
|
|
205
|
-
}
|
|
206
|
-
ses.sendRawEmail(rawParams, done)
|
|
103
|
+
raw += _.upperFirst(type) + ': ' + _.join(recipients, ', ') + '\n'
|
|
207
104
|
}
|
|
208
|
-
}, (err, result) => {
|
|
209
|
-
if (err && err === 423) err = null // this is not an error, just blocked
|
|
210
|
-
if (_.isFunction(cb)) {
|
|
211
|
-
return cb(err, _.get(result, 'sendRawMessage'))
|
|
212
|
-
}
|
|
213
|
-
if (err) throw new Error(err)
|
|
214
105
|
})
|
|
215
|
-
|
|
106
|
+
if (params.replyTo) {
|
|
107
|
+
let recipients = _.map(_.get(params, 'replyTo'), (recipient) => {
|
|
108
|
+
return prepareEmailAddress(recipient)
|
|
109
|
+
})
|
|
110
|
+
raw += 'Reply-To: ' + _.join(recipients, ', ') + '\n'
|
|
111
|
+
}
|
|
216
112
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* Differences: no HTML to improve delivery
|
|
221
|
-
* @param {*} params
|
|
222
|
-
* @param {*} cb
|
|
223
|
-
*/
|
|
224
|
-
const informSecurity = function(params, cb) {
|
|
225
|
-
if (!securityRecipient) return cb({ message: 'acses.informSecurity - securityRecipient_notSet' })
|
|
226
|
-
let message = {
|
|
227
|
-
to: [securityRecipient],
|
|
228
|
-
subject: params.subject,
|
|
229
|
-
text: params.text,
|
|
230
|
-
blockTime: _.get(params, 'blockTime', defaultBlockTime),
|
|
231
|
-
redisKey: _.get(params, 'redisKey')
|
|
113
|
+
raw += 'Subject: ' + (useEnvironmentPrefixInSubject ? (_.toUpper(environment) + ' | ') : '') + params.subject + '\n'
|
|
114
|
+
if (params?.debug) {
|
|
115
|
+
console.log('ACSES | DEBUG Headers | %j', raw.split('/n'))
|
|
232
116
|
}
|
|
233
|
-
if (_.isFunction(cb)) sendEmail(message, cb)
|
|
234
|
-
else sendEmail(message)
|
|
235
|
-
}
|
|
236
117
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
text:
|
|
250
|
-
|
|
251
|
-
|
|
118
|
+
// announce multipart/mixed
|
|
119
|
+
raw += 'Mime-Version: 1.0\n'
|
|
120
|
+
raw += 'Content-type: multipart/mixed; boundary="' + boundaryMixed + '"\n\n'
|
|
121
|
+
raw += 'This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible.\n\n'
|
|
122
|
+
|
|
123
|
+
// text and HTML are multipart/alternatives with their own boundaries
|
|
124
|
+
raw += '--' + boundaryMixed + '\nContent-Type: multipart/alternative; boundary="' + boundaryAlternative + '"\n\n'
|
|
125
|
+
if (params.text) {
|
|
126
|
+
raw += '--' + boundaryAlternative + '\nContent-Type: text/plain; charset="UTF-8"\nContent-Transfer-Encoding: ' + encoding + '\n\n'
|
|
127
|
+
raw += quotedPrintable.encode(utf8.encode(params.text)) + '\n\n'
|
|
128
|
+
}
|
|
129
|
+
if (params.html) {
|
|
130
|
+
raw += '--' + boundaryAlternative + '\nContent-Type: text/html; charset="UTF-8"\nContent-Transfer-Encoding: ' + encoding + '\n\n'
|
|
131
|
+
raw += quotedPrintable.encode(utf8.encode(params.html)) + '\n\n'
|
|
132
|
+
}
|
|
133
|
+
raw += '--' + boundaryAlternative + '--\n\n'
|
|
134
|
+
|
|
135
|
+
if (params.attachments) {
|
|
136
|
+
_.forEach(params.attachments, attachment => {
|
|
137
|
+
raw += '--' + boundaryMixed + '\n'
|
|
138
|
+
raw += 'Content-Disposition: attachment; filename="' + attachment.filename + '"\n'
|
|
139
|
+
raw += 'Content-Type: ' + attachment.contentType + '; name="' + attachment.filename + '"\nContent-Transfer-Encoding: ' + attachment.encoding + '\n\n'
|
|
140
|
+
raw += attachment.content + '\n\n'
|
|
141
|
+
})
|
|
142
|
+
raw += '--' + boundaryMixed + '--\n'
|
|
252
143
|
}
|
|
253
|
-
if (_.isFunction(cb)) sendEmail(message, cb)
|
|
254
|
-
else sendEmail(message)
|
|
255
|
-
}
|
|
256
144
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
145
|
+
const rawParams = {
|
|
146
|
+
RawMessage: { /* required */
|
|
147
|
+
Data: Buffer.from(raw)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (testMode) {
|
|
152
|
+
// return fake response, but do not send message
|
|
153
|
+
let mockResponse = {
|
|
154
|
+
'$metadata': {
|
|
155
|
+
httpStatusCode: 200,
|
|
156
|
+
requestId: uuidV4(),
|
|
157
|
+
attempts: 1
|
|
158
|
+
},
|
|
159
|
+
MessageId: Math.random().toString(36) + '-' + uuidV4() + '-000000',
|
|
160
|
+
testMode: true
|
|
161
|
+
}
|
|
162
|
+
return mockResponse
|
|
272
163
|
}
|
|
273
|
-
|
|
274
|
-
|
|
164
|
+
const command = new SendRawEmailCommand(rawParams)
|
|
165
|
+
const response = await ses.send(command)
|
|
166
|
+
return response
|
|
275
167
|
}
|
|
276
168
|
|
|
277
169
|
return {
|
|
278
170
|
init,
|
|
279
|
-
sendEmail
|
|
280
|
-
informSecurity,
|
|
281
|
-
informSupport,
|
|
282
|
-
informOperations
|
|
171
|
+
sendEmail
|
|
283
172
|
}
|
|
284
173
|
}
|
|
285
174
|
|
package/package.json
CHANGED
|
@@ -4,21 +4,19 @@
|
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "admiralcloud/ac-ses",
|
|
6
6
|
"homepage": "https://www.admiralcloud.com",
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "2.0.0",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"
|
|
10
|
-
"aws-sdk": "^2.1305.0",
|
|
9
|
+
"@aws-sdk/client-ses": "^3.485.0",
|
|
11
10
|
"lodash": "^4.17.21",
|
|
12
11
|
"quoted-printable": "^1.0.1",
|
|
13
12
|
"utf8": "^3.0.0",
|
|
14
13
|
"uuid": "^9.x"
|
|
15
14
|
},
|
|
16
15
|
"devDependencies": {
|
|
17
|
-
"ac-semantic-release": "^0.
|
|
18
|
-
"chai": "^4.
|
|
16
|
+
"ac-semantic-release": "^0.4.2",
|
|
17
|
+
"chai": "^4.4.0",
|
|
19
18
|
"eslint": "8.x",
|
|
20
|
-
"
|
|
21
|
-
"expect": "^29.4.1",
|
|
19
|
+
"expect": "^29.7.0",
|
|
22
20
|
"mocha": "^10.2.0",
|
|
23
21
|
"mocha-jenkins-reporter": "0.4.8"
|
|
24
22
|
},
|
package/test/test.js
CHANGED
|
@@ -1,127 +1,41 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
1
|
+
const fs = require('fs/promises')
|
|
2
2
|
|
|
3
3
|
const acses = require('../index')
|
|
4
4
|
|
|
5
5
|
const expect = require('chai').expect
|
|
6
|
-
|
|
7
|
-
var Redis = require('ioredis')
|
|
8
|
-
var redis = new Redis({
|
|
9
|
-
host: process.env.REDIS_HOST || 'localhost',
|
|
10
|
-
port: process.env.REDIS_PORT || 6379
|
|
11
|
-
})
|
|
12
|
-
|
|
13
6
|
const testConfig = require('./testConfig.js')
|
|
14
7
|
|
|
15
8
|
describe('CHECKING ERRORS', function () {
|
|
16
|
-
it('Send email without init',
|
|
9
|
+
it('Send email without init', async() => {
|
|
17
10
|
let params = testConfig.email
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
try {
|
|
12
|
+
await acses.sendEmail(params)
|
|
13
|
+
}
|
|
14
|
+
catch(e) {
|
|
15
|
+
expect(e.message).to.eql('pleaseUseInitBeforeSendingEmail')
|
|
16
|
+
}
|
|
22
17
|
})
|
|
23
18
|
})
|
|
24
19
|
|
|
25
20
|
describe('TESTING EMAIL', function () {
|
|
26
|
-
it('Init AC SES',
|
|
21
|
+
it('Init AC SES', async() => {
|
|
27
22
|
acses.init(testConfig.init)
|
|
28
|
-
return done()
|
|
29
23
|
})
|
|
30
24
|
|
|
31
|
-
it('Send a text email',
|
|
25
|
+
it('Send a text email', async() => {
|
|
32
26
|
let params = testConfig.email
|
|
33
|
-
acses.sendEmail(params
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
expect(result).to.have.property('MessageId')
|
|
37
|
-
return done()
|
|
38
|
-
})
|
|
27
|
+
let result = await acses.sendEmail(params)
|
|
28
|
+
expect(result).to.have.property('$metadata')
|
|
29
|
+
expect(result).to.have.property('MessageId')
|
|
39
30
|
})
|
|
40
31
|
|
|
41
|
-
it('Send a HTML email',
|
|
32
|
+
it('Send a HTML email', async() => {
|
|
42
33
|
let params = testConfig.email
|
|
43
|
-
fs.readFile(process.cwd() + '/test/htmlTemplate.html'
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return done()
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('Send a security email', function(done) {
|
|
57
|
-
let params = testConfig.securityEmail
|
|
58
|
-
acses.informSecurity(params, (err, result) => {
|
|
59
|
-
if (err) return done(err)
|
|
60
|
-
expect(result).to.have.property('ResponseMetadata')
|
|
61
|
-
expect(result).to.have.property('MessageId')
|
|
62
|
-
return done()
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('Send a support email', function(done) {
|
|
67
|
-
let params = testConfig.supportEmail
|
|
68
|
-
acses.informSecurity(params, (err, result) => {
|
|
69
|
-
if (err) return done(err)
|
|
70
|
-
expect(result).to.have.property('ResponseMetadata')
|
|
71
|
-
expect(result).to.have.property('MessageId')
|
|
72
|
-
return done()
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
describe('TESTING BLOCK TIME', function() {
|
|
78
|
-
this.timeout(60000)
|
|
79
|
-
|
|
80
|
-
it('Init AC SES with Redis support', function(done) {
|
|
81
|
-
testConfig.init.redis = redis
|
|
82
|
-
acses.init(testConfig.init)
|
|
83
|
-
return done()
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('Send a text email', function(done) {
|
|
87
|
-
let params = testConfig.email
|
|
88
|
-
params.html = null
|
|
89
|
-
params.subject = 'Test email with blocktime'
|
|
90
|
-
params.blockTime = 10
|
|
91
|
-
acses.sendEmail(params, (err, result) => {
|
|
92
|
-
if (err) return done(err)
|
|
93
|
-
expect(result).to.have.property('ResponseMetadata')
|
|
94
|
-
expect(result).to.have.property('MessageId')
|
|
95
|
-
return done()
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('Send a text email - should fail - it is the SAME email and block time is active', function(done) {
|
|
100
|
-
let params = testConfig.email
|
|
101
|
-
acses.sendEmail(params, (err, result) => {
|
|
102
|
-
if (err) return done(err)
|
|
103
|
-
expect(result).to.be.undefined
|
|
104
|
-
return done()
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('Wait until blocktime is over', function(done) {
|
|
109
|
-
setTimeout(done, 10000)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('Send a text email - should work again', function(done) {
|
|
113
|
-
let params = testConfig.email
|
|
114
|
-
params.html = null
|
|
115
|
-
params.subject = 'Test after block time'
|
|
116
|
-
acses.sendEmail(params, (err, result) => {
|
|
117
|
-
if (err) return done(err)
|
|
118
|
-
expect(result).to.have.property('ResponseMetadata')
|
|
119
|
-
expect(result).to.have.property('MessageId')
|
|
120
|
-
return done()
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('Close Redis connection', function(done) {
|
|
125
|
-
redis.quit(done)
|
|
126
|
-
})
|
|
127
|
-
})
|
|
34
|
+
const data = await fs.readFile(process.cwd() + '/test/htmlTemplate.html')
|
|
35
|
+
params.subject = 'HTML Test E-Mail'
|
|
36
|
+
params.html = data.toString()
|
|
37
|
+
const result = await acses.sendEmail(params)
|
|
38
|
+
expect(result).to.have.property('$metadata')
|
|
39
|
+
expect(result).to.have.property('MessageId')
|
|
40
|
+
})
|
|
41
|
+
})
|