backend-manager 3.2.100 → 3.2.102

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "3.2.100",
3
+ "version": "3.2.102",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -0,0 +1,190 @@
1
+ const fetch = require('wonderful-fetch');
2
+ const moment = require('moment');
3
+ const jetpack = require('fs-jetpack');
4
+ const powertools = require('node-powertools');
5
+ const uuidv4 = require('uuid').v4;
6
+
7
+ const POST_TEMPLATE = jetpack.read(`${__dirname}/templates/post.html`);
8
+ const IMAGE_TAG = `{%- include /master/helpers/blog-image.html name="{name}" alt="{alt}" -%}`;
9
+ const IMAGE_PATH_SRC = `./assets/_src/images/blog/posts/post-{id}/`;
10
+ const IMAGE_PATH_REG = `/assets/images/blog/posts/post-{id}/`;
11
+ const AD_TAG = `{% include /master/modules/adunits/adsense-in-article.html index="{index}" %}`;
12
+
13
+ const IMAGE_REGEX = /(?:!\[(.*?)\]\((.*?)\))/img;
14
+ const LINK_REGEX = /(?:\[(.*?)\]\((.*?)\))/img;
15
+
16
+ function Module() {
17
+
18
+ }
19
+
20
+ Module.prototype.main = function () {
21
+ const self = this;
22
+ const Manager = self.Manager;
23
+ const Api = self.Api;
24
+ const assistant = self.assistant;
25
+ const payload = self.payload;
26
+
27
+ return new Promise(async function(resolve, reject) {
28
+ // Perform checks
29
+ if (!payload.user.roles.admin && !payload.user.roles.blogger) {
30
+ return reject(assistant.errorify(`Admin required.`, {code: 401}));
31
+ }
32
+
33
+ // Log payload
34
+ assistant.log(`main(): payload.data`, payload.data);
35
+
36
+ // Set now
37
+ const now = assistant.meta.startTime.timestamp;
38
+ const bemRepo = assistant.parseRepo(Manager?.config?.github?.repo_website);
39
+
40
+ // Check for required values
41
+ if (!payload.data.payload.title) {
42
+ return reject(assistant.errorify(`Missing required parameter: title`, {code: 400}));
43
+ } else if (!payload.data.payload.url) {
44
+ return reject(assistant.errorify(`Missing required parameter: url`, {code: 400}));
45
+ } else if (!payload.data.payload.excerpt) {
46
+ return reject(assistant.errorify(`Missing required parameter: excerpt`, {code: 400}));
47
+ } else if (!payload.data.payload.headerImageURL) {
48
+ return reject(assistant.errorify(`Missing required parameter: headerImageURL`, {code: 400}));
49
+ } else if (!payload.data.payload.body) {
50
+ return reject(assistant.errorify(`Missing required parameter: body`, {code: 400}));
51
+ }
52
+
53
+ // Fix other values
54
+ payload.data.payload.author = payload.data.payload.author || 'alex';
55
+ payload.data.payload.affiliate = payload.data.payload.affiliate || '';
56
+ payload.data.payload.tags = payload.data.payload.tags || '';
57
+ payload.data.payload.categories = payload.data.payload.categories || '';
58
+
59
+ // Fix even more values
60
+ payload.data.payload.layout = payload.data.payload.layout || 'app/blog/post';
61
+ payload.data.payload.date = payload.data.payload.date || moment(now).format('YYYY-MM-DD');
62
+ payload.data.payload.id = payload.data.payload.id || Math.round(new Date(now).getTime() / 1000);
63
+ payload.data.payload.path = payload.data.payload.path || `./_posts/${moment(now).format('YYYY')}/guest`;
64
+ payload.data.payload.githubUser = payload.data.payload.githubUser || bemRepo.user;
65
+ payload.data.payload.githubRepo = payload.data.payload.githubRepo || bemRepo.name;
66
+
67
+ // Log
68
+ assistant.log(`main(): Creating post...`, payload.data.payload);
69
+
70
+ // Extract all images
71
+ await self.extractImages();
72
+
73
+ // Set defaults
74
+ const finalContent = powertools.template(POST_TEMPLATE, payload.data.payload);
75
+
76
+ // Log
77
+ assistant.log(`main(): finalContent`, finalContent);
78
+
79
+ });
80
+
81
+ };
82
+
83
+ // Extract images
84
+ Module.prototype.extractImages = function () {
85
+ const self = this;
86
+ const Manager = self.Manager;
87
+ const Api = self.Api;
88
+ const assistant = self.assistant;
89
+ const payload = self.payload;
90
+
91
+ return new Promise(async function(resolve, reject) {
92
+ // Extract images
93
+ const matches = payload.data.payload.body.matchAll(IMAGE_REGEX);
94
+ const images = Array.from(matches).map(match => ({
95
+ src: match[2] || '',
96
+ alt: match[1] || uuidv4(),
97
+ }));
98
+
99
+ // Log
100
+ assistant.log(`extractImages(): images`, images);
101
+
102
+ if (!images) {
103
+ return resolve();
104
+ }
105
+
106
+ // Loop through images
107
+ for (let index = 0; index < images.length; index++) {
108
+ const image = images[index];
109
+
110
+ // download image
111
+ const download = await self.downloadImage(image.src, image.alt);
112
+
113
+ // Log
114
+ assistant.log(`extractImages(): download`, download);
115
+
116
+ // const src = image.src;
117
+ // const alt = image.alt;
118
+ // const hyphenated = hyphenate(alt);
119
+ // const tag = powertools.template(IMAGE_TAG, {name: hyphenate(alt), alt: alt});
120
+
121
+ // console.log(`Image ${index + 1}: src=${image.src}, alt='${image.alt}'`);
122
+ }
123
+
124
+ // const image = images[i];
125
+ // const src = image.match(LINK_REGEX)[2];
126
+ // const alt = image.match(LINK_REGEX)[1] || uuidv4();
127
+ // const name = hyphenate(alt);
128
+ // const tag = IMAGE_TAG.replace('{name}', name).replace('{alt}', alt);
129
+
130
+ // // Log
131
+ // assistant.log(`extractImages(): original`, image.match(LINK_REGEX));
132
+ // assistant.log(`extractImages(): image`, image);
133
+ // assistant.log(`extractImages(): src`, src);
134
+ // assistant.log(`extractImages(): alt`, alt);
135
+ // assistant.log(`extractImages(): name`, name);
136
+ // assistant.log(`extractImages(): tag`, tag);
137
+
138
+ // // Replace image
139
+ // payload.data.payload.body = payload.data.payload.body
140
+ // .replace(image, tag);
141
+
142
+ // // Save image
143
+ // await self.saveImage(src, id, name);
144
+
145
+ // Resolve
146
+ return resolve();
147
+ });
148
+ };
149
+
150
+ // Downlaod image
151
+ Module.prototype.downloadImage = function (src, alt) {
152
+ const self = this;
153
+ const Manager = self.Manager;
154
+ const Api = self.Api;
155
+ const assistant = self.assistant;
156
+ const payload = self.payload;
157
+
158
+ return new Promise(async function(resolve, reject) {
159
+ // Log
160
+ assistant.log(`downloadImage(): src=${src}, alt=${alt}`);
161
+
162
+ // Get image
163
+ const image = await fetch(src, {
164
+ method: 'GET',
165
+ download: true,
166
+ })
167
+ .then((r) => {
168
+ // Log
169
+ assistant.log(`downloadImage(): Result`, r);
170
+
171
+ // Save image
172
+ return resolve(r);
173
+ })
174
+ .catch((e) => reject(e));
175
+ });
176
+ };
177
+
178
+ function hyphenate(s) {
179
+ return s
180
+ // Remove everything that is not a letter or a number
181
+ .replace(/[^a-zA-Z0-9]/g, '-')
182
+ // Replace multiple hyphens with a single hyphen
183
+ .replace(/-+/g, '-')
184
+ // Remove leading and trailing hyphens
185
+ .replace(/^-|-$/g, '')
186
+ // Lowercase
187
+ .toLowerCase();
188
+ }
189
+
190
+ module.exports = Module;
@@ -0,0 +1,15 @@
1
+ ---
2
+ ### ALL PAGES ###
3
+ layout: {layout}
4
+
5
+ ### POST ONLY ###
6
+ post:
7
+ title: "{title}"
8
+ excerpt: "{excerpt}"
9
+ author: {author}
10
+ id: {id}
11
+ tags: {tags}
12
+ categories: {categories}
13
+ affiliate-search-term: {affiliate}
14
+ ---
15
+ {body}
@@ -118,10 +118,12 @@ Module.prototype.main = function () {
118
118
  if (user.providerData.filter(function (item) {
119
119
  if (item.providerId !== 'anonymous') {
120
120
  analytics.event({
121
- category: 'engagement',
122
- action: 'signup',
123
- label: item.providerId,
121
+ name: 'sign_up',
122
+ params: {
123
+ method: item.providerId,
124
+ },
124
125
  });
126
+
125
127
  return true
126
128
  }
127
129
  }).length < 1) {
@@ -33,9 +33,8 @@ Module.prototype.main = function () {
33
33
  uuid: user.uid,
34
34
  })
35
35
  .event({
36
- category: 'engagement',
37
- action: 'user-delete',
38
- // label: 'regular',
36
+ name: 'user-delete',
37
+ params: {},
39
38
  });
40
39
 
41
40
  // Delete user record
@@ -57,9 +57,8 @@ Module.prototype.main = function () {
57
57
  uuid: _.get(dataBefore, 'link.user.data.uid', undefined),
58
58
  })
