@feardread/fear 1.1.5 → 1.1.7
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/FEAR.js +76 -63
- package/FEARServer.js +5 -6
- package/controllers/address.js +9 -0
- package/controllers/auth/index.js +499 -92
- package/controllers/order.js +0 -1
- package/controllers/payment.js +5 -185
- package/libs/db/index.js +5 -0
- package/libs/emailer/info.js +22 -34
- package/libs/emailer/smtp.js +511 -65
- package/libs/passport/index.js +137 -0
- package/libs/passport.js +22 -0
- package/libs/paypal/index.js +82 -0
- package/libs/stripe/index.js +306 -0
- package/libs/validator/index.js +2 -2
- package/models/address.js +37 -0
- package/models/order.js +29 -154
- package/models/payment.js +18 -79
- package/models/user.js +116 -51
- package/package.json +1 -1
- package/routes/address.js +16 -0
- package/routes/auth.js +6 -0
- package/routes/mail.js +10 -165
- package/routes/order.js +7 -4
- package/routes/payment.js +4 -8
- package/routes/paypal.js +12 -0
- package/routes/stripe.js +27 -0
- package/libs/passport/passport.js +0 -109
package/libs/emailer/smtp.js
CHANGED
|
@@ -1,77 +1,523 @@
|
|
|
1
1
|
const nodemailer = require("nodemailer");
|
|
2
|
+
const FormData = require("form-data");
|
|
3
|
+
const Mailgun = require("mailgun");
|
|
2
4
|
|
|
3
|
-
module.exports =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
module.exports = function (fear) {
|
|
6
|
+
const _this = {};
|
|
7
|
+
const logger = fear.getLogger();
|
|
8
|
+
|
|
9
|
+
_this.mailConfig = fear.mailinfo || {};
|
|
10
|
+
_this.mailService = fear.mailinfo?.service || 'smtp';
|
|
11
|
+
_this.transporter = null;
|
|
12
|
+
_this.mailgunClient = null;
|
|
13
|
+
|
|
14
|
+
// Initialize the appropriate mail service
|
|
15
|
+
_this.initializeMailService = () => {
|
|
16
|
+
if (_this.mailService === 'mailgun') {
|
|
17
|
+
if (!_this.mailConfig.mailgun) {
|
|
18
|
+
throw new Error('Missing Mailgun configuration. Please update mail configuration.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { apiKey, domain, region } = _this.mailConfig.mailgun;
|
|
22
|
+
|
|
23
|
+
if (!apiKey || !domain) {
|
|
24
|
+
throw new Error('Mailgun requires apiKey and domain in configuration.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const mailgun = new Mailgun(FormData);
|
|
28
|
+
const clientOptions = {
|
|
29
|
+
username: 'api',
|
|
30
|
+
key: apiKey
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Add EU endpoint if region is specified
|
|
34
|
+
if (region === 'EU') {
|
|
35
|
+
clientOptions.url = 'https://api.eu.mailgun.net';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_this.mailgunClient = mailgun.client(clientOptions);
|
|
39
|
+
_this.mailgunDomain = domain;
|
|
40
|
+
|
|
41
|
+
logger.info('Mailgun client initialized successfully.');
|
|
42
|
+
} else {
|
|
43
|
+
// Default SMTP setup with nodemailer
|
|
44
|
+
if (!_this.mailConfig.smtp || !_this.mailConfig.smtp[_this.mailService]) {
|
|
45
|
+
throw new Error(`Missing mail configuration for service: ${_this.mailService}. Please update mail configuration.`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_this.transporter = nodemailer.createTransport(_this.mailConfig.smtp[_this.mailService]);
|
|
49
|
+
_this.transporter.verify()
|
|
50
|
+
.then(() => logger.info('Mail transport setup complete.'))
|
|
51
|
+
.catch((error) => logger.error('Error loading mail transport :: ', error));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Initialize on module load
|
|
56
|
+
_this.initializeMailService();
|
|
57
|
+
|
|
58
|
+
_this.isValidEmail = (email) => {
|
|
59
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
60
|
+
return emailRegex.test(email);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
_this.validateEmailOptions = (options) => {
|
|
64
|
+
const errors = [];
|
|
65
|
+
|
|
66
|
+
if (!options.to) errors.push('Recipient email (to) is required');
|
|
67
|
+
if (!options.subject) errors.push('Subject is required');
|
|
68
|
+
if (!options.html && !options.text) errors.push('Email content (html or text) is required');
|
|
69
|
+
|
|
70
|
+
if (options.to && !_this.isValidEmail(options.to)) {
|
|
71
|
+
errors.push('Invalid recipient email address');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (options.from && !_this.isValidEmail(options.from)) {
|
|
75
|
+
errors.push('Invalid sender email address');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
isValid: errors.length === 0,
|
|
80
|
+
errors
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
_this.sendViaMailgun = (options) => {
|
|
85
|
+
const messageData = {
|
|
86
|
+
from: options.from,
|
|
87
|
+
to: Array.isArray(options.to) ? options.to : [options.to],
|
|
88
|
+
subject: options.subject,
|
|
89
|
+
text: options.text,
|
|
90
|
+
html: options.html
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Add reply-to if specified
|
|
94
|
+
if (options.replyTo) {
|
|
95
|
+
messageData['h:Reply-To'] = options.replyTo;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add CC if specified
|
|
99
|
+
if (options.cc) {
|
|
100
|
+
messageData.cc = Array.isArray(options.cc) ? options.cc : [options.cc];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Add BCC if specified
|
|
104
|
+
if (options.bcc) {
|
|
105
|
+
messageData.bcc = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return _this.mailgunClient.messages.create(_this.mailgunDomain, messageData)
|
|
109
|
+
.then((data) => {
|
|
110
|
+
logger.info(`Email sent successfully via Mailgun :: messageId: ${data.id}`);
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
message: 'Email sent successfully',
|
|
114
|
+
messageId: data.id,
|
|
115
|
+
response: data
|
|
116
|
+
};
|
|
117
|
+
})
|
|
118
|
+
.catch((error) => {
|
|
119
|
+
logger.error('Error sending email via Mailgun :: ', error);
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
message: error.message || 'Failed to send email',
|
|
123
|
+
error: error
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
_this.sendViaSMTP = (options) => {
|
|
129
|
+
return _this.transporter.sendMail(options)
|
|
130
|
+
.then((info) => {
|
|
131
|
+
if (!info.messageId) {
|
|
132
|
+
logger.warn('Email sent but messageId not found :: ', info);
|
|
133
|
+
}
|
|
134
|
+
logger.info(`Email sent successfully via SMTP :: messageId: ${info.messageId}`);
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
message: 'Email sent successfully',
|
|
138
|
+
messageId: info.messageId
|
|
139
|
+
};
|
|
140
|
+
})
|
|
141
|
+
.catch((error) => {
|
|
142
|
+
logger.error('Error sending email via SMTP :: ', error);
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
message: error.message || 'Failed to send email',
|
|
146
|
+
error: error
|
|
147
|
+
};
|
|
23
148
|
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
_this.sendEmail = (options) => {
|
|
152
|
+
const validation = _this.validateEmailOptions(options);
|
|
153
|
+
|
|
154
|
+
if (!validation.isValid) {
|
|
155
|
+
return Promise.reject(new Error(`Validation failed: ${validation.errors.join(', ')}`));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Set default from address based on service
|
|
159
|
+
let defaultFrom;
|
|
160
|
+
if (_this.mailService === 'mailgun') {
|
|
161
|
+
defaultFrom = _this.mailConfig.mailgun.from || `noreply@${_this.mailgunDomain}`;
|
|
162
|
+
} else {
|
|
163
|
+
defaultFrom = _this.mailConfig.smtp[_this.mailService].auth.user;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const emailOptions = {
|
|
167
|
+
from: defaultFrom,
|
|
168
|
+
...options
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Route to appropriate sending method
|
|
172
|
+
if (_this.mailService === 'mailgun') {
|
|
173
|
+
return _this.sendViaMailgun(emailOptions);
|
|
174
|
+
} else {
|
|
175
|
+
return _this.sendViaSMTP(emailOptions);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
_this.handleError = (res, statusCode, error) => {
|
|
180
|
+
logger.error(`E-Mailer Error :: `, error);
|
|
181
|
+
return res.status(statusCode).json({
|
|
182
|
+
success: false,
|
|
183
|
+
message: error.message || 'An error occurred',
|
|
184
|
+
error
|
|
24
185
|
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
_this.getDefaultFromAddress = () => {
|
|
189
|
+
if (_this.mailService === 'mailgun') {
|
|
190
|
+
return _this.mailConfig.mailgun.from || `noreply@${_this.mailgunDomain}`;
|
|
191
|
+
}
|
|
192
|
+
return _this.mailConfig.smtp[_this.mailService].auth.user;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
_this.templates = {
|
|
196
|
+
baseTemplate(content, title = "Email") {
|
|
197
|
+
return `
|
|
198
|
+
<!DOCTYPE html>
|
|
199
|
+
<html lang="en">
|
|
200
|
+
<head>
|
|
201
|
+
<meta charset="UTF-8">
|
|
202
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
203
|
+
<title>${title}</title>
|
|
204
|
+
<style>
|
|
205
|
+
body {
|
|
206
|
+
margin: 0;
|
|
207
|
+
padding: 0;
|
|
208
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
209
|
+
line-height: 1.6;
|
|
210
|
+
color: #333;
|
|
211
|
+
background-color: #f4f4f4;
|
|
212
|
+
}
|
|
213
|
+
.container {
|
|
214
|
+
max-width: 600px;
|
|
215
|
+
margin: 40px auto;
|
|
216
|
+
background: #ffffff;
|
|
217
|
+
border-radius: 8px;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
220
|
+
}
|
|
221
|
+
.header {
|
|
222
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
223
|
+
color: white;
|
|
224
|
+
padding: 30px;
|
|
225
|
+
text-align: center;
|
|
226
|
+
}
|
|
227
|
+
.content {
|
|
228
|
+
padding: 40px 30px;
|
|
229
|
+
}
|
|
230
|
+
.field {
|
|
231
|
+
margin-bottom: 20px;
|
|
232
|
+
padding-bottom: 20px;
|
|
233
|
+
border-bottom: 1px solid #eee;
|
|
234
|
+
}
|
|
235
|
+
.field:last-child {
|
|
236
|
+
border-bottom: none;
|
|
237
|
+
}
|
|
238
|
+
.label {
|
|
239
|
+
font-weight: 600;
|
|
240
|
+
color: #667eea;
|
|
241
|
+
font-size: 12px;
|
|
242
|
+
text-transform: uppercase;
|
|
243
|
+
letter-spacing: 0.5px;
|
|
244
|
+
margin-bottom: 5px;
|
|
245
|
+
}
|
|
246
|
+
.value {
|
|
247
|
+
color: #333;
|
|
248
|
+
font-size: 16px;
|
|
249
|
+
}
|
|
250
|
+
.footer {
|
|
251
|
+
background: #f8f9fa;
|
|
252
|
+
padding: 20px 30px;
|
|
253
|
+
text-align: center;
|
|
254
|
+
color: #666;
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
}
|
|
257
|
+
.message-box {
|
|
258
|
+
background: #f8f9fa;
|
|
259
|
+
padding: 20px;
|
|
260
|
+
border-radius: 6px;
|
|
261
|
+
margin-top: 10px;
|
|
262
|
+
}
|
|
263
|
+
</style>
|
|
264
|
+
</head>
|
|
265
|
+
<body>
|
|
266
|
+
<div class="container">
|
|
267
|
+
${content}
|
|
268
|
+
</div>
|
|
269
|
+
</body>
|
|
270
|
+
</html>
|
|
271
|
+
`;
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
projectTemplate(data) {
|
|
275
|
+
const { fullname, company, email, phone, budget, about } = data;
|
|
276
|
+
|
|
277
|
+
const content = `
|
|
278
|
+
<div class="header">
|
|
279
|
+
<h1 style="margin: 0; font-size: 28px;">New Project Inquiry</h1>
|
|
280
|
+
<p style="margin: 10px 0 0 0; opacity: 0.9;">You have received a new project submission</p>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="content">
|
|
283
|
+
<div class="field">
|
|
284
|
+
<div class="label">Full Name</div>
|
|
285
|
+
<div class="value">${fullname || 'Not provided'}</div>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="field">
|
|
288
|
+
<div class="label">Email Address</div>
|
|
289
|
+
<div class="value"><a href="mailto:${email}" style="color: #667eea;">${email}</a></div>
|
|
290
|
+
</div>
|
|
291
|
+
${company ? `
|
|
292
|
+
<div class="field">
|
|
293
|
+
<div class="label">Company</div>
|
|
294
|
+
<div class="value">${company}</div>
|
|
295
|
+
</div>
|
|
296
|
+
` : ''}
|
|
297
|
+
${phone ? `
|
|
298
|
+
<div class="field">
|
|
299
|
+
<div class="label">Phone Number</div>
|
|
300
|
+
<div class="value"><a href="tel:${phone}" style="color: #667eea;">${phone}</a></div>
|
|
301
|
+
</div>
|
|
302
|
+
` : ''}
|
|
303
|
+
${budget ? `
|
|
304
|
+
<div class="field">
|
|
305
|
+
<div class="label">Budget</div>
|
|
306
|
+
<div class="value">${budget}</div>
|
|
307
|
+
</div>
|
|
308
|
+
` : ''}
|
|
309
|
+
${about ? `
|
|
310
|
+
<div class="field">
|
|
311
|
+
<div class="label">Project Details</div>
|
|
312
|
+
<div class="message-box">${about.replace(/\n/g, '<br>')}</div>
|
|
313
|
+
</div>
|
|
314
|
+
` : ''}
|
|
315
|
+
</div>
|
|
316
|
+
<div class="footer">
|
|
317
|
+
<p style="margin: 0;">Received on ${new Date().toLocaleString()}</p>
|
|
318
|
+
</div>
|
|
319
|
+
`;
|
|
320
|
+
|
|
321
|
+
return _this.templates.baseTemplate(content, "New Project Inquiry");
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
contactTemplate(data) {
|
|
325
|
+
const { $email, $message, $name } = data;
|
|
326
|
+
|
|
327
|
+
const content = `
|
|
328
|
+
<div class="header">
|
|
329
|
+
<h1 style="margin: 0; font-size: 28px;">New Contact Message</h1>
|
|
330
|
+
<p style="margin: 10px 0 0 0; opacity: 0.9;">You have received a new message</p>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="content">
|
|
333
|
+
${$name ? `
|
|
334
|
+
<div class="field">
|
|
335
|
+
<div class="label">Name</div>
|
|
336
|
+
<div class="value">${$name}</div>
|
|
337
|
+
</div>
|
|
338
|
+
` : ''}
|
|
339
|
+
<div class="field">
|
|
340
|
+
<div class="label">Email Address</div>
|
|
341
|
+
<div class="value"><a href="mailto:${$email}" style="color: #667eea;">${$email}</a></div>
|
|
342
|
+
</div>
|
|
343
|
+
<div class="field">
|
|
344
|
+
<div class="label">Message</div>
|
|
345
|
+
<div class="message-box">${$message.replace(/\n/g, '<br>')}</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="footer">
|
|
349
|
+
<p style="margin: 0;">Received on ${new Date().toLocaleString()}</p>
|
|
350
|
+
</div>
|
|
351
|
+
`;
|
|
352
|
+
|
|
353
|
+
return _this.templates.baseTemplate(content, "New Contact Message");
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
generatePlainText(data, type = 'contact') {
|
|
357
|
+
if (type === 'project') {
|
|
358
|
+
const { fullname, company, email, phone, budget, about } = data;
|
|
359
|
+
return `
|
|
360
|
+
New Project Inquiry
|
|
361
|
+
===================
|
|
362
|
+
|
|
363
|
+
Full Name: ${fullname || 'Not provided'}
|
|
364
|
+
Email: ${email}
|
|
365
|
+
${company ? `Company: ${company}\n` : ''}${phone ? `Phone: ${phone}\n` : ''}${budget ? `Budget: ${budget}\n` : ''}
|
|
366
|
+
Project Details:
|
|
367
|
+
${about || 'No details provided'}
|
|
368
|
+
|
|
369
|
+
Received: ${new Date().toLocaleString()}
|
|
370
|
+
`.trim();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Contact form
|
|
374
|
+
const { $email, $message, $name } = data;
|
|
375
|
+
return `
|
|
376
|
+
New Contact Message
|
|
377
|
+
===================
|
|
378
|
+
|
|
379
|
+
${$name ? `Name: ${$name}\n` : ''}Email: ${$email}
|
|
380
|
+
|
|
381
|
+
Message:
|
|
382
|
+
${$message}
|
|
383
|
+
|
|
384
|
+
Received: ${new Date().toLocaleString()}
|
|
385
|
+
`.trim();
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
templates: _this.templates,
|
|
391
|
+
sendEmail: _this.sendEmail,
|
|
392
|
+
|
|
393
|
+
sendProjectEmail(req, res) {
|
|
394
|
+
const data = req.body;
|
|
395
|
+
const { $subject, email } = data;
|
|
396
|
+
|
|
397
|
+
if (!email || !_this.isValidEmail(email)) {
|
|
398
|
+
return _this.handleError(res, 400, { message: 'Valid email required' });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const htmlContent = _this.templates.projectTemplate(data);
|
|
402
|
+
const textContent = _this.templates.generatePlainText(data, 'project');
|
|
40
403
|
const options = {
|
|
41
|
-
from:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
404
|
+
from: _this.getDefaultFromAddress(),
|
|
405
|
+
replyTo: email,
|
|
406
|
+
to: _this.getDefaultFromAddress(),
|
|
407
|
+
subject: $subject || 'New Project Inquiry',
|
|
408
|
+
html: htmlContent,
|
|
409
|
+
text: textContent
|
|
45
410
|
};
|
|
46
|
-
|
|
47
|
-
transporter.sendMail(options, function(error, info) {
|
|
48
|
-
if (error) {
|
|
49
|
-
return reject({ message: `An error has occured: ${error}`});
|
|
50
|
-
}
|
|
51
|
-
return resolve({ message: 'Email sent succesfully!'})
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
411
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
412
|
+
return _this.sendEmail(options)
|
|
413
|
+
.then((resp) => {
|
|
414
|
+
if (!resp.success) {
|
|
415
|
+
return _this.handleError(res, 500, resp);
|
|
416
|
+
}
|
|
417
|
+
return res.status(200).json({
|
|
418
|
+
success: true,
|
|
419
|
+
message: 'Project email sent successfully',
|
|
420
|
+
result: resp
|
|
421
|
+
});
|
|
422
|
+
})
|
|
423
|
+
.catch((error) => _this.handleError(res, 500, error));
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
sendContactEmail(req, res) {
|
|
427
|
+
const { $email, $message, $subject } = req.body;
|
|
428
|
+
|
|
429
|
+
if (!$email || !_this.isValidEmail($email)) {
|
|
430
|
+
return _this.handleError(res, 400, { message: 'Valid email is required' });
|
|
431
|
+
}
|
|
432
|
+
if (!$message || $message.trim().length === 0) {
|
|
433
|
+
return _this.handleError(res, 400, { message: 'Message is required' });
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const htmlContent = _this.templates.contactTemplate(req.body);
|
|
437
|
+
const textContent = _this.templates.generatePlainText(req.body, 'contact');
|
|
60
438
|
|
|
61
439
|
const options = {
|
|
62
|
-
from:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
440
|
+
from: _this.getDefaultFromAddress(),
|
|
441
|
+
replyTo: $email,
|
|
442
|
+
to: _this.getDefaultFromAddress(),
|
|
443
|
+
subject: $subject || `Contact Form Message from ${$email}`,
|
|
444
|
+
html: htmlContent,
|
|
445
|
+
text: textContent
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
return _this.sendEmail(options)
|
|
449
|
+
.then((resp) => {
|
|
450
|
+
if (!resp.success) {
|
|
451
|
+
return res.status(500).json(resp);
|
|
452
|
+
}
|
|
453
|
+
return res.status(200).json({
|
|
454
|
+
success: true,
|
|
455
|
+
message: 'Contact email sent successfully',
|
|
456
|
+
result: resp
|
|
457
|
+
});
|
|
458
|
+
})
|
|
459
|
+
.catch((error) => _this.handleError(res, 500, error));
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
sendSubscriptionEmail(req, res) {
|
|
463
|
+
const { email, options = {} } = req.body;
|
|
464
|
+
|
|
465
|
+
if (!email || !_this.isValidEmail(email)) {
|
|
466
|
+
return _this.handleError(res, 400, { message: 'Valid email is required' });
|
|
66
467
|
}
|
|
67
468
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
469
|
+
const { subject, customMessage } = options;
|
|
470
|
+
const htmlContent = `
|
|
471
|
+
<div class="header">
|
|
472
|
+
<h1 style="margin: 0; font-size: 28px;">Welcome! 🎉</h1>
|
|
473
|
+
<p style="margin: 10px 0 0 0; opacity: 0.9;">Thank you for subscribing</p>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="content">
|
|
476
|
+
<p style="font-size: 16px;">
|
|
477
|
+
${customMessage || "You've successfully subscribed to our newsletter. We're excited to have you on board!"}
|
|
478
|
+
</p>
|
|
479
|
+
<p style="font-size: 16px;">
|
|
480
|
+
You'll receive updates and news directly to your inbox.
|
|
481
|
+
</p>
|
|
482
|
+
</div>
|
|
483
|
+
<div class="footer">
|
|
484
|
+
<p style="margin: 0;">If you didn't subscribe, you can safely ignore this email.</p>
|
|
485
|
+
</div>
|
|
486
|
+
`;
|
|
487
|
+
|
|
488
|
+
const emailOptions = {
|
|
489
|
+
to: email,
|
|
490
|
+
subject: subject || 'Welcome to Our Newsletter!',
|
|
491
|
+
html: _this.templates.baseTemplate(htmlContent, 'Subscription Confirmation'),
|
|
492
|
+
text: customMessage || "Thank you for subscribing! You'll receive updates directly to your inbox."
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
return _this.sendEmail(emailOptions)
|
|
496
|
+
.then((resp) => {
|
|
497
|
+
if (!resp.success) {
|
|
498
|
+
return _this.handleError(res, 500, resp);
|
|
499
|
+
}
|
|
500
|
+
return res.status(200).json({
|
|
501
|
+
success: true,
|
|
502
|
+
message: 'Subscription email sent successfully',
|
|
503
|
+
result: resp
|
|
504
|
+
});
|
|
505
|
+
})
|
|
506
|
+
.catch((error) => _this.handleError(res, 500, error));
|
|
507
|
+
},
|
|
508
|
+
sendTestMessage(req, res) {
|
|
509
|
+
const mg = _this.mailgunClient
|
|
510
|
+
|
|
511
|
+
mg.messages.create("sandbox933b4315c0164f209bbf0bc7fb908598.mailgun.org", {
|
|
512
|
+
from: "Mailgun Sandbox <postmaster@sandbox933b4315c0164f209bbf0bc7fb908598.mailgun.org>",
|
|
513
|
+
to: ["Garrett Haptonstall <fear.dread@underworld.dog>"],
|
|
514
|
+
subject: "Hello Garrett Haptonstall",
|
|
515
|
+
text: "Congratulations Garrett Haptonstall, you just sent an email with Mailgun! You are truly awesome!",
|
|
516
|
+
})
|
|
517
|
+
.then((data) => {
|
|
518
|
+
logger.info(data)
|
|
74
519
|
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
520
|
+
.catch(error => logger.error(error));
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
};
|