@toa.io/extensions.mail 1.0.0-alpha.146 → 1.0.0-alpha.149
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/components/mail.agent/manifest.toa.yaml +13 -11
- package/components/mail.agent/operations/providers/Resend.js +1 -1
- package/components/mail.agent/operations/send.js +17 -52
- package/features/mail.feature +31 -30
- package/features/steps/components/spam/manifest.toa.yaml +2 -7
- package/package.json +3 -3
|
@@ -5,21 +5,23 @@ operations:
|
|
|
5
5
|
send:
|
|
6
6
|
input:
|
|
7
7
|
properties:
|
|
8
|
+
from: { type: string }
|
|
8
9
|
to: { type: string }
|
|
9
10
|
subject: { type: string }
|
|
10
11
|
text: { type: string }
|
|
11
|
-
|
|
12
|
-
data: { type: object, default: { } }
|
|
13
|
-
required: [to]
|
|
14
|
-
oneOf:
|
|
15
|
-
- required: [subject, text]
|
|
16
|
-
- required: [template]
|
|
12
|
+
required: [from, to, subject, text]
|
|
17
13
|
|
|
18
14
|
configuration:
|
|
19
15
|
schema:
|
|
20
16
|
properties:
|
|
21
|
-
provider:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
provider:
|
|
18
|
+
type: string
|
|
19
|
+
enum: [Console, Resend]
|
|
20
|
+
domains:
|
|
21
|
+
description: Allowed sender domains
|
|
22
|
+
type: array
|
|
23
|
+
items: { type: string }
|
|
24
|
+
options:
|
|
25
|
+
description: Provider specific options
|
|
26
|
+
type: object
|
|
27
|
+
required: [provider, domains]
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
const assert = require('node:assert')
|
|
2
|
+
const { Err } = require('error-value')
|
|
2
3
|
const providers = require('./providers')
|
|
3
|
-
const { load: parse } = require('cheerio')
|
|
4
4
|
|
|
5
5
|
class Effect {
|
|
6
|
-
/**
|
|
7
|
-
* Base url for rendering
|
|
8
|
-
*/
|
|
9
|
-
base
|
|
10
|
-
from
|
|
11
6
|
provider
|
|
7
|
+
config
|
|
12
8
|
logs
|
|
13
9
|
|
|
14
10
|
mount (context) {
|
|
@@ -17,27 +13,18 @@ class Effect {
|
|
|
17
13
|
const Provider = providers[context.configuration.provider]
|
|
18
14
|
|
|
19
15
|
this.provider = new Provider(context)
|
|
20
|
-
this.
|
|
21
|
-
this.base = context.configuration.templates
|
|
16
|
+
this.config = context.configuration
|
|
22
17
|
this.logs = context.logs
|
|
23
18
|
}
|
|
24
19
|
|
|
25
|
-
async execute (
|
|
26
|
-
if (
|
|
27
|
-
|
|
20
|
+
async execute (message) {
|
|
21
|
+
if (this.invalidSender(message.from))
|
|
22
|
+
return ERR_INVALID_SENDER
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const properties = template === undefined ? { text } : await this.html(template, data)
|
|
33
|
-
|
|
34
|
-
if (subject !== undefined)
|
|
35
|
-
properties.subject = subject
|
|
24
|
+
if (message.to.endsWith('.null')) {
|
|
25
|
+
this.logs.debug('Mail skipped', { to: message.to })
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
...properties,
|
|
39
|
-
to,
|
|
40
|
-
from: this.from
|
|
27
|
+
return
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
this.logs.debug('Sending mail', message)
|
|
@@ -45,39 +32,17 @@ class Effect {
|
|
|
45
32
|
await this.provider.send(message)
|
|
46
33
|
}
|
|
47
34
|
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
/** @type {toa.extensions.mail.Message} */
|
|
52
|
-
return { subject: title, html }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async render (template, data) {
|
|
56
|
-
assert.ok(this.base !== undefined, 'Base url for rendering is not set, cannot send HTML mail')
|
|
57
|
-
|
|
58
|
-
const url = new URL(`./${template}/`, this.base).href
|
|
59
|
-
|
|
60
|
-
this.logs.debug('Requesting render', { url, data })
|
|
61
|
-
|
|
62
|
-
const response = await fetch(url, {
|
|
63
|
-
method: 'POST',
|
|
64
|
-
headers: {
|
|
65
|
-
'content-type': 'application/json'
|
|
66
|
-
},
|
|
67
|
-
body: JSON.stringify(data)
|
|
68
|
-
})
|
|
35
|
+
invalidSender (from) {
|
|
36
|
+
const domain = from.split('@')[1]
|
|
37
|
+
const invalid = !this.config.domains.includes(domain)
|
|
69
38
|
|
|
70
|
-
|
|
39
|
+
if (invalid)
|
|
40
|
+
this.logs.debug('Sender domain not allowed', { domain })
|
|
71
41
|
|
|
72
|
-
|
|
73
|
-
assert.ok(type === 'text/html', `Rendering reply must be text/html, ${type} received`)
|
|
74
|
-
|
|
75
|
-
const html = await response.text()
|
|
76
|
-
const $ = parse(html)
|
|
77
|
-
const title = $('title').text()
|
|
78
|
-
|
|
79
|
-
return { title, html }
|
|
42
|
+
return invalid
|
|
80
43
|
}
|
|
81
44
|
}
|
|
82
45
|
|
|
46
|
+
const ERR_INVALID_SENDER = new Err('INVALID_SENDER')
|
|
47
|
+
|
|
83
48
|
exports.Effect = Effect
|
package/features/mail.feature
CHANGED
|
@@ -1,49 +1,50 @@
|
|
|
1
1
|
Feature: Sending an email
|
|
2
2
|
|
|
3
|
-
Scenario: Sending
|
|
3
|
+
Scenario: Sending text email
|
|
4
4
|
Given the `mail.agent` configuration:
|
|
5
5
|
"""yaml
|
|
6
|
-
templates: http://localhost:8088/emails/
|
|
7
6
|
provider: Console
|
|
8
|
-
|
|
7
|
+
domains: [nobody.null]
|
|
9
8
|
"""
|
|
10
9
|
And the service is running
|
|
11
10
|
And the spam is running
|
|
12
|
-
And rendering is sending:
|
|
13
|
-
"""html
|
|
14
|
-
<head>
|
|
15
|
-
<title>Hello!</title>
|
|
16
|
-
</head>
|
|
17
|
-
<body>
|
|
18
|
-
<p>This is a beautiful email</p>
|
|
19
|
-
</body>
|
|
20
|
-
"""
|
|
21
11
|
When `spam.send` is called:
|
|
22
12
|
"""yaml
|
|
13
|
+
from: alice@nobody.null
|
|
23
14
|
to: no@one
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
When `spam.send` is called:
|
|
27
|
-
"""yaml
|
|
28
|
-
to: some@one
|
|
29
|
-
template: bye
|
|
30
|
-
data:
|
|
31
|
-
name: Some One
|
|
15
|
+
subject: Test
|
|
16
|
+
text: hello!
|
|
32
17
|
"""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Scenario: Sending text email
|
|
36
|
-
Given the `mail.agent` configuration:
|
|
18
|
+
And `spam.send` is called:
|
|
37
19
|
"""yaml
|
|
38
|
-
|
|
39
|
-
|
|
20
|
+
from: bob@nobody.null
|
|
21
|
+
to: no@one
|
|
22
|
+
subject: Test
|
|
23
|
+
text: hello!
|
|
40
24
|
"""
|
|
41
|
-
And
|
|
42
|
-
And the spam is running
|
|
43
|
-
When `spam.send` is called:
|
|
25
|
+
And `spam.send` is called:
|
|
44
26
|
"""yaml
|
|
27
|
+
from: charlie@somebody.null
|
|
45
28
|
to: no@one
|
|
46
29
|
subject: Test
|
|
47
|
-
text:
|
|
30
|
+
text: This won't work, domain is not allowed
|
|
48
31
|
"""
|
|
49
32
|
Then go check the email or logs
|
|
33
|
+
|
|
34
|
+
# Scenario: Sending text email via Resend
|
|
35
|
+
# Given the `mail.agent` configuration:
|
|
36
|
+
# """yaml
|
|
37
|
+
# provider: Resend
|
|
38
|
+
# domains: [resend.dev]
|
|
39
|
+
# options:
|
|
40
|
+
# key: << PUT KEY HERE >>
|
|
41
|
+
# """
|
|
42
|
+
# And the service is running
|
|
43
|
+
# And the spam is running
|
|
44
|
+
# When `spam.send` is called:
|
|
45
|
+
# """yaml
|
|
46
|
+
# from: onboarding@resend.dev
|
|
47
|
+
# to: tema.gurtovoy@gmail.com
|
|
48
|
+
# subject: Sent via Resend
|
|
49
|
+
# text: Ok.
|
|
50
|
+
# """
|
|
@@ -3,16 +3,11 @@ name: spam
|
|
|
3
3
|
operations:
|
|
4
4
|
send:
|
|
5
5
|
input:
|
|
6
|
-
type: object
|
|
7
6
|
properties:
|
|
7
|
+
from: { type: string }
|
|
8
8
|
to: { type: string }
|
|
9
9
|
subject: { type: string }
|
|
10
10
|
text: { type: string }
|
|
11
|
-
|
|
12
|
-
data: { type: object }
|
|
13
|
-
required: [to]
|
|
14
|
-
oneOf:
|
|
15
|
-
- required: [subject, text]
|
|
16
|
-
- required: [template]
|
|
11
|
+
required: [from, to, subject, text]
|
|
17
12
|
|
|
18
13
|
mail: ~
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/extensions.mail",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.149",
|
|
4
4
|
"description": "Toa Mail",
|
|
5
5
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"@toa.io/core": "1.0.0-alpha.136",
|
|
27
27
|
"@toa.io/generic": "1.0.0-alpha.93",
|
|
28
28
|
"@toa.io/schemas": "1.0.0-alpha.143",
|
|
29
|
-
"
|
|
29
|
+
"error-value": "0.4.4",
|
|
30
30
|
"resend": "4.1.2"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "add1742266b0203944346a95ca6e9564b9725276"
|
|
33
33
|
}
|