59
59
  .event({
60
- category: 'engagement',
61
- action: 'notification-unsubscribe',
62
- // label: 'regular',
60
+ name: 'notification-unsubscribe',
61
+ params: {},
63
62
  });
64
63
 
65
64
  assistant.log('Notification subscription deleted:', dataBefore);
@@ -87,9 +86,8 @@ Module.prototype.main = function () {
87
86
  uuid: _.get(dataAfter, 'link.user.data.uid', undefined),
88
87
  })
89
88
  .event({
90
- category: 'engagement',
91
- action: 'notification-subscribe',
92
- // label: 'regular',
89
+ name: 'notification-subscribe',
90
+ params: {},
93
91
  });
94
92
 
95
93
  assistant.log('Notification subscription created:', dataAfter);
@@ -24,11 +24,10 @@ let Module = {
24
24
  uuid: user.auth.uid,
25
25
  })
26
26
  .event({
27
- category: 'admin',
28
- action: 'authenticate-test',
29
- // label: '',
27
+ name: 'authenticate-test',
28
+ params: {},
30
29
  });
31
-
30
+
32
31
  assistant.log('Request:', assistant.request.data);
33
32
  assistant.log('Result user:', user);
34
33
  return res.status(200).json({status: 200, user: user });
@@ -25,9 +25,8 @@ let Module = {
25
25
  uuid: user.auth.uid,
26
26
  })
