backend-manager 3.0.64 → 3.1.1
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/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
|
+
## [3.1.0] - 2023-12-19
|
|
19
|
+
### Added
|
|
20
|
+
- Added `.analytics()` GA4 support.
|
|
21
|
+
|
|
22
|
+
#### New Analytics Format
|
|
23
|
+
```js
|
|
24
|
+
analytics.send({
|
|
25
|
+
name: 'tutorial_begin',
|
|
26
|
+
params: {
|
|
27
|
+
tutorial_id: 'tutorial_1',
|
|
28
|
+
tutorial_name: 'the_beginning',
|
|
29
|
+
tutorial_step: 1,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
- Added `.usage()` to track API usage.
|
|
34
|
+
- Added `.middleware()` to help setup http functions.
|
|
35
|
+
|
|
18
36
|
## [3.0.0] - 2023-09-05
|
|
19
37
|
### ⚠️ BREAKING
|
|
20
38
|
- Updated `firebase-admin` from `9.12.0` --> `11.10.1`
|
|
@@ -30,6 +48,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
30
48
|
- Removed `backend-assistant` dependency and moved to custom library within this module at `./src/manager/helpers/assistant.js`
|
|
31
49
|
- Replaced `require('firebase-functions/lib/logger/compat')` with the updated `require('firebase-functions/logger/compat')`
|
|
32
50
|
- Changed default for `options.setupFunctionsLegacy` from `true` --> `false`
|
|
51
|
+
- `.analytics()` is broken due to GA4 updates and should not be used until the next feature release
|
|
33
52
|
- Updated geolocation and client data retrieval to new format:
|
|
34
53
|
#### New Way
|
|
35
54
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -64,7 +64,6 @@
|
|
|
64
64
|
"sizeitup": "^1.0.7",
|
|
65
65
|
"uid-generator": "^2.0.0",
|
|
66
66
|
"ultimate-jekyll-poster": "^0.0.15",
|
|
67
|
-
"universal-analytics": "^0.5.3",
|
|
68
67
|
"uuid": "^9.0.1",
|
|
69
68
|
"wonderful-fetch": "^1.0.1",
|
|
70
69
|
"wonderful-log": "^1.0.5",
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// const ua = require('universal-analytics');
|
|
2
|
+
const get = require('lodash/get');
|
|
3
|
+
const fetch = require('wonderful-fetch');
|
|
4
|
+
|
|
5
|
+
const uuidRegex = /[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}/;
|
|
6
|
+
let uuidv5;
|
|
7
|
+
|
|
8
|
+
const BLACKLISTED_USER_AGENTS = new RegExp([
|
|
9
|
+
/(node-fetch)/,
|
|
10
|
+
].map(r => r.source).join(''));
|
|
11
|
+
|
|
12
|
+
function Analytics(Manager, options) {
|
|
13
|
+
let self = this;
|
|
14
|
+
self.Manager = Manager;
|
|
15
|
+
// self._request = self.Manager._inner || {};
|
|
16
|
+
|
|
17
|
+
// Set id and secret
|
|
18
|
+
// const analyticsId = get(self.Manager, 'config.google_analytics.id', undefined);
|
|
19
|
+
self.analyticsId = self?.Manager?.config?.google_analytics?.id;
|
|
20
|
+
self.analyticsSecret = self?.Manager?.config?.google_analytics?.secret;
|
|
21
|
+
|
|
22
|
+
// Fix options
|
|
23
|
+
options = options || {};
|
|
24
|
+
|
|
25
|
+
// Set properties
|
|
26
|
+
self._assistant = options.assistant || Manager.Assistant();
|
|
27
|
+
self._request = {
|
|
28
|
+
ip: get(self._assistant, 'request.geolocation.ip', '127.0.0.1'),
|
|
29
|
+
country: get(self._assistant, 'request.geolocation.country', ''),
|
|
30
|
+
referrer: get(self._assistant, 'request.referrer', ''),
|
|
31
|
+
userAgent: get(self._assistant, 'request.client.userAgent', ''),
|
|
32
|
+
name: get(self._assistant, 'meta.name', ''),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self._request.userAgent = self._request.userAgent.match(BLACKLISTED_USER_AGENTS) ? '' : self._request.userAgent;
|
|
36
|
+
|
|
37
|
+
self._ds = 'app';
|
|
38
|
+
self._uuid = options.uuid || self._request.ip || self.Manager.SERVER_UUID;
|
|
39
|
+
self._uuid = self._uuid.match(uuidRegex) ? self._uuid : self.generateId(self._uuid);
|
|
40
|
+
self._debug = typeof options.debug === 'undefined' ? (self.Manager.assistant.meta.environment === 'development') : options.debug;
|
|
41
|
+
self._pageview = typeof options.pageview === 'undefined' ? true : options.pageview;
|
|
42
|
+
self._version = self.Manager.package.version;
|
|
43
|
+
self._initialized = false;
|
|
44
|
+
|
|
45
|
+
if (!self.analyticsId) {
|
|
46
|
+
console.log('Not initializing because missing analyticsId', self.analyticsId);
|
|
47
|
+
return self;
|
|
48
|
+
} else if (!self.analyticsSecret) {
|
|
49
|
+
console.log('Not initializing because missing analyticsSecret', self.analyticsSecret);
|
|
50
|
+
return self;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// self.user = ua(self.analyticsId, self._uuid, {
|
|
54
|
+
// strictCidFormat: false, // https://analytics.google.com/analytics/web/#/report-home/a104885300w228822596p215709578
|
|
55
|
+
// });
|
|
56
|
+
// self.user.set('ds', 'app');
|
|
57
|
+
|
|
58
|
+
// // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
59
|
+
// if (self._uuid) {
|
|
60
|
+
// self.user.set('uid', self._uuid);
|
|
61
|
+
// }
|
|
62
|
+
// if (self._request.ip) {
|
|
63
|
+
// self.user.set('uip', encodeURIComponent(self._request.ip));
|
|
64
|
+
// }
|
|
65
|
+
// if (self._request.userAgent) {
|
|
66
|
+
// self.user.set('ua', encodeURIComponent(self._request.userAgent));
|
|
67
|
+
// }
|
|
68
|
+
// if (self._request.referrer) {
|
|
69
|
+
// self.user.set('dr', encodeURIComponent(self._request.referrer));
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
self._initialized = true;
|
|
73
|
+
|
|
74
|
+
if (self._pageview) {
|
|
75
|
+
self.pageview({
|
|
76
|
+
path: self._request.name,
|
|
77
|
+
location: self._request.name,
|
|
78
|
+
host: self._request.name,
|
|
79
|
+
title: self._request.name,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return self;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Analytics.prototype.generateId = function (id) {
|
|
87
|
+
let self = this;
|
|
88
|
+
uuidv5 = uuidv5 || require('uuid').v5;
|
|
89
|
+
let namespace = get(self.Manager, 'config.backend_manager.namespace', undefined);
|
|
90
|
+
|
|
91
|
+
return id && namespace ? uuidv5(id, namespace) : undefined;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
Analytics.prototype.pageview = function (options) {
|
|
95
|
+
let self = this;
|
|
96
|
+
options = options || {};
|
|
97
|
+
options.path = options.path || self._request.name;
|
|
98
|
+
options.location = options.location || self._request.name;
|
|
99
|
+
options.host = options.host || self._request.name;
|
|
100
|
+
options.title = options.title || self._request.name;
|
|
101
|
+
|
|
102
|
+
if (!self._initialized) {
|
|
103
|
+
return self;
|
|
104
|
+
} else if (self._debug) {
|
|
105
|
+
console.log('Skipping Analytics.pageview() because in development', self._uuid, options);
|
|
106
|
+
return self;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// self.user.pageview({
|
|
110
|
+
// dp: options.path,
|
|
111
|
+
// dl: options.location,
|
|
112
|
+
// dh: options.host,
|
|
113
|
+
// dt: options.title,
|
|
114
|
+
// }).send();
|
|
115
|
+
|
|
116
|
+
self.send();
|
|
117
|
+
|
|
118
|
+
return self;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
Analytics.prototype.event = function (options) {
|
|
122
|
+
let self = this;
|
|
123
|
+
options = options || {};
|
|
124
|
+
options.category = options.category;
|
|
125
|
+
options.action = options.action;
|
|
126
|
+
options.label = options.label;
|
|
127
|
+
options.value = options.value;
|
|
128
|
+
options.path = options.path || self._request.name;
|
|
129
|
+
|
|
130
|
+
if (!self._initialized) {
|
|
131
|
+
return self;
|
|
132
|
+
} else if (self._debug) {
|
|
133
|
+
console.log('Skipping Analytics.event() because in development', self._uuid, options);
|
|
134
|
+
return self;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// self.user.event({
|
|
138
|
+
// ec: options.category,
|
|
139
|
+
// ea: options.action,
|
|
140
|
+
// el: options.label,
|
|
141
|
+
// ev: options.value,
|
|
142
|
+
// // dp: options.path || window.location.href,
|
|
143
|
+
// }).send();
|
|
144
|
+
|
|
145
|
+
self.send({
|
|
146
|
+
name: 'tutorial_begin',
|
|
147
|
+
params: {
|
|
148
|
+
campaign_id: 'google_1234',
|
|
149
|
+
campaign: 'Summer_fun',
|
|
150
|
+
source: 'google',
|
|
151
|
+
medium: 'cpc',
|
|
152
|
+
term: 'summer+travel',
|
|
153
|
+
content: 'logolink',
|
|
154
|
+
session_id: '123',
|
|
155
|
+
engagement_time_msec: '100',
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return self;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
Analytics.prototype.send = function (event) {
|
|
163
|
+
let self = this;
|
|
164
|
+
|
|
165
|
+
if (!self._initialized) {
|
|
166
|
+
return self;
|
|
167
|
+
} else if (self._debug) {
|
|
168
|
+
console.log('Skipping Analytics.event() because in development', self._uuid, options);
|
|
169
|
+
return self;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// self.user = ua(self.analyticsId, self._uuid, {
|
|
173
|
+
// strictCidFormat: false, // https://analytics.google.com/analytics/web/#/report-home/a104885300w228822596p215709578
|
|
174
|
+
// });
|
|
175
|
+
// self.user.set('ds', 'app');
|
|
176
|
+
|
|
177
|
+
// // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
178
|
+
// if (self._uuid) {
|
|
179
|
+
// self.user.set('uid', self._uuid);
|
|
180
|
+
// }
|
|
181
|
+
// if (self._request.ip) {
|
|
182
|
+
// self.user.set('uip', encodeURIComponent(self._request.ip));
|
|
183
|
+
// }
|
|
184
|
+
// if (self._request.userAgent) {
|
|
185
|
+
// self.user.set('ua', encodeURIComponent(self._request.userAgent));
|
|
186
|
+
// }
|
|
187
|
+
// if (self._request.referrer) {
|
|
188
|
+
// self.user.set('dr', encodeURIComponent(self._request.referrer));
|
|
189
|
+
// }
|
|
190
|
+
|
|
191
|
+
/*
|
|
192
|
+
https://stackoverflow.com/questions/68773179/what-should-the-client-id-be-when-sending-events-to-google-analytics-4-using-the
|
|
193
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
// Send event
|
|
199
|
+
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`, {
|
|
200
|
+
method: 'post',
|
|
201
|
+
body: {
|
|
202
|
+
client_id: self._uuid,
|
|
203
|
+
user_id: self._uuid,
|
|
204
|
+
events: [event],
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
.then((r) => {
|
|
208
|
+
console.log('Analytics.send(): Sent', r);
|
|
209
|
+
})
|
|
210
|
+
.catch((e) => {
|
|
211
|
+
console.error('Analytics.send(): Error sending', e);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return self;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
module.exports = Analytics;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const ua = require('universal-analytics');
|
|
1
|
+
// const ua = require('universal-analytics');
|
|
2
2
|
const get = require('lodash/get');
|
|
3
|
+
const fetch = require('wonderful-fetch');
|
|
4
|
+
|
|
3
5
|
const uuidRegex = /[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}/;
|
|
4
6
|
let uuidv5;
|
|
5
7
|
|
|
@@ -8,13 +10,16 @@ const BLACKLISTED_USER_AGENTS = new RegExp([
|
|
|
8
10
|
].map(r => r.source).join(''));
|
|
9
11
|
|
|
10
12
|
function Analytics(Manager, options) {
|
|
11
|
-
|
|
13
|
+
const self = this;
|
|
12
14
|
self.Manager = Manager;
|
|
13
15
|
// self._request = self.Manager._inner || {};
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
// Set id and secret
|
|
18
|
+
// const analyticsId = get(self.Manager, 'config.google_analytics.id', undefined);
|
|
19
|
+
self.analyticsId = self?.Manager?.config?.google_analytics?.id;
|
|
20
|
+
self.analyticsSecret = self?.Manager?.config?.google_analytics?.secret;
|
|
16
21
|
|
|
17
|
-
// Fix
|
|
22
|
+
// Fix options
|
|
18
23
|
options = options || {};
|
|
19
24
|
|
|
20
25
|
// Set properties
|
|
@@ -29,36 +34,40 @@ function Analytics(Manager, options) {
|
|
|
29
34
|
|
|
30
35
|
self._request.userAgent = self._request.userAgent.match(BLACKLISTED_USER_AGENTS) ? '' : self._request.userAgent;
|
|
31
36
|
|
|
37
|
+
self._data_soruce = 'server';
|
|
32
38
|
self._uuid = options.uuid || self._request.ip || self.Manager.SERVER_UUID;
|
|
33
39
|
self._uuid = self._uuid.match(uuidRegex) ? self._uuid : self.generateId(self._uuid);
|
|
34
|
-
self._debug = typeof options.debug === 'undefined' ?
|
|
40
|
+
self._debug = typeof options.debug === 'undefined' ? self._assistant.meta.environment === 'development' : options.debug;
|
|
35
41
|
self._pageview = typeof options.pageview === 'undefined' ? true : options.pageview;
|
|
36
42
|
self._version = self.Manager.package.version;
|
|
37
43
|
self._initialized = false;
|
|
38
44
|
|
|
39
|
-
if (!analyticsId) {
|
|
40
|
-
|
|
45
|
+
if (!self.analyticsId) {
|
|
46
|
+
self._assistant.log('analytics(): Not initializing because missing analyticsId', self.analyticsId);
|
|
47
|
+
return self;
|
|
48
|
+
} else if (!self.analyticsSecret) {
|
|
49
|
+
self._assistant.log('analytics(): Not initializing because missing analyticsSecret', self.analyticsSecret);
|
|
41
50
|
return self;
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
self.user = ua(analyticsId, self._uuid, {
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
self.user.set('ds', 'app');
|
|
48
|
-
|
|
49
|
-
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
50
|
-
if (self._uuid) {
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (self._request.ip) {
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
if (self._request.userAgent) {
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
if (self._request.referrer) {
|
|
60
|
-
|
|
61
|
-
}
|
|
53
|
+
// self.user = ua(self.analyticsId, self._uuid, {
|
|
54
|
+
// strictCidFormat: false, // https://analytics.google.com/analytics/web/#/report-home/a104885300w228822596p215709578
|
|
55
|
+
// });
|
|
56
|
+
// self.user.set('ds', 'app');
|
|
57
|
+
|
|
58
|
+
// // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
59
|
+
// if (self._uuid) {
|
|
60
|
+
// self.user.set('uid', self._uuid);
|
|
61
|
+
// }
|
|
62
|
+
// if (self._request.ip) {
|
|
63
|
+
// self.user.set('uip', encodeURIComponent(self._request.ip));
|
|
64
|
+
// }
|
|
65
|
+
// if (self._request.userAgent) {
|
|
66
|
+
// self.user.set('ua', encodeURIComponent(self._request.userAgent));
|
|
67
|
+
// }
|
|
68
|
+
// if (self._request.referrer) {
|
|
69
|
+
// self.user.set('dr', encodeURIComponent(self._request.referrer));
|
|
70
|
+
// }
|
|
62
71
|
|
|
63
72
|
self._initialized = true;
|
|
64
73
|
|
|
@@ -75,61 +84,150 @@ function Analytics(Manager, options) {
|
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
Analytics.prototype.generateId = function (id) {
|
|
78
|
-
|
|
87
|
+
const self = this;
|
|
79
88
|
uuidv5 = uuidv5 || require('uuid').v5;
|
|
80
89
|
let namespace = get(self.Manager, 'config.backend_manager.namespace', undefined);
|
|
81
90
|
|
|
82
91
|
return id && namespace ? uuidv5(id, namespace) : undefined;
|
|
83
92
|
};
|
|
84
93
|
|
|
94
|
+
// Disabled because we are using the Measurement Protocol (12/19/2023)
|
|
85
95
|
Analytics.prototype.pageview = function (options) {
|
|
86
|
-
|
|
96
|
+
// const self = this;
|
|
97
|
+
// options = options || {};
|
|
98
|
+
// options.path = options.path || self._request.name;
|
|
99
|
+
// options.location = options.location || self._request.name;
|
|
100
|
+
// options.host = options.host || self._request.name;
|
|
101
|
+
// options.title = options.title || self._request.name;
|
|
102
|
+
|
|
103
|
+
// if (!self._initialized) {
|
|
104
|
+
// return self;
|
|
105
|
+
// } else if (self._debug) {
|
|
106
|
+
// self._assistant.log('analytics(): Skipping Analytics.pageview() because in development', self._uuid, options);
|
|
107
|
+
// return self;
|
|
108
|
+
// }
|
|
109
|
+
|
|
110
|
+
// // self.user.pageview({
|
|
111
|
+
// // dp: options.path,
|
|
112
|
+
// // dl: options.location,
|
|
113
|
+
// // dh: options.host,
|
|
114
|
+
// // dt: options.title,
|
|
115
|
+
// // }).send();
|
|
116
|
+
|
|
117
|
+
// // self.send();
|
|
118
|
+
|
|
119
|
+
// return self;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
Analytics.prototype.event = function (options) {
|
|
123
|
+
const self = this;
|
|
124
|
+
|
|
125
|
+
// Fix options
|
|
87
126
|
options = options || {};
|
|
88
|
-
options.path = options.path || self._request.name;
|
|
89
|
-
options.location = options.location || self._request.name;
|
|
90
|
-
options.host = options.host || self._request.name;
|
|
91
|
-
options.title = options.title || self._request.name;
|
|
92
127
|
|
|
93
128
|
if (!self._initialized) {
|
|
94
129
|
return self;
|
|
95
130
|
} else if (self._debug) {
|
|
96
|
-
|
|
131
|
+
self._assistant.log('analytics(): Skipping Analytics.event() because in development', self._uuid, options);
|
|
97
132
|
return self;
|
|
98
133
|
}
|
|
99
134
|
|
|
100
|
-
self.user.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
// self.user.event({
|
|
136
|
+
// ec: options.category,
|
|
137
|
+
// ea: options.action,
|
|
138
|
+
// el: options.label,
|
|
139
|
+
// ev: options.value,
|
|
140
|
+
// // dp: options.path || window.location.href,
|
|
141
|
+
// }).send();
|
|
142
|
+
|
|
143
|
+
self.send({
|
|
144
|
+
name: options.name,
|
|
145
|
+
params: options.params
|
|
146
|
+
});
|
|
106
147
|
|
|
107
148
|
return self;
|
|
108
149
|
};
|
|
109
150
|
|
|
110
|
-
Analytics.prototype.
|
|
111
|
-
|
|
112
|
-
options = options || {};
|
|
113
|
-
options.category = options.category;
|
|
114
|
-
options.action = options.action;
|
|
115
|
-
options.label = options.label;
|
|
116
|
-
options.value = options.value;
|
|
117
|
-
options.path = options.path || self._request.name;
|
|
151
|
+
Analytics.prototype.send = function (event) {
|
|
152
|
+
const self = this;
|
|
118
153
|
|
|
119
154
|
if (!self._initialized) {
|
|
120
155
|
return self;
|
|
121
156
|
} else if (self._debug) {
|
|
122
|
-
|
|
157
|
+
self._assistant.log('analytics(): Skipping Analytics.event() because in development', self._uuid, event);
|
|
123
158
|
return self;
|
|
124
159
|
}
|
|
125
160
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
161
|
+
/*
|
|
162
|
+
https://stackoverflow.com/questions/68773179/what-should-the-client-id-be-when-sending-events-to-google-analytics-4-using-the
|
|
163
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
167
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#recommended_parameters_for_reports
|
|
168
|
+
https://stackoverflow.com/questions/43049662/how-to-send-measurement-protocol-if-there-is-no-clientid
|
|
169
|
+
|
|
170
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=gtag
|
|
171
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag
|
|
172
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events
|
|
173
|
+
https://developers.google.com/analytics/devguides/collection/protocol/ga4/ua-feature-matrix
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
// Format event
|
|
177
|
+
event = event || {};
|
|
178
|
+
event.name = event.name || '';
|
|
179
|
+
event.timestamp_micros = new Date().getTime() * 1000,
|
|
180
|
+
event.params = event.params || {};
|
|
181
|
+
event.params.session_id = self._assistant.id;
|
|
182
|
+
event.params.engagement_time_msec = `${new Date().getTime() - new Date(self._assistant.meta.startTime.timestamp).getTime()}`;
|
|
183
|
+
event.params.event_source = self._data_soruce;
|
|
184
|
+
event.params.ip_override = self._request.ip;
|
|
185
|
+
event.params.user_agent = self._request.userAgent;
|
|
186
|
+
event.params.page_location = self._request.name;
|
|
187
|
+
event.params.page_referrer = self._request.referrer;
|
|
188
|
+
event.params.page_title = self._request.name;
|
|
189
|
+
|
|
190
|
+
// "event_source": "server",
|
|
191
|
+
// "page_location": "https:\/\/www.yourdomain.com\/page2",
|
|
192
|
+
// "page_referrer": "\/page1",
|
|
193
|
+
// "page_title": "Page 2",
|
|
194
|
+
// "ip_override": "xxx.xxx.xxx.0",
|
|
195
|
+
// "campaign": "your_campaign",
|
|
196
|
+
// "source": "your_source",
|
|
197
|
+
// "medium": "your_medium",
|
|
198
|
+
// "term": "your_term",
|
|
199
|
+
// "content": "your_content"
|
|
200
|
+
|
|
201
|
+
const body = {
|
|
202
|
+
client_id: self._uuid,
|
|
203
|
+
user_id: self._uuid,
|
|
204
|
+
// ip_override: self._request.ip,
|
|
205
|
+
// user_agent: self._request.userAgent,
|
|
206
|
+
events: [event],
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Log
|
|
210
|
+
if (self._assistant.meta.environment === 'development') {
|
|
211
|
+
self._assistant.log('analytics().send(): Sending...', JSON.stringify(body));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Send event
|
|
215
|
+
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`, {
|
|
216
|
+
// fetch(`https://www.google-analytics.com/debug/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`, {
|
|
217
|
+
method: 'post',
|
|
218
|
+
response: 'text',
|
|
219
|
+
tries: 2,
|
|
220
|
+
timeout: 30000,
|
|
221
|
+
body: body,
|
|
222
|
+
})
|
|
223
|
+
.then((r) => {
|
|
224
|
+
if (self._assistant.meta.environment === 'development') {
|
|
225
|
+
self._assistant.log('analytics().send(): Success', r);
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
.catch((e) => {
|
|
229
|
+
self._assistant.error('analytics().send(): Failed', e);
|
|
230
|
+
});
|
|
133
231
|
|
|
134
232
|
return self;
|
|
135
233
|
};
|
|
@@ -392,10 +392,12 @@ BackendAssistant.prototype.errorManager = function(e, options) {
|
|
|
392
392
|
if (options.send && self.ref.res && self.ref.res.status) {
|
|
393
393
|
self.ref.res
|
|
394
394
|
.status(options.code)
|
|
395
|
-
.send(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
.send((
|
|
396
|
+
newError?.message
|
|
397
|
+
// ? newError.stack
|
|
398
|
+
? newError.message
|
|
399
|
+
: newError
|
|
400
|
+
) || 'Unknown error');
|
|
399
401
|
}
|
|
400
402
|
|
|
401
403
|
return {
|
|
@@ -417,6 +419,14 @@ BackendAssistant.prototype.respond = function(response, options) {
|
|
|
417
419
|
? true
|
|
418
420
|
: options.log;
|
|
419
421
|
|
|
422
|
+
// Handle error
|
|
423
|
+
if (
|
|
424
|
+
response instanceof Error
|
|
425
|
+
|| (options.code >= 400 && options.code <= 599)
|
|
426
|
+
) {
|
|
427
|
+
return self.errorify(response, options);
|
|
428
|
+
}
|
|
429
|
+
|
|
420
430
|
// Attach properties
|
|
421
431
|
_attachHeaderProperties(self, options);
|
|
422
432
|
|
|
@@ -488,11 +498,11 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
488
498
|
}
|
|
489
499
|
}
|
|
490
500
|
|
|
491
|
-
if (req
|
|
501
|
+
if (req?.headers?.authorization?.startsWith('Bearer ')) {
|
|
492
502
|
// Read the ID Token from the Authorization header.
|
|
493
503
|
idToken = req.headers.authorization.split('Bearer ')[1];
|
|
494
504
|
self.log('Found "Authorization" header', idToken, logOptions);
|
|
495
|
-
} else if (req
|
|
505
|
+
} else if (req?.cookies?.__session) {
|
|
496
506
|
// Read the ID Token from cookie.
|
|
497
507
|
idToken = req.cookies.__session;
|
|
498
508
|
self.log('Found "__session" cookie', idToken, logOptions);
|
|
@@ -517,29 +527,31 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
517
527
|
}
|
|
518
528
|
} else if (options.apiKey) {
|
|
519
529
|
self.log('Found "options.apiKey"', options.apiKey, logOptions);
|
|
530
|
+
|
|
520
531
|
if (options.apiKey.includes('test')) {
|
|
521
532
|
return _resolve(self.request.user);
|
|
522
533
|
}
|
|
534
|
+
|
|
523
535
|
await admin.firestore().collection(`users`)
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
536
|
+
.where('api.privateKey', '==', options.apiKey)
|
|
537
|
+
.get()
|
|
538
|
+
.then(function(querySnapshot) {
|
|
539
|
+
querySnapshot.forEach(function(doc) {
|
|
540
|
+
self.request.user = doc.data();
|
|
541
|
+
self.request.user.authenticated = true;
|
|
542
|
+
});
|
|
543
|
+
})
|
|
544
|
+
.catch(function(error) {
|
|
545
|
+
console.error('Error getting documents: ', error);
|
|
530
546
|
});
|
|
531
|
-
})
|
|
532
|
-
.catch(function(error) {
|
|
533
|
-
console.error('Error getting documents: ', error);
|
|
534
|
-
});
|
|
535
547
|
|
|
536
548
|
return _resolve(self.request.user);
|
|
537
549
|
} else {
|
|
538
|
-
self.log('No Firebase ID token was able to be extracted.',
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
550
|
+
// self.log('No Firebase ID token was able to be extracted.',
|
|
551
|
+
// 'Make sure you authenticate your request by providing either the following HTTP header:',
|
|
552
|
+
// 'Authorization: Bearer <Firebase ID Token>',
|
|
553
|
+
// 'or by passing a "__session" cookie',
|
|
554
|
+
// 'or by passing backendManagerKey or authenticationToken in the body or query', logOptions);
|
|
543
555
|
|
|
544
556
|
return _resolve(self.request.user);
|
|
545
557
|
}
|