@5minds/node-red-contrib-processcube-tools 1.0.1 → 1.0.2-feature-8805f0-mfnm2ko3
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/.env.template +4 -0
- package/README.md +1 -1
- package/email-receiver/email-receiver.html +219 -0
- package/email-receiver/email-receiver.js +250 -0
- package/email-sender/email-sender.html +307 -0
- package/email-sender/email-sender.js +178 -0
- package/package.json +25 -4
- package/{processcube-html-to-text.html → processcube-html-to-text/processcube-html-to-text.html} +9 -9
- package/{processcube-html-to-text.js → processcube-html-to-text/processcube-html-to-text.js} +0 -3
- package/test/helpers/email-receiver.mocks.js +447 -0
- package/test/helpers/email-sender.mocks.js +368 -0
- package/test/integration/email-receiver.integration.test.js +515 -0
- package/test/integration/email-sender.integration.test.js +239 -0
- package/test/unit/email-receiver.unit.test.js +304 -0
- package/test/unit/email-sender.unit.test.js +570 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('email-sender', {
|
|
3
|
+
category: 'ProcessCube Tools',
|
|
4
|
+
color: '#02AFD6',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: { value: '' },
|
|
7
|
+
// Mail fields
|
|
8
|
+
sender: {
|
|
9
|
+
value: 'Stoelting Ticket-System',
|
|
10
|
+
required: true,
|
|
11
|
+
validate: RED.validators.typedInput('senderType'),
|
|
12
|
+
},
|
|
13
|
+
senderType: { value: 'str' },
|
|
14
|
+
address: { value: '', required: true, validate: RED.validators.typedInput('addressType') },
|
|
15
|
+
addressType: { value: 'str' },
|
|
16
|
+
to: { value: '', required: true, validate: RED.validators.typedInput('toType') },
|
|
17
|
+
toType: { value: 'str' },
|
|
18
|
+
cc: { value: '', validate: RED.validators.typedInput('ccType') },
|
|
19
|
+
ccType: { value: 'str' },
|
|
20
|
+
bcc: { value: '', validate: RED.validators.typedInput('bccType') },
|
|
21
|
+
bccType: { value: 'str' },
|
|
22
|
+
replyTo: { value: '', validate: RED.validators.typedInput('replyToType') },
|
|
23
|
+
replyToType: { value: 'str' },
|
|
24
|
+
subject: { value: '', required: true, validate: RED.validators.typedInput('subjectType') },
|
|
25
|
+
subjectType: { value: 'str' },
|
|
26
|
+
htmlContent: { value: '', required: true, validate: RED.validators.typedInput('htmlContentType') },
|
|
27
|
+
htmlContentType: { value: 'str' },
|
|
28
|
+
attachments: { value: '', required: false },
|
|
29
|
+
attachmentsType: { value: 'msg' },
|
|
30
|
+
|
|
31
|
+
// SMTP-Fields
|
|
32
|
+
host: { value: '', required: true, validate: RED.validators.typedInput('hostType') },
|
|
33
|
+
hostType: { value: 'str' },
|
|
34
|
+
port: { value: '', required: true, validate: RED.validators.typedInput('portType') },
|
|
35
|
+
portType: { value: 'num' },
|
|
36
|
+
user: { value: '', required: true, validate: RED.validators.typedInput('userType') },
|
|
37
|
+
userType: { value: 'str' },
|
|
38
|
+
password: { value: '', required: true, type: 'password' },
|
|
39
|
+
passwordType: { value: 'env', required: true },
|
|
40
|
+
secure: { value: '', required: true, validate: RED.validators.typedInput('secureType') },
|
|
41
|
+
secureType: { value: 'bool' },
|
|
42
|
+
rejectUnauthorized: {
|
|
43
|
+
value: '',
|
|
44
|
+
required: true,
|
|
45
|
+
validate: RED.validators.typedInput('rejectUnauthorizedType'),
|
|
46
|
+
},
|
|
47
|
+
rejectUnauthorizedType: { value: 'bool' },
|
|
48
|
+
},
|
|
49
|
+
inputs: 1,
|
|
50
|
+
outputs: 1,
|
|
51
|
+
icon: 'font-awesome/fa-paper-plane',
|
|
52
|
+
label: function () {
|
|
53
|
+
return this.name || 'E-Mail Sender';
|
|
54
|
+
},
|
|
55
|
+
oneditprepare: function () {
|
|
56
|
+
// Mail Fields
|
|
57
|
+
$('#node-input-sender').typedInput({
|
|
58
|
+
default: 'str',
|
|
59
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
60
|
+
typeField: '#node-input-senderType',
|
|
61
|
+
});
|
|
62
|
+
$('#node-input-address').typedInput({
|
|
63
|
+
default: 'str',
|
|
64
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
65
|
+
typeField: '#node-input-addressType',
|
|
66
|
+
});
|
|
67
|
+
$('#node-input-to').typedInput({
|
|
68
|
+
default: 'str',
|
|
69
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
70
|
+
typeField: '#node-input-toType',
|
|
71
|
+
});
|
|
72
|
+
$('#node-input-cc').typedInput({
|
|
73
|
+
default: 'str',
|
|
74
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
75
|
+
typeField: '#node-input-ccType',
|
|
76
|
+
});
|
|
77
|
+
$('#node-input-bcc').typedInput({
|
|
78
|
+
default: 'str',
|
|
79
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
80
|
+
typeField: '#node-input-bccType',
|
|
81
|
+
});
|
|
82
|
+
$('#node-input-replyTo').typedInput({
|
|
83
|
+
default: 'str',
|
|
84
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
85
|
+
typeField: '#node-input-replyToType',
|
|
86
|
+
});
|
|
87
|
+
$('#node-input-subject').typedInput({
|
|
88
|
+
default: 'str',
|
|
89
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
90
|
+
typeField: '#node-input-subjectType',
|
|
91
|
+
});
|
|
92
|
+
$('#node-input-htmlContent').typedInput({
|
|
93
|
+
default: 'str',
|
|
94
|
+
types: ['str', 'msg', 'flow', 'global', 'json'],
|
|
95
|
+
typeField: '#node-input-htmlContentType',
|
|
96
|
+
});
|
|
97
|
+
$('#node-input-attachments').typedInput({
|
|
98
|
+
default: 'msg',
|
|
99
|
+
types: ['msg', 'flow', 'global'],
|
|
100
|
+
typeField: '#node-input-attachmentsType',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// SMTP Fields
|
|
104
|
+
$('#node-input-host').typedInput({
|
|
105
|
+
default: 'str',
|
|
106
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
107
|
+
typeField: '#node-input-hostType',
|
|
108
|
+
});
|
|
109
|
+
$('#node-input-port').typedInput({
|
|
110
|
+
default: 'num',
|
|
111
|
+
types: ['num', 'msg', 'flow', 'global', 'env'],
|
|
112
|
+
typeField: '#node-input-portType',
|
|
113
|
+
});
|
|
114
|
+
$('#node-input-user').typedInput({
|
|
115
|
+
default: 'str',
|
|
116
|
+
types: ['str', 'msg', 'flow', 'global', 'env'],
|
|
117
|
+
typeField: '#node-input-userType',
|
|
118
|
+
});
|
|
119
|
+
$('#node-input-password').typedInput({
|
|
120
|
+
default: 'env',
|
|
121
|
+
types: ['msg', 'flow', 'global', 'env'],
|
|
122
|
+
typeField: '#node-input-passwordType',
|
|
123
|
+
});
|
|
124
|
+
$('#node-input-secure').typedInput({
|
|
125
|
+
default: 'bool',
|
|
126
|
+
types: ['bool', 'msg', 'flow', 'global', 'env'],
|
|
127
|
+
typeField: '#node-input-secureType',
|
|
128
|
+
});
|
|
129
|
+
$('#node-input-rejectUnauthorized').typedInput({
|
|
130
|
+
default: 'bool',
|
|
131
|
+
types: ['bool', 'msg', 'flow', 'global', 'env'],
|
|
132
|
+
typeField: '#node-input-rejectUnauthorizedType',
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<script type="text/html" data-template-name="email-sender">
|
|
139
|
+
<div class="form-row">
|
|
140
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
141
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<h3>Mail Configuration</h3>
|
|
145
|
+
<div class="form-row">
|
|
146
|
+
<label for="node-input-sender"><i class="fa fa-user"></i> Sender</label>
|
|
147
|
+
<input type="text" id="node-input-sender" placeholder="John Doe" />
|
|
148
|
+
<input type="hidden" id="node-input-senderType" />
|
|
149
|
+
</div>
|
|
150
|
+
<div class="form-row">
|
|
151
|
+
<label for="node-input-address"><i class="fa fa-envelope"></i> Address</label>
|
|
152
|
+
<input type="text" id="node-input-address" placeholder="john.doe@example.com" />
|
|
153
|
+
<input type="hidden" id="node-input-addressType" />
|
|
154
|
+
</div>
|
|
155
|
+
<div class="form-row">
|
|
156
|
+
<label for="node-input-to"><i class="fa fa-user"></i> To</label>
|
|
157
|
+
<input type="text" id="node-input-to" placeholder="my.test@example.com" />
|
|
158
|
+
<input type="hidden" id="node-input-toType" />
|
|
159
|
+
</div>
|
|
160
|
+
<div class="form-row">
|
|
161
|
+
<label for="node-input-cc"><i class="fa fa-user-plus"></i> CC</label>
|
|
162
|
+
<input type="text" id="node-input-cc" />
|
|
163
|
+
<input type="hidden" id="node-input-ccType" />
|
|
164
|
+
</div>
|
|
165
|
+
<div class="form-row">
|
|
166
|
+
<label for="node-input-bcc"><i class="fa fa-user-secret"></i> BCC</label>
|
|
167
|
+
<input type="text" id="node-input-bcc" />
|
|
168
|
+
<input type="hidden" id="node-input-bccType" />
|
|
169
|
+
</div>
|
|
170
|
+
<div class="form-row">
|
|
171
|
+
<label for="node-input-replyTo"><i class="fa fa-mail-reply"></i> Reply To</label>
|
|
172
|
+
<input type="text" id="node-input-replyTo" />
|
|
173
|
+
<input type="hidden" id="node-input-replyToType" />
|
|
174
|
+
</div>
|
|
175
|
+
<div class="form-row">
|
|
176
|
+
<label for="node-input-subject"><i class="fa fa-info-circle"></i> Subject</label>
|
|
177
|
+
<input type="text" id="node-input-subject" />
|
|
178
|
+
<input type="hidden" id="node-input-subjectType" />
|
|
179
|
+
</div>
|
|
180
|
+
<div class="form-row">
|
|
181
|
+
<label for="node-input-htmlContent"><i class="fa fa-file-code-o"></i> HTML Content</label>
|
|
182
|
+
<input type="text" id="node-input-htmlContent" />
|
|
183
|
+
<input type="hidden" id="node-input-htmlContentType" />
|
|
184
|
+
</div>
|
|
185
|
+
<div class="form-row">
|
|
186
|
+
<label for="node-input-attachments"><i class="fa fa-paperclip"></i> Attachments</label>
|
|
187
|
+
<input type="text" id="node-input-attachments" />
|
|
188
|
+
<input type="hidden" id="node-input-attachmentsType" />
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<h3>SMTP Configuration</h3>
|
|
192
|
+
<div class="form-row">
|
|
193
|
+
<label for="node-input-host"><i class="fa fa-server"></i> Host</label>
|
|
194
|
+
<input type="text" id="node-input-host" placeholder="smtp.gmail.com" />
|
|
195
|
+
<input type="hidden" id="node-input-hostType" />
|
|
196
|
+
</div>
|
|
197
|
+
<div class="form-row">
|
|
198
|
+
<label for="node-input-port"><i class="fa fa-plug"></i> Port</label>
|
|
199
|
+
<input type="text" id="node-input-port" placeholder="587" />
|
|
200
|
+
<input type="hidden" id="node-input-portType" />
|
|
201
|
+
</div>
|
|
202
|
+
<div class="form-row">
|
|
203
|
+
<label for="node-input-user"><i class="fa fa-user"></i> User</label>
|
|
204
|
+
<input type="text" id="node-input-user" placeholder="test.user@config.com" />
|
|
205
|
+
<input type="hidden" id="node-input-userType" />
|
|
206
|
+
</div>
|
|
207
|
+
<div class="form-row">
|
|
208
|
+
<label for="node-input-password"><i class="fa fa-lock"></i> Password</label>
|
|
209
|
+
<input type="text" id="node-input-password" />
|
|
210
|
+
<input type="hidden" id="node-input-passwordType" />
|
|
211
|
+
</div>
|
|
212
|
+
<div class="form-row">
|
|
213
|
+
<label for="node-input-secure"><i class="fa fa-shield"></i> SSL/TLS (Secure)</label>
|
|
214
|
+
<input type="text" id="node-input-secure" />
|
|
215
|
+
<input type="hidden" id="node-input-secureType" />
|
|
216
|
+
</div>
|
|
217
|
+
<div class="form-row">
|
|
218
|
+
<label for="node-input-rejectUnauthorized"><i class="fa fa-ban"></i> Reject Unauthorized</label>
|
|
219
|
+
<input type="text" id="node-input-rejectUnauthorized" />
|
|
220
|
+
<input type="hidden" id="node-input-rejectUnauthorizedType" />
|
|
221
|
+
</div>
|
|
222
|
+
</script>
|
|
223
|
+
|
|
224
|
+
<script type="text/html" data-help-name="email-sender">
|
|
225
|
+
<p>
|
|
226
|
+
A custom Node-RED node that simplifies sending emails using an SMTP server.
|
|
227
|
+
</p>
|
|
228
|
+
|
|
229
|
+
<h3>Description</h3>
|
|
230
|
+
<p>
|
|
231
|
+
The <b>email-sender</b> node is designed to send emails from your Node-RED flows. It supports a wide range of features, including sending HTML content, adding attachments, and using flexible configurations for all email and SMTP settings. All fields are <b>typed inputs</b>, meaning you can pull values from different sources like <code>msg</code> properties, <code>flow</code>/<code>global</code> context, or environment variables.
|
|
232
|
+
</p>
|
|
233
|
+
|
|
234
|
+
<h3>Inputs</h3>
|
|
235
|
+
<p>
|
|
236
|
+
This node accepts a message (<code>msg</code>) as input. The node is triggered when it receives a message. The properties of the incoming message can be used to set the email's configuration fields.
|
|
237
|
+
</p>
|
|
238
|
+
|
|
239
|
+
<h3>Outputs</h3>
|
|
240
|
+
<p>
|
|
241
|
+
This node sends one message upon successful execution.
|
|
242
|
+
</p>
|
|
243
|
+
<dl class="message-properties">
|
|
244
|
+
<dt>payload <span class="property-type">any</span></dt>
|
|
245
|
+
<dd>The <code>payload</code> of the original message is passed through to the next node.</dd>
|
|
246
|
+
<dt>topic <span class="property-type">string</span></dt>
|
|
247
|
+
<dd>The <code>topic</code> of the original message is passed through to the next node.</dd>
|
|
248
|
+
</dl>
|
|
249
|
+
<p>The node's status will be updated to show whether the email was sent successfully.</p>
|
|
250
|
+
|
|
251
|
+
<h3>Configuration</h3>
|
|
252
|
+
<p>
|
|
253
|
+
All configuration fields are <b>typed inputs</b>, allowing you to set a <b>static value</b> or pull data dynamically from a <b>message</b>, <b>flow</b>, <b>global</b> context, or <b>environment variable</b>.
|
|
254
|
+
</p>
|
|
255
|
+
|
|
256
|
+
<h4>Mail Configuration</h4>
|
|
257
|
+
<dl class="message-properties">
|
|
258
|
+
<dt>Name <i>(Optional)</i> <span class="property-type">string</span></dt>
|
|
259
|
+
<dd>A descriptive name for the node.</dd>
|
|
260
|
+
<dt>Sender <span class="property-type">string | variable</span></dt>
|
|
261
|
+
<dd>The name displayed as the sender to the recipient.</dd>
|
|
262
|
+
<dt>Address <span class="property-type">string | variable</span></dt>
|
|
263
|
+
<dd>The email address from which the email is sent.</dd>
|
|
264
|
+
<dt>To <span class="property-type">string | variable</span></dt>
|
|
265
|
+
<dd>The primary recipient(s) of the email. For multiple recipients, separate addresses with a comma.</dd>
|
|
266
|
+
<dt>CC <i>(Optional)</i> <span class="property-type">string | variable</span></dt>
|
|
267
|
+
<dd>Carbon copy recipient(s). Multiple addresses should be separated by commas.</dd>
|
|
268
|
+
<dt>BCC <i>(Optional)</i> <span class="property-type">string | variable</span></dt>
|
|
269
|
+
<dd>Blind carbon copy recipient(s). Multiple addresses should be separated by commas.</dd>
|
|
270
|
+
<dt>Reply To <i>(Optional)</i> <span class="property-type">string | variable</span></dt>
|
|
271
|
+
<dd>The email address that is pre-filled when the recipient replies to the email.</dd>
|
|
272
|
+
<dt>Subject <span class="property-type">string | variable</span></dt>
|
|
273
|
+
<dd>The subject line of the email.</dd>
|
|
274
|
+
<dt>HTML Content <span class="property-type">string | variable</span></dt>
|
|
275
|
+
<dd>The full HTML body of the email.</dd>
|
|
276
|
+
<dt>Attachments <i>(Optional)</i> <span class="property-type">array | variable</span></dt>
|
|
277
|
+
<dd>A list of file attachments. This field should be an array of attachment objects. Each object must contain <code>filename</code>, <code>content</code> (the file's data), and optionally <code>contentType</code>.</dd>
|
|
278
|
+
</dl>
|
|
279
|
+
|
|
280
|
+
<h4>SMTP Configuration</h4>
|
|
281
|
+
<dl class="message-properties">
|
|
282
|
+
<dt>Host <span class="property-type">string | variable</span></dt>
|
|
283
|
+
<dd>The hostname of your SMTP server (e.g., <code>smtp.gmail.com</code>).</dd>
|
|
284
|
+
<dt>Port <span class="property-type">number | variable</span></dt>
|
|
285
|
+
<dd>The port number for the SMTP server (e.g., <code>587</code> or <code>465</code>).</dd>
|
|
286
|
+
<dt>User <span class="property-type">string | variable</span></dt>
|
|
287
|
+
<dd>The username for SMTP authentication.</dd>
|
|
288
|
+
<dt>Password <span class="property-type">password | variable</span></dt>
|
|
289
|
+
<dd>The password for SMTP authentication. This is a secure field and its value will not be shown after deployment.</dd>
|
|
290
|
+
<dt>SSL/TLS (Secure) <span class="property-type">boolean | variable</span></dt>
|
|
291
|
+
<dd>Enables a secure connection. Set this to <code>true</code> to use SSL/TLS encryption.</dd>
|
|
292
|
+
<dt>Reject Unauthorized <span class="property-type">boolean | variable</span></dt>
|
|
293
|
+
<dd>If <code>true</code>, the server's certificate is rejected if it isn't authorized by a trusted Certificate Authority (CA). Set this to <code>false</code> only if you know the server's certificate is self-signed or not from a trusted CA.</dd>
|
|
294
|
+
</dl>
|
|
295
|
+
|
|
296
|
+
<h3>Usage Notes</h3>
|
|
297
|
+
<ul>
|
|
298
|
+
<li><b>Dynamic Content</b>: You can use a <b>Template node</b> before the <b>email-sender</b> node to generate dynamic HTML content for your emails, using <code>msg</code> properties to fill in placeholders.</li>
|
|
299
|
+
<li><b>Testing</b>: When testing, use a free service like <a href="https://mailtrap.io" target="_blank">Mailtrap</a> to avoid sending real emails. This allows you to inspect the email and its content without cluttering your inbox.</li>
|
|
300
|
+
<li><b>Troubleshooting</b>: If you encounter a <code>Missing required IMAP config</code> error, double-check that all required fields are correctly configured and have values.</li>
|
|
301
|
+
</ul>
|
|
302
|
+
|
|
303
|
+
<h3>Example</h3>
|
|
304
|
+
<p>A basic flow could look like this:</p>
|
|
305
|
+
<code>Inject Node</code> ➡️ <code>Change Node</code> ➡️ <code>email-sender Node</code>
|
|
306
|
+
<p>In the <b>Change Node</b>, you can set <code>msg.payload</code> to your email's content and <code>msg.topic</code> to the subject. The <b>email-sender</b> node can then be configured to use these message properties as its input.</p>
|
|
307
|
+
</script>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
'use strict';
|
|
3
|
+
const nodemailer = require('nodemailer');
|
|
4
|
+
|
|
5
|
+
function EmailSenderNode(config) {
|
|
6
|
+
RED.nodes.createNode(this, config);
|
|
7
|
+
var node = this;
|
|
8
|
+
|
|
9
|
+
node.on('input', function (msg, send, done) {
|
|
10
|
+
send =
|
|
11
|
+
send ||
|
|
12
|
+
function () {
|
|
13
|
+
node.send.apply(node, arguments);
|
|
14
|
+
};
|
|
15
|
+
done =
|
|
16
|
+
done ||
|
|
17
|
+
function (err) {
|
|
18
|
+
if (err) node.error(err, msg);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Retrieve and evaluate mail configuration values
|
|
22
|
+
const sender = RED.util.evaluateNodeProperty(config.sender, config.senderType, node, msg);
|
|
23
|
+
const address = RED.util.evaluateNodeProperty(config.address, config.addressType, node, msg);
|
|
24
|
+
const to = RED.util.evaluateNodeProperty(config.to, config.toType, node, msg);
|
|
25
|
+
const cc = RED.util.evaluateNodeProperty(config.cc, config.ccType, node, msg) || '';
|
|
26
|
+
const bcc = RED.util.evaluateNodeProperty(config.bcc, config.bccType, node, msg) || '';
|
|
27
|
+
const replyTo = RED.util.evaluateNodeProperty(config.replyTo, config.replyToType, node, msg) || '';
|
|
28
|
+
const subject =
|
|
29
|
+
RED.util.evaluateNodeProperty(config.subject, config.subjectType, node, msg) ||
|
|
30
|
+
msg.topic ||
|
|
31
|
+
'Message from Node-RED';
|
|
32
|
+
const htmlContent = RED.util.evaluateNodeProperty(config.htmlContent, config.htmlContentType, node, msg);
|
|
33
|
+
const attachments = safeEvaluatePropertyAttachment(config, node, msg);
|
|
34
|
+
|
|
35
|
+
// Retrieve and evaluate SMTP configuration values
|
|
36
|
+
const host = RED.util.evaluateNodeProperty(config.host, config.hostType, node, msg);
|
|
37
|
+
const port = RED.util.evaluateNodeProperty(config.port, config.portType, node, msg);
|
|
38
|
+
const user = RED.util.evaluateNodeProperty(config.user, config.userType, node, msg);
|
|
39
|
+
const password = RED.util.evaluateNodeProperty(config.password, config.passwordType, node, msg);
|
|
40
|
+
const secure = RED.util.evaluateNodeProperty(config.secure, config.secureType, node, msg);
|
|
41
|
+
const rejectUnauthorized = RED.util.evaluateNodeProperty(
|
|
42
|
+
config.rejectUnauthorized,
|
|
43
|
+
config.rejectUnauthorizedType,
|
|
44
|
+
node,
|
|
45
|
+
msg,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Handle attachments and format them for Nodemailer
|
|
49
|
+
let processedAttachments = [];
|
|
50
|
+
|
|
51
|
+
let parsedAttachments = config.attachments;
|
|
52
|
+
|
|
53
|
+
if (config.attachmentsType === 'json' && typeof parsedAttachments === 'string') {
|
|
54
|
+
try {
|
|
55
|
+
parsedAttachments = JSON.parse(parsedAttachments);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
node.error('Failed to parse attachments JSON: ' + e.message);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (parsedAttachments) {
|
|
63
|
+
// Check if it's a single attachment or an array
|
|
64
|
+
const attachmentArray = Array.isArray(parsedAttachments) ? parsedAttachments : [parsedAttachments];
|
|
65
|
+
|
|
66
|
+
for (const attachment of attachmentArray) {
|
|
67
|
+
try {
|
|
68
|
+
// Assuming the attachment object has a 'filename' and 'content' property
|
|
69
|
+
if (attachment.filename && attachment.content) {
|
|
70
|
+
processedAttachments.push({
|
|
71
|
+
filename: attachment.filename,
|
|
72
|
+
content: attachment.content,
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
node.status({ fill: 'red', shape: 'dot', text: 'attachment error' });
|
|
76
|
+
node.error("Attachment object is missing 'filename' or 'content' property.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
node.error('Failed to process attachment: ' + e.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create SMTP transporter
|
|
86
|
+
const transporter = nodemailer.createTransport({
|
|
87
|
+
host: host,
|
|
88
|
+
port: port,
|
|
89
|
+
secure: secure,
|
|
90
|
+
auth: {
|
|
91
|
+
user: user,
|
|
92
|
+
pass: password,
|
|
93
|
+
},
|
|
94
|
+
tls: {
|
|
95
|
+
rejectUnauthorized: rejectUnauthorized,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Create email object
|
|
100
|
+
const mailOptions = {
|
|
101
|
+
from: {
|
|
102
|
+
name: sender,
|
|
103
|
+
address: address,
|
|
104
|
+
},
|
|
105
|
+
to: to,
|
|
106
|
+
cc: cc,
|
|
107
|
+
bcc: bcc,
|
|
108
|
+
replyTo: replyTo,
|
|
109
|
+
subject: subject,
|
|
110
|
+
html: Buffer.from(htmlContent, 'utf-8'),
|
|
111
|
+
attachments: processedAttachments,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Send email
|
|
115
|
+
transporter.sendMail(mailOptions, (error, info) => {
|
|
116
|
+
if (error) {
|
|
117
|
+
node.status({ fill: 'red', shape: 'dot', text: 'error sending' });
|
|
118
|
+
if (
|
|
119
|
+
error.message &&
|
|
120
|
+
error.message.includes('SSL routines') &&
|
|
121
|
+
error.message.includes('wrong version number')
|
|
122
|
+
) {
|
|
123
|
+
// Improved error message for SSL/TLS issues
|
|
124
|
+
done(
|
|
125
|
+
new Error(
|
|
126
|
+
'SSL/TLS connection failed: Wrong version number. ' +
|
|
127
|
+
'This usually means the wrong port or security settings are used. ' +
|
|
128
|
+
'For SMTP: use port 587 with secure=false (STARTTLS) or port 465 with secure=true (SSL/TLS).',
|
|
129
|
+
),
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
done(error);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
node.log('Email sent: ' + info.response);
|
|
136
|
+
msg.payload = info;
|
|
137
|
+
|
|
138
|
+
if (msg.payload.accepted && msg.payload.accepted.length > 0) {
|
|
139
|
+
msg.payload = msg.input;
|
|
140
|
+
node.status({ fill: 'green', shape: 'dot', text: 'sent' });
|
|
141
|
+
send(msg);
|
|
142
|
+
done();
|
|
143
|
+
} else if (msg.payload.rejected && msg.payload.rejected.length > 0) {
|
|
144
|
+
msg.error = { result: msg.payload.rejected };
|
|
145
|
+
node.status({ fill: 'red', shape: 'dot', text: 'rejected' });
|
|
146
|
+
done(new Error('Email rejected: ' + msg.payload.rejected.join(', ')));
|
|
147
|
+
} else if (msg.payload.pending && msg.payload.pending.length > 0) {
|
|
148
|
+
msg.error = { result: msg.payload.pending };
|
|
149
|
+
node.status({ fill: 'yellow', shape: 'dot', text: 'pending' });
|
|
150
|
+
done(new Error('Email pending: ' + msg.payload.pending.join(', ')));
|
|
151
|
+
} else {
|
|
152
|
+
node.status({ fill: 'red', shape: 'dot', text: 'unknown error' });
|
|
153
|
+
done(new Error('Unknown error while sending email.'));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function safeEvaluatePropertyAttachment(config, node, msg) {
|
|
161
|
+
if (config.attachments && config.attachments.trim() !== '') {
|
|
162
|
+
try {
|
|
163
|
+
return RED.util.evaluateNodeProperty(config.attachments, config.attachmentsType, node, msg);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
node.error('Failed to evaluate attachments property: ' + e.message, msg);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
RED.nodes.registerType('email-sender', EmailSenderNode, {
|
|
174
|
+
credentials: {
|
|
175
|
+
password: { type: 'password' },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@5minds/node-red-contrib-processcube-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2-feature-8805f0-mfnm2ko3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Node-RED tools nodes for ProcessCube",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"lint": "prettier --write --config ./.prettierrc.json \"**/*.{html,js}\""
|
|
7
|
+
"lint": "prettier --write --config ./.prettierrc.json \"**/*.{html,js}\"",
|
|
8
|
+
"test": "mocha test/unit/ test/integration"
|
|
8
9
|
},
|
|
9
10
|
"authors": [
|
|
10
11
|
{
|
|
@@ -14,6 +15,10 @@
|
|
|
14
15
|
{
|
|
15
16
|
"name": "Robin Lenz",
|
|
16
17
|
"email": "Robin.Lenz@5Minds.de"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "Diana Stefan",
|
|
21
|
+
"email": "Diana.Stefan@5Minds.de"
|
|
17
22
|
}
|
|
18
23
|
],
|
|
19
24
|
"repository": {
|
|
@@ -31,12 +36,28 @@
|
|
|
31
36
|
"node-red": {
|
|
32
37
|
"version": ">=3.1.9",
|
|
33
38
|
"nodes": {
|
|
34
|
-
"
|
|
39
|
+
"EmailReceiver": "email-receiver/email-receiver.js",
|
|
40
|
+
"EmailSender": "email-sender/email-sender.js",
|
|
41
|
+
"HtmlToText": "processcube-html-to-text/processcube-html-to-text.js"
|
|
35
42
|
},
|
|
36
43
|
"examples": "examples"
|
|
37
44
|
},
|
|
38
45
|
"dependencies": {
|
|
39
|
-
"html-to-text": "^9.0.5"
|
|
46
|
+
"html-to-text": "^9.0.5",
|
|
47
|
+
"mailparser": "^3.6.8",
|
|
48
|
+
"node-imap": "^0.9.6",
|
|
49
|
+
"nodemailer": "^7.0.6",
|
|
50
|
+
"utf7": "^1.0.2"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"chai": "^4.3.4",
|
|
54
|
+
"mocha": "^11.7.2",
|
|
55
|
+
"node-red": "^4.0.9",
|
|
56
|
+
"node-red-node-test-helper": "^0.3.5"
|
|
57
|
+
},
|
|
58
|
+
"overrides": {
|
|
59
|
+
"semver": ">=7.5.2",
|
|
60
|
+
"axios": ">=1.12.0"
|
|
40
61
|
},
|
|
41
62
|
"keywords": [
|
|
42
63
|
"node-red",
|
package/{processcube-html-to-text.html → processcube-html-to-text/processcube-html-to-text.html}
RENAMED
|
@@ -22,20 +22,20 @@
|
|
|
22
22
|
</script>
|
|
23
23
|
|
|
24
24
|
<script type="text/markdown" data-help-name="processcube-google-docs-mail-template">
|
|
25
|
-
# Html 2 Text
|
|
25
|
+
# Html 2 Text
|
|
26
26
|
|
|
27
|
-
## Inputs
|
|
27
|
+
## Inputs
|
|
28
28
|
|
|
29
|
-
: payload (String) : html content to convert to text
|
|
29
|
+
: payload (String) : html content to convert to text
|
|
30
30
|
|
|
31
|
-
## Outputs
|
|
31
|
+
## Outputs
|
|
32
32
|
|
|
33
|
-
: payload (String) : text content
|
|
33
|
+
: payload (String) : text content
|
|
34
34
|
|
|
35
|
-
---
|
|
35
|
+
---
|
|
36
36
|
|
|
37
|
-
## References
|
|
37
|
+
## References
|
|
38
38
|
|
|
39
|
-
- [The ProcessCube Developer Network](https://processcube.io) – All documentation for the ProcessCube© platform
|
|
40
|
-
- [Node-RED Integration in ProcessCube©](https://processcube.io/docs/node-red) – Node-RED integration in ProcessCube©
|
|
39
|
+
- [The ProcessCube Developer Network](https://processcube.io) – All documentation for the ProcessCube© platform
|
|
40
|
+
- [Node-RED Integration in ProcessCube©](https://processcube.io/docs/node-red) – Node-RED integration in ProcessCube©
|
|
41
41
|
</script>
|
package/{processcube-html-to-text.js → processcube-html-to-text/processcube-html-to-text.js}
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
|
|
3
2
|
const { compile } = require('html-to-text');
|
|
4
3
|
|
|
5
4
|
function ProcesscubeHtmlToText(config) {
|
|
@@ -12,9 +11,7 @@ module.exports = function (RED) {
|
|
|
12
11
|
};
|
|
13
12
|
const compiledConvert = compile(options); // options passed here
|
|
14
13
|
|
|
15
|
-
|
|
16
14
|
node.on('input', async function (msg) {
|
|
17
|
-
|
|
18
15
|
msg.payload = compiledConvert(msg.payload);
|
|
19
16
|
|
|
20
17
|
node.send(msg);
|