27
27
  .event({
28
- category: 'admin',
29
- action: 'create-test-accounts',
30
- // label: '',
28
+ name: 'create-test-accounts',
29
+ params: {},
31
30
  });
32
31
 
33
32
  let assistant = self.assistant;
@@ -24,9 +24,8 @@ let Module = {
24
24
  uuid: user.auth.uid,
25
25
  })
26
26
  .event({
27
- category: 'admin',
28
- action: 'webhook-test',
29
- // label: '',
27
+ name: 'webhook-test',
28
+ params: {},
30
29
  });
31
30
 
32
31
  assistant.log(assistant.request);
@@ -1,5 +1,6 @@
1
1
  const fetch = require('wonderful-fetch');
2
2
  const moment = require('moment');
3
+ const crypto = require('crypto');
3
4
  let uuidv5;
4
5
 
5
6
  const UUID_REGEX = /[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/;
@@ -25,6 +26,7 @@ function Analytics(Manager, options) {
25
26
  ip: self.assistant?.request?.geolocation?.ip || '127.0.0.1',
26
27
  country: self.assistant?.request?.geolocation?.country || '',
27
28
  city: self.assistant?.request?.geolocation?.city || '',
29
+ region: self.assistant?.request?.geolocation?.region || '',
28
30
  referrer: self.assistant?.request?.referrer || '',
29
31
  userAgent: self.assistant?.request?.client?.userAgent || '',
30
32
  language: (self.assistant?.request?.client?.language || '').split(',')[0],
@@ -45,13 +47,14 @@ function Analytics(Manager, options) {
45
47
  options.pageview = typeof options.pageview === 'undefined' ? true : options.pageview;
46
48
  options.version = options.version || Manager.package.version;
47
49
  options.userProperties = options.userProperties || {};
50
+ options.userData = options.userData || {};
48
51
 
49
52
  // Set user
50
53
  // https://www.optimizesmart.com/how-to-create-and-use-user-properties-in-ga4/
51
54
  // https://developers.google.com/analytics/devguides/collection/protocol/ga4/user-properties?client_type=gtag
52
55
  // https://support.google.com/analytics/answer/12980150?hl=en&co=GENIE.Platform%3DAndroid
53
56
  const authUser = self.assistant?.usage?.user;
54
- self.user = {
57
+ self.userProperties = {
55
58
  app_version: {
56
59
  value: options.version,
57
60
  },
@@ -126,9 +129,33 @@ function Analytics(Manager, options) {
126
129
  // dr? (referrer)
127
130
  };
128
131
 
132
+ // Fix user data
133
+ // https://developers.google.com/analytics/devguides/collection/ga4/uid-data
134
+ self.userData = {
135
+ sha256_email_address: authUser?.auth?.email
136
+ ? toSHA256(authUser?.auth?.email)
137
+ : undefined,
138
+ sha256_phone_number: authUser?.personal?.telephone?.number
139
+ ? toSHA256(authUser?.personal?.telephone?.countryCode + authUser?.personal?.telephone?.number)
140
+ : undefined,
141
+ address: {
142
+ sha256_first_name: authUser?.personal?.name?.first
143
+ ? toSHA256(authUser?.personal?.name?.first)
144
+ : undefined,
145
+ sha256_last_name: authUser?.personal?.name?.last
146
+ ? toSHA256(authUser?.personal?.name?.last)
147
+ : undefined,
148
+ // sha256_street: TODO,
149
+ city: self.request.city || undefined,
150
+ region: self.request.region || undefined,
151
+ // postal_code: TODO,
152
+ country: self.request.country || undefined,
153
+ }
154
+ }
155
+
129
156
  // Merge user properties
130
- self.user = {
131
- ...self.user,
157
+ self.userProperties = {
158
+ ...self.userProperties,
132
159
  ...options.userProperties,
133
160
  };
134
161
 
@@ -169,7 +196,8 @@ Analytics.prototype.generateId = function (id) {
169
196
  const assistant = self.assistant;
170
197
  const options = self.options;
171
198
  const request = self.request;
172
- const user = self.user;
199
+ const userProperties = self.userProperties;
200
+ const userData = self.userData;
173
201
 
174
202
  // Load uuidv5
175
203
  uuidv5 = uuidv5 || require('uuid').v5;
@@ -189,7 +217,8 @@ Analytics.prototype.event = function (payload) {
189
217
  const assistant = self.assistant;
190
218
  const options = self.options;
191
219
  const request = self.request;
192
- const user = self.user;
220
+ const userProperties = self.userProperties;
221
+ const userData = self.userData;
193
222
 
194
223
  // Fix payload
195
224
  // https://support.google.com/analytics/answer/13316687?hl=en#zippy=%2Cweb
@@ -271,14 +300,14 @@ Analytics.prototype.event = function (payload) {
271
300
  // ]
272
301
  // }
273
302
 
274
-
275
303
  // Build url and body
276
304
  const url = `https://www.google-analytics.com/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`;
277
305
  const body = {
278
306
  client_id: self.options.uuid,
279
307
  user_id: self.options.uuid,
280
308
  // timestamp_micros: new Date().getTime() * 1000,
281
- user_properties: user,
309
+ user_properties: userProperties,
310
+ user_data: userData,
282
311
  // consent: {},
283
312
  // non_personalized_ads: false,
284
313
  events: [payload],
@@ -422,4 +451,16 @@ Analytics.prototype.event = function (payload) {
422
451
  // return self;
423
452
  // };
424
453
 
454
+ /*
455
+ Unlike gtag, which automatically hashes sensitive user-provided data, the Measurement Protocol requires a developer to hash sensitive user-provided data using a secure one-way hashing algorithm called SHA256 and encode it using hex string format prior to calling the API.
456
+
457
+ All user data fields starting with the sha256 prefix in their name should be only populated with hashed and hex-encoded values.
458
+
459
+ The following example code performs the necessary encryption and encoding steps:
460
+ */
461
+
462
+ function toSHA256(value) {
463
+ return crypto.createHash('sha256').update(value).digest('hex');
464
+ }
465
+
425
466
  module.exports = Analytics;
@@ -450,46 +450,16 @@ Manager.prototype.init = function (exporter, options) {
450
450
  }
451
451
 
452
452
  // Send analytics
453
- // self.Analytics({
454
- // assistant: self.assistant,
455
- // uuid: self.SERVER_UUID,
456
- // isDevelopment: false,
457
- // })
458
- // .event({
459
- // // category: 'admin',
460
- // // action: 'initialized',
461
- // // label: '',
462
- // // name: 'admin/initialized',
463
- // // name: 'screen_view',
464
- // // params: {
465
- // // screen_class: 'MainActivity',
466
- // // },
467
- // });
468
-
469
- // self.Analytics({
470
- // assistant: self.assistant,
471
- // // uuid: '256675177.1692831448',
472
- // // uuid: self.SERVER_UUID,
473
- // uuid: 'a5b43c29-7bb3-514b-b7bb-33782cd8d802',
474
- // isDevelopment: false,
475
- // })
476
- // .event({
477
- // "name": "purchase",
478
- // "params": {
479
- // "transaction_id": "T12345",
480
- // "value": 123.45,
481
- // "currency": "USD",
482
- // "items": [
483
- // {
484
- // "item_id": "sku1234",
485
- // "item_name": "widget",
486
- // "quantity": 1,
487
- // "item_brand": "Google",
488
- // "price": 123.45
489
- // }
490
- // ]
491
- // }
492
- // });
453
+ self.Analytics({
454
+ assistant: self.assistant,
455
+ uuid: self.SERVER_UUID,
456
+ })
457
+ .event({
458
+ name: 'admin/initialized',
459
+ params: {
460
+ // screen_class: 'MainActivity',
461
+ },
462
+ });
493
463
 
494
464
  // Return
495
465
  return self;