backend-manager 3.2.32 → 3.2.33

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.
@@ -1,8 +1,3 @@
1
- const _ = require('lodash')
2
- const fetch = require('wonderful-fetch');
3
- const moment = require('moment');
4
-
5
- const MAX_SIGNUPS = 3;
6
1
  const MAX_AGE = 30;
7
2
 
8
3
  function Module() {
@@ -18,528 +13,87 @@ Module.prototype.main = function () {
18
13
  const Api = self.Api;
19
14
  const payload = self.payload;
20
15
 
16
+ const { admin } = Manager.libraries;
17
+
21
18
  Api.resolveUser({adminRequired: true})
22
19
  .then(async (user) => {
23
- // ⛔️⛔️⛔️ This function could be triggered when the user signs up with Google after already having a email/password account
24
- // Get auth user from firebase
25
- const ip = assistant.request.geolocation.ip;
26
- const authUser = await Manager.libraries.admin.auth().getUser(user.auth.uid).catch(e => e);
27
- const usage = await Manager.Usage().init(assistant, {log: true, key: ip});
28
-
29
- if (authUser instanceof Error) {
30
- return reject(assistant.errorify(`Failed to get auth user: ${authUser}`, {code: 500}));
31
- }
32
-
33
- // Age in seconds
34
- const ageInSeconds = (Date.now() - new Date(authUser.metadata.creationTime)) / 1000;
35
-
36
- // If the user is not new, reject
37
- if (ageInSeconds >= MAX_AGE) {
38
- return reject(assistant.errorify(`User is not new.`, {code: 400}));
39
- }
40
-
41
- // Check if IP has signed up too many times
42
- const signups = usage.getUsage('signups');
43
-
44
- // Log the signup
45
- assistant.log(`Validating signups ${signups}/${MAX_SIGNUPS} for ip ${ip}`, user);
46
-
47
- // If over limit, reject and delete the user
48
- if (signups >= MAX_SIGNUPS) {
49
- await Api.import('user:delete')
50
- .then(async (lib) => {
51
- await lib.main().catch(e => e);
52
- })
53
-
54
- await self.sendRateEmail(user).catch(e => e);
55
-
56
- // Reject
57
- return reject(assistant.errorify(`Too many signups from this IP (${ip}).`, {code: 429}));
58
- }
59
-
60
- // Increment signups
61
- usage.increment('signups');
62
-
63
- // Update signups
64
- await usage.update();
65
-
66
- // Send welcome email
67
- await self.sendWelcomeEmail(user).catch(e => e);
68
- await self.sendCheckupEmail(user).catch(e => e);
69
- await self.sendFeedbackEmail(user).catch(e => e);
70
-
71
- await self.signUp({
72
- auth: {
73
- uid: user?.auth?.uid,
74
- email: user?.auth?.email,
75
- },
76
- affiliate: {
77
- referrer: payload?.data?.payload?.affiliateCode || null,
78
- },
79
- })
80
- .then(async function (result) {
81
- // Skip if not a newsletter sign up
82
- if (!payload?.data?.payload?.newsletterSignUp) {
83
- return resolve({data: result});
84
- }
85
-
86
- // Add to SendGrid list
87
- await self.addToSendGridList(user)
88
- .then((r) => {
89
- assistant.log('addToSendGridList(): Success', r)
90
- })
91
- .catch((e) => {
92
- assistant.log('Failed to add user to MC list.', e)
93
- })
94
-
95
- // Resolve
96
- return resolve({data: result});
97
- })
98
- .catch((e) => {
99
- return reject(assistant.errorify(`Failed to sign up: ${e}`, {code: 500}));
100
- })
101
- })
102
- .catch((e) => {
103
- return reject(e);
104
- })
105
-
106
- });
107
-
108
- };
109
-
110
- Module.prototype.signUp = function (payload) {
111
- const self = this;
112
-
113
- return new Promise(async function(resolve, reject) {
114
- const Manager = self.Manager;
115
- const assistant = self.assistant;
116
- const Api = self.Api;
20
+ // ⛔️⛔️⛔️ This function could be triggered when the user signs up with Google after already having a email/password account
21
+ // Get auth user from firebase
22
+ const authUser = await admin.auth().getUser(user.auth.uid).catch(e => e);
117
23
 
118
- const result = {
119
- signedUp: false,
120
- referrerUid: null,
121
- // updatedReferral: true,
122
- };
123
-
124
- payload = payload || {};
125
-
126
- assistant.log(`signUp(): payload`, payload)
127
-
128
- // Check if the user has a UID and email
129
- if (!payload?.auth?.uid || !payload?.auth?.email) {
130
- return reject(new Error('Cannot create user without UID and email.'))
131
- }
132
-
133
- // Update the user who referred this user
134
- await self.updateReferral({
135
- affiliateCode: _.get(payload, 'affiliate.referrer', null),
136
- uid: payload.auth.uid,
137
- })
138
- .then((r) => {
139
- payload.affiliate.referrer = r.referrerUid;
140
- result.referrerUid = payload.affiliate.referrer;
141
- })
142
- .catch((e) => {
143
- payload.affiliate.referrer = null;
144
-
145
- assistant.error('Failed to update affiliate code', e)
146
- })
147
-
148
- // Merge the payload and the default user object
149
- const user = {
150
- activity: {
151
- geolocation: assistant.request.geolocation,
152
- client: assistant.request.client,
153
- },
154
- affiliate: {
155
- referrer: payload.affiliate.referrer,
156
- },
157
- metadata: Manager.Metadata().set({tag: 'user:sign-up'}),
158
- }
159
-
160
- assistant.log(`signUp(): user`, user);
161
-
162
- // Set the user
163
- self.libraries.admin.firestore().doc(`users/${payload.auth.uid}`)
164
- .set(user, { merge: true })
165
- .then((data) => {
166
- result.signedUp = true;
167
-
168
- return resolve(result);
169
- })
170
- .catch((e) => {
171
- return reject(e);
172
- })
173
-
174
- });
175
- },
176
-
177
- Module.prototype.updateReferral = function (payload) {
178
- const self = this;
179
-
180
- return new Promise(function(resolve, reject) {
181
- const Manager = self.Manager;
182
- const assistant = self.assistant;
183
- const Api = self.Api;
184
-
185
- const result = {
186
- count: 0,
187
- updatedReferral: false,
188
- referrerUid: null,
189
- }
190
- payload = payload || {};
191
-
192
- assistant.log(`updateReferral(): payload`, payload)
193
-
194
- self.libraries.admin.firestore().collection('users')
195
- .where('affiliate.code', '==', payload.affiliateCode)
196
- .get()
197
- .then(async (snapshot) => {
198
- if (snapshot.empty) {
199
- return resolve(result)
24
+ if (authUser instanceof Error) {
25
+ return reject(assistant.errorify(`Failed to get auth user: ${authUser}`, {code: 500}));
200
26
  }
201
- let count = 0;
202
- let found = false;
203
- let error = null;
204
-
205
- for (var i = 0; i < snapshot.size; i++) {
206
- const doc = snapshot.docs[i];
207
- if (!found) {
208
- let data = doc.data() || {};
209
-
210
- let referrals = data.affiliate && data.affiliate.referrals ? data.affiliate.referrals : [];
211
- referrals = Array.isArray(referrals) ? referrals : [];
212
- count = referrals.length;
213
- referrals = referrals.concat({
214
- uid: payload.uid,
215
- timestamp: self.assistant.meta.startTime.timestamp,
216
- })
217
27
 
218
- assistant.log(`updateReferral(): appending referrals...`, doc.ref.id, referrals)
28
+ // Age in seconds
29
+ const ageInSeconds = (Date.now() - new Date(authUser.metadata.creationTime)) / 1000;
30
+ // const ageInSeconds = 0;
219
31
 
220
- await self.libraries.admin.firestore().doc(`users/${doc.ref.id}`)
221
- .set({
222
- affiliate: {
223
- referrals: referrals,
224
- }
225
- }, {merge: true})
226
- .catch(e => {
227
- self.assistant.error('Error updating referral', e);
32
+ assistant.log(`signUp(): ageInSeconds`, ageInSeconds);
228
33
 
229
- error = e;
230
- })
34
+ // If the user is not new, reject
35
+ // This is important to prevent this from running when they link another provider
36
+ if (ageInSeconds >= MAX_AGE) {
37
+ return reject(assistant.errorify(`User is not new.`, {code: 400}));
38
+ }
231
39
 
232
- result.count = count;
233
- result.updatedReferral = true;
234
- result.referrerUid = doc.ref.id;
235
- found = true
40
+ // Create the user with the base data
41
+ const temporaryRecord = {
42
+ signupOptions: {
43
+ newsletter: payload.newsletterSignUp || payload.newsletter || false,
236
44
  }
237
45
  }
238
- if (error) {
239
- return reject(error);
240
- }
241
- return resolve(result)
242
- })
243
- .catch(e => {
244
- return reject(e);
245
- });
246
- });
247
- }
248
-
249
- // addToSendGridList
250
- Module.prototype.addToSendGridList = function (user) {
251
- const self = this;
252
-
253
- return new Promise(async function(resolve, reject) {
254
- const Manager = self.Manager;
255
- const assistant = self.assistant;
256
- const Api = self.Api;
257
-
258
- if (!user?.auth?.email) {
259
- return reject(new Error('Cannot add user to SendGrid list without email.'))
260
- }
261
-
262
- // Add to SendGrid list
263
- fetch('https://api.itwcreativeworks.com/wrapper', {
264
- method: 'post',
265
- response: 'json',
266
- body: {
267
- backendManagerKey: Manager.config.backend_manager.key,
268
- service: 'sendgrid',
269
- command: `v3/marketing/contacts`,
270
- method: `put`,
271
- supplemental: {
272
- user: user,
46
+ const userRecord = {
47
+ activity: {
48
+ geolocation: assistant.request.geolocation,
49
+ client: assistant.request.client,
273
50
  },
274
- body: {
275
- contacts: [
276
- {
277
- email: user?.auth?.email,
278
- address_line_1: undefined,
279
- address_line_2: undefined,
280
- // alternate_emails: [],
281
- city: user?.activity?.geolocation?.city,
282
- country: user?.activity?.geolocation?.country,
283
- first_name: undefined,
284
- last_name: undefined,
285
- postal_code: undefined,
286
- state_province_region: user?.activity?.geolocation?.region,
287
-
288
- custom_fields: {
289
- app: Manager.config.app.id,
290
- user: user?.auth?.uid,
291
- },
292
- },
293
- ],
51
+ affiliate: {
52
+ referrer: payload.affiliateCode || payload.affiliate || null,
294
53
  },
295
- },
296
- })
297
- .then((r) => {
298
- assistant.log('addToSendGridList(): Success', r)
299
- return resolve(res);
300
- })
301
- .catch((e) => {
302
- assistant.error('addToSendGridList(): Failed', e)
303
- return resolve(e);
304
- })
54
+ auth: {
55
+ email: authUser.email,
56
+ uid: authUser.uid,
57
+ },
58
+ metadata: Manager.Metadata().set({tag: 'user:sign-up'}),
59
+ }
305
60
 
306
- // await self.libraries.sendgrid.request({
307
- // method: 'post',
308
- // url: `/v3/contactdb/recipients`,
309
- // body: [{
310
- // email: email,
311
- // }],
312
- // })
313
- // .then(function (res) {
314
- // assistant.log('Sucessfully added user to SendGrid list.')
315
- // return resolve(res);
316
- // })
317
- // .catch(function (e) {
318
- // assistant.log('Failed to add user to SendGrid list.', e)
319
- // return resolve(e);
320
- // })
321
- });
322
- }
61
+ const ipKey = assistant.request.geolocation.ip.replace(/[\.:]/g, '_');
323
62
 
324
- Module.prototype.sendRateEmail = function (user) {
325
- const self = this;
63
+ const promises = [];
326
64
 
327
- return new Promise(async function(resolve, reject) {
328
- const Manager = self.Manager;
329
- const assistant = self.assistant;
65
+ // Log the user
66
+ assistant.log(`signUp(): userRecord`, userRecord);
330
67
 
331
- // Send email
332
- fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail`, {
333
- method: 'post',
334
- response: 'json',
335
- log: true,
336
- body: {
337
- backendManagerKey: Manager.config.backend_manager.key,
338
- app: Manager.config.app.id,
339
- to: {
340
- email: user.auth.email,
341
- },
342
- categories: [`account/too-many-signups`],
343
- subject: `Your ${Manager.config.brand.name} account has been deleted [${user.auth.uid}]`,
344
- template: 'd-b7f8da3c98ad49a2ad1e187f3a67b546',
345
- group: 25927,
346
- copy: true,
347
- data: {
348
- email: {
349
- preview: `You have signed up for too many accounts at ${Manager.config.brand.name}! Your account has been deleted.`,
350
- },
351
- body: {
352
- title: `${Manager.config.brand.name} account deleted`,
353
- message: `
354
- Your account at <strong>${Manager.config.brand.name}</strong> has been <strong>deleted</strong> because you have signed up for too many accounts.
355
- <br>
356
- <br>
357
- If you believe this is a mistake, please contact us at ${Manager.config.brand.email}.
358
- <br>
359
- <br>
360
- <strong>User Details</strong>:
361
- <br>
362
- <strong>UID</strong>: ${user.auth.uid}
363
- <br>
364
- <strong>Email</strong>: ${user.auth.email}
365
- <br>
366
- `,
367
- },
368
- },
369
- },
370
- })
371
- .then((json) => {
372
- assistant.log('sendEmail(): Success', json)
373
- return resolve(json);
374
- })
375
- .catch((e) => {
376
- assistant.error('sendEmail(): Failed', e)
377
- return resolve(e);
378
- });
379
- });
380
- }
68
+ // Set the user and the temporary data
69
+ promises.push(
70
+ admin.firestore().doc(`temporary/${ipKey}`)
71
+ .set(temporaryRecord, { merge: true }),
72
+ admin.firestore().doc(`users/${authUser.uid}`)
73
+ .set(userRecord, { merge: true }),
74
+ )
381
75
 
382
- Module.prototype.sendWelcomeEmail = function (user) {
383
- const self = this;
76
+ // Run the promises
77
+ Promise.all(promises)
78
+ .then(() => {
79
+ return resolve({
80
+ data: {
81
+ signedUp: true,
82
+ }
83
+ });
84
+ })
85
+ .catch((e) => {
86
+ return reject(assistant.errorify(e, {code: 500}));
87
+ });
384
88
 
385
- return new Promise(async function(resolve, reject) {
386
- const Manager = self.Manager;
387
- const assistant = self.assistant;
388
89
 
389
- // Send email
390
- fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail`, {
391
- method: 'post',
392
- response: 'json',
393
- log: true,
394
- body: {
395
- backendManagerKey: Manager.config.backend_manager.key,
396
- app: Manager.config.app.id,
397
- to: {
398
- email: user.auth.email,
399
- },
400
- categories: [`account/welcome`],
401
- subject: `Welcome to ${Manager.config.brand.name}!`,
402
- template: 'd-b7f8da3c98ad49a2ad1e187f3a67b546',
403
- group: 25928,
404
- copy: false,
405
- sendAt: moment().add(1, 'hour').unix(),
406
- data: {
407
- email: {
408
- preview: `Welcome aboard! I'm Ian, the CEO and founder of ${Manager.config.brand.name}. I'm here to ensure your journey with us gets off to a great start.`,
409
- },
410
- body: {
411
- title: `Welcome to ${Manager.config.brand.name}!`,
412
- message: `
413
- Welcome aboard!
414
- <br><br>
415
- I'm Ian, the founder and CEO of <strong>${Manager.config.brand.name}</strong>, and I'm thrilled to have you with us.
416
- Your journey begins today, and we are committed to supporting you every step of the way.
417
- <br><br>
418
- Feel free to reply directly to this email with any questions you may have.
419
- Our team and I are dedicated to ensuring your experience is exceptional.
420
- <br><br>
421
- Thank you for choosing <strong>${Manager.config.brand.name}</strong>. Here's to new beginnings!
422
- `
423
- },
424
- signoff: {
425
- type: 'personal',
426
- image: undefined,
427
- name: 'Ian Wiedenman, CEO',
428
- url: `https://ianwiedenman.com?utm_source=welcome-email&utm_medium=email&utm_campaign=${Manager.config.app.id}`,
429
- urlText: '@ianwieds',
430
- },
431
- },
432
- },
433
- })
434
- .then((json) => {
435
- assistant.log('sendEmail(): Success', json)
436
- return resolve(json);
437
90
  })
438
91
  .catch((e) => {
439
- assistant.error('sendEmail(): Failed', e)
440
- return resolve(e);
441
- });
442
- });
443
- }
444
-
445
- Module.prototype.sendCheckupEmail = function (user) {
446
- const self = this;
447
-
448
- return new Promise(async function(resolve, reject) {
449
- const Manager = self.Manager;
450
- const assistant = self.assistant;
451
-
452
- // Send email
453
- fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail`, {
454
- method: 'post',
455
- response: 'json',
456
- log: true,
457
- body: {
458
- backendManagerKey: Manager.config.backend_manager.key,
459
- app: Manager.config.app.id,
460
- to: {
461
- email: user.auth.email,
462
- },
463
- categories: [`account/checkup`],
464
- subject: `How's your experience with ${Manager.config.brand.name}?`,
465
- template: 'd-b7f8da3c98ad49a2ad1e187f3a67b546',
466
- group: 25928,
467
- copy: false,
468
- sendAt: moment().add(7, 'days').unix(),
469
- data: {
470
- email: {
471
- preview: `Checking in from ${Manager.config.brand.name} to see how things are going. Let us know if you have any questions or feedback!`,
472
- },
473
- body: {
474
- title: `How's everything going?`,
475
- message: `
476
- Hi there,
477
- <br><br>
478
- It's Ian again from <strong>${Manager.config.brand.name}</strong>. Just checking in to see how things are going for you.
479
- <br><br>
480
- Have you had a chance to explore all our features? Any questions or feedback for us?
481
- <br><br>
482
- We're always here to help, so don't hesitate to reach out. Just reply to this email and we'll get back to you as soon as possible.
483
- <br><br>
484
- Thank you for choosing <strong>${Manager.config.brand.name}</strong>. Here's to new beginnings!
485
- `
486
- },
487
- signoff: {
488
- type: 'personal',
489
- image: undefined,
490
- name: 'Ian Wiedenman, CEO',
491
- url: `https://ianwiedenman.com?utm_source=checkup-email&utm_medium=email&utm_campaign=${Manager.config.app.id}`,
492
- urlText: '@ianwieds',
493
- },
494
- },
495
- },
496
- })
497
- .then((json) => {
498
- assistant.log('sendEmail(): Success', json)
499
- return resolve(json);
92
+ return reject(e);
500
93
  })
501
- .catch((e) => {
502
- assistant.error('sendEmail(): Failed', e)
503
- return resolve(e);
504
- });
505
- });
506
- }
507
94
 
508
- Module.prototype.sendFeedbackEmail = function (user) {
509
- const self = this;
510
-
511
- return new Promise(async function(resolve, reject) {
512
- const Manager = self.Manager;
513
- const assistant = self.assistant;
514
-
515
- // Send email
516
- fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail`, {
517
- method: 'post',
518
- response: 'json',
519
- log: true,
520
- body: {
521
- backendManagerKey: Manager.config.backend_manager.key,
522
- app: Manager.config.app.id,
523
- to: {
524
- email: user.auth.email,
525
- },
526
- categories: [`engagement/feedback`],
527
- subject: `Want to share your feedback about ${Manager.config.brand.name}?`,
528
- template: 'd-c1522214c67b47058669acc5a81ed663',
529
- group: 25928,
530
- copy: false,
531
- sendAt: moment().add(14, 'days').unix(),
532
- },
533
- })
534
- .then((json) => {
535
- assistant.log('sendEmail(): Success', json)
536
- return resolve(json);
537
- })
538
- .catch((e) => {
539
- assistant.error('sendEmail(): Failed', e)
540
- return resolve(e);
541
- });
542
95
  });
543
- }
96
+
97
+ };
544
98
 
545
99
  module.exports = Module;