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 +1 -1
- package/src/manager/functions/core/actions/api/admin/create-post-new.js +190 -0
- package/src/manager/functions/core/actions/api/admin/templates/post.html +15 -0
- package/src/manager/functions/core/events/auth/on-create.js +5 -3
- package/src/manager/functions/core/events/auth/on-delete.js +2 -3
- package/src/manager/functions/core/events/firestore/on-subscription.js +4 -6
- package/src/manager/functions/test/authenticate.js +3 -4
- package/src/manager/functions/test/create-test-accounts.js +2 -3
- package/src/manager/functions/test/webhook.js +2 -3
- package/src/manager/helpers/analytics.js +48 -7
- package/src/manager/index.js +10 -40
package/package.json
CHANGED
|
@@ -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;
|
|
@@ -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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
name: 'sign_up',
|
|
122
|
+
params: {
|
|
123
|
+
method: item.providerId,
|
|
124
|
+
},
|
|
124
125
|
});
|
|
126
|
+
|
|
125
127
|
return true
|
|
126
128
|
}
|
|
127
129
|
}).length < 1) {
|
|
@@ -57,9 +57,8 @@ Module.prototype.main = function () {
|
|
|
57
57
|
uuid: _.get(dataBefore, 'link.user.data.uid', undefined),
|
|
58
58
|
})
|
|
59
59
|
.event({
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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 });
|
|
@@ -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.
|
|
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.
|
|
131
|
-
...self.
|
|
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
|
|
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
|
|
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:
|
|
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;
|
package/src/manager/index.js
CHANGED
|
@@ -450,46 +450,16 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
// Send analytics
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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;
|