backend-manager 3.0.63 → 3.1.0
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 +2 -3
- package/src/manager/functions/core/cron/daily/reset-usage.js +9 -0
- package/src/manager/helpers/analytics copy.js +217 -0
- package/src/manager/helpers/analytics.js +152 -54
- package/src/manager/helpers/assistant.js +110 -28
- package/src/manager/helpers/middleware.js +70 -0
- package/src/manager/helpers/usage.js +12 -2
- package/src/manager/index.js +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -64,10 +64,9 @@
|
|
|
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",
|
|
71
70
|
"yargs": "^17.7.2"
|
|
72
71
|
}
|
|
73
|
-
}
|
|
72
|
+
}
|
|
@@ -163,6 +163,15 @@ Module.prototype.clearFirestore = function() {
|
|
|
163
163
|
})
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// Clear temporary/usage in firestore by deleting the doc
|
|
167
|
+
await libraries.admin.firestore().doc(`temporary/usage`).delete()
|
|
168
|
+
.then(r => {
|
|
169
|
+
assistant.log(`cron/daily/reset-usage() [firestore]: Deleted temporary/usage`);
|
|
170
|
+
})
|
|
171
|
+
.catch(e => {
|
|
172
|
+
assistant.errorManager(`Error deleting temporary/usage: ${e}`, {sentry: false, send: false, log: true})
|
|
173
|
+
})
|
|
174
|
+
|
|
166
175
|
return resolve();
|
|
167
176
|
});
|
|
168
177
|
}
|
|
@@ -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
|
};
|
|
@@ -33,6 +33,11 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
33
33
|
|
|
34
34
|
const now = new Date();
|
|
35
35
|
|
|
36
|
+
// Attached libraries - used in .errorify()
|
|
37
|
+
self.analytics = null;
|
|
38
|
+
self.usage = null;
|
|
39
|
+
|
|
40
|
+
// Set meta
|
|
36
41
|
self.meta = {};
|
|
37
42
|
|
|
38
43
|
self.meta.startTime = {};
|
|
@@ -43,6 +48,7 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
43
48
|
self.meta.environment = options.environment || self.getEnvironment();
|
|
44
49
|
self.meta.type = options.functionType || process.env.FUNCTION_SIGNATURE_TYPE || 'unknown';
|
|
45
50
|
|
|
51
|
+
// Set ref
|
|
46
52
|
self.ref = {};
|
|
47
53
|
ref = ref || {};
|
|
48
54
|
self.ref.res = ref.res || {};
|
|
@@ -50,14 +56,17 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
50
56
|
self.ref.admin = ref.admin || {};
|
|
51
57
|
self.ref.functions = ref.functions || {};
|
|
52
58
|
self.ref.Manager = ref.Manager || {};
|
|
59
|
+
self.Manager = self.ref.Manager;
|
|
53
60
|
|
|
54
61
|
// Set ID
|
|
55
62
|
try {
|
|
56
|
-
self.id = self.
|
|
63
|
+
self.id = self.Manager.Utilities().randomId();
|
|
57
64
|
} catch {
|
|
58
65
|
self.id = now.getTime();
|
|
59
66
|
}
|
|
60
67
|
|
|
68
|
+
self.tag = `${self.meta.name}/${self.id}`;
|
|
69
|
+
|
|
61
70
|
// Set stuff about request
|
|
62
71
|
self.request = {};
|
|
63
72
|
self.request.referrer = (self.ref.req.headers || {}).referrer || (self.ref.req.headers || {}).referer || '';
|
|
@@ -146,6 +155,7 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
146
155
|
self.constant.pastTime.timestamp = '1999-01-01T00:00:00Z';
|
|
147
156
|
self.constant.pastTime.timestampUNIX = 915148800;
|
|
148
157
|
|
|
158
|
+
// Log options
|
|
149
159
|
if (
|
|
150
160
|
(self.meta.environment === 'development')
|
|
151
161
|
&& ((self.request.method !== 'OPTIONS') || (self.request.method === 'OPTIONS' && options.showOptionsLog))
|
|
@@ -155,8 +165,10 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
155
165
|
console.log(options.optionsLogString);
|
|
156
166
|
}
|
|
157
167
|
|
|
168
|
+
// Set tmpdir
|
|
158
169
|
self.tmpdir = path.resolve(os.tmpdir(), options.fileSavePath, uuid.v4());
|
|
159
170
|
|
|
171
|
+
// Set initialized
|
|
160
172
|
self.initialized = true;
|
|
161
173
|
|
|
162
174
|
return self;
|
|
@@ -279,7 +291,7 @@ BackendAssistant.prototype._log = function() {
|
|
|
279
291
|
const logs = [...Array.prototype.slice.call(arguments)];
|
|
280
292
|
|
|
281
293
|
// 2. Prepend log prefix log string
|
|
282
|
-
logs.unshift(`[${self.
|
|
294
|
+
logs.unshift(`[${self.tag} @ ${new Date().toISOString()}]:`);
|
|
283
295
|
|
|
284
296
|
// 3. Pass along arguments to console.log
|
|
285
297
|
if (logs[1] === 'error') {
|
|
@@ -335,22 +347,36 @@ BackendAssistant.prototype._log = function() {
|
|
|
335
347
|
BackendAssistant.prototype.errorManager = function(e, options) {
|
|
336
348
|
const self = this;
|
|
337
349
|
|
|
350
|
+
// Set options
|
|
338
351
|
options = options || {};
|
|
339
|
-
options.code = typeof options.code === 'undefined'
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
options.
|
|
343
|
-
|
|
352
|
+
options.code = typeof options.code === 'undefined'
|
|
353
|
+
? 500
|
|
354
|
+
: options.code;
|
|
355
|
+
options.log = typeof options.log === 'undefined'
|
|
356
|
+
? true
|
|
357
|
+
: options.log;
|
|
358
|
+
options.sentry = typeof options.sentry === 'undefined'
|
|
359
|
+
? true
|
|
360
|
+
: options.sentry;
|
|
361
|
+
options.send = typeof options.send === 'undefined'
|
|
362
|
+
? true
|
|
363
|
+
: options.send;
|
|
364
|
+
|
|
365
|
+
// Construct error
|
|
344
366
|
const newError = e instanceof Error
|
|
345
367
|
? e
|
|
346
368
|
: new Error(stringify(e));
|
|
347
369
|
|
|
370
|
+
options.code = newError.code || options.code;
|
|
371
|
+
|
|
348
372
|
// Attach properties
|
|
349
373
|
Object.keys(options)
|
|
350
374
|
.forEach((item, i) => {
|
|
351
|
-
Object.assign(newError
|
|
375
|
+
Object.assign(newError, { [item]: options[item] });
|
|
352
376
|
});
|
|
353
377
|
|
|
378
|
+
// Attach properties
|
|
379
|
+
_attachHeaderProperties(self, options);
|
|
354
380
|
|
|
355
381
|
// Log the error
|
|
356
382
|
if (options.log) {
|
|
@@ -359,12 +385,19 @@ BackendAssistant.prototype.errorManager = function(e, options) {
|
|
|
359
385
|
|
|
360
386
|
// Send error to Sentry
|
|
361
387
|
if (options.sentry) {
|
|
362
|
-
self.
|
|
388
|
+
self.Manager.libraries.sentry.captureException(newError);
|
|
363
389
|
}
|
|
364
390
|
|
|
365
391
|
// Quit and respond to the request
|
|
366
392
|
if (options.send && self.ref.res && self.ref.res.status) {
|
|
367
|
-
self.ref.res
|
|
393
|
+
self.ref.res
|
|
394
|
+
.status(options.code)
|
|
395
|
+
.send((
|
|
396
|
+
newError?.message
|
|
397
|
+
// ? newError.stack
|
|
398
|
+
? newError.message
|
|
399
|
+
: newError
|
|
400
|
+
) || 'Unknown error');
|
|
368
401
|
}
|
|
369
402
|
|
|
370
403
|
return {
|
|
@@ -372,6 +405,38 @@ BackendAssistant.prototype.errorManager = function(e, options) {
|
|
|
372
405
|
}
|
|
373
406
|
}
|
|
374
407
|
|
|
408
|
+
BackendAssistant.prototype.errorify = BackendAssistant.prototype.errorManager;
|
|
409
|
+
|
|
410
|
+
BackendAssistant.prototype.respond = function(response, options) {
|
|
411
|
+
const self = this;
|
|
412
|
+
|
|
413
|
+
// Set options
|
|
414
|
+
options = options || {};
|
|
415
|
+
options.code = typeof options.code === 'undefined'
|
|
416
|
+
? 200
|
|
417
|
+
: options.code;
|
|
418
|
+
options.log = typeof options.log === 'undefined'
|
|
419
|
+
? true
|
|
420
|
+
: options.log;
|
|
421
|
+
|
|
422
|
+
// Attach properties
|
|
423
|
+
_attachHeaderProperties(self, options);
|
|
424
|
+
|
|
425
|
+
// Log the error
|
|
426
|
+
if (options.log) {
|
|
427
|
+
self.log(`Responding with ${options.code} code:`, JSON.stringify(response));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Send response
|
|
431
|
+
self.ref.res.status(options.code);
|
|
432
|
+
|
|
433
|
+
if (typeof response === 'string') {
|
|
434
|
+
self.ref.res.send(response);
|
|
435
|
+
} else {
|
|
436
|
+
self.ref.res.json(response);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
375
440
|
function stringify(e) {
|
|
376
441
|
if (typeof e === 'string') {
|
|
377
442
|
return e;
|
|
@@ -380,6 +445,21 @@ function stringify(e) {
|
|
|
380
445
|
}
|
|
381
446
|
}
|
|
382
447
|
|
|
448
|
+
function _attachHeaderProperties(self, options) {
|
|
449
|
+
// Create headers
|
|
450
|
+
const headers = {
|
|
451
|
+
code: options.code,
|
|
452
|
+
tag: self.tag,
|
|
453
|
+
usage: {
|
|
454
|
+
current: self?.usage?.getUsage() || {},
|
|
455
|
+
limits: self?.usage?.getLimit() || {},
|
|
456
|
+
},
|
|
457
|
+
additional: options.additional || {},
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Attach properties
|
|
461
|
+
self.ref.res.header('bm-properties', JSON.stringify(headers));
|
|
462
|
+
}
|
|
383
463
|
|
|
384
464
|
BackendAssistant.prototype.authenticate = async function (options) {
|
|
385
465
|
const self = this;
|
|
@@ -410,11 +490,11 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
410
490
|
}
|
|
411
491
|
}
|
|
412
492
|
|
|
413
|
-
if (req
|
|
493
|
+
if (req?.headers?.authorization?.startsWith('Bearer ')) {
|
|
414
494
|
// Read the ID Token from the Authorization header.
|
|
415
495
|
idToken = req.headers.authorization.split('Bearer ')[1];
|
|
416
496
|
self.log('Found "Authorization" header', idToken, logOptions);
|
|
417
|
-
} else if (req
|
|
497
|
+
} else if (req?.cookies?.__session) {
|
|
418
498
|
// Read the ID Token from cookie.
|
|
419
499
|
idToken = req.cookies.__session;
|
|
420
500
|
self.log('Found "__session" cookie', idToken, logOptions);
|
|
@@ -422,7 +502,7 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
422
502
|
// Check with custom BEM Token
|
|
423
503
|
let storedApiKey;
|
|
424
504
|
try {
|
|
425
|
-
const workingConfig = _.get(self.
|
|
505
|
+
const workingConfig = _.get(self.Manager, 'config') || functions.config();
|
|
426
506
|
storedApiKey = _.get(workingConfig, 'backend_manager.key', '')
|
|
427
507
|
} catch (e) {
|
|
428
508
|
|
|
@@ -439,29 +519,31 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
439
519
|
}
|
|
440
520
|
} else if (options.apiKey) {
|
|
441
521
|
self.log('Found "options.apiKey"', options.apiKey, logOptions);
|
|
522
|
+
|
|
442
523
|
if (options.apiKey.includes('test')) {
|
|
443
524
|
return _resolve(self.request.user);
|
|
444
525
|
}
|
|
526
|
+
|
|
445
527
|
await admin.firestore().collection(`users`)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
528
|
+
.where('api.privateKey', '==', options.apiKey)
|
|
529
|
+
.get()
|
|
530
|
+
.then(function(querySnapshot) {
|
|
531
|
+
querySnapshot.forEach(function(doc) {
|
|
532
|
+
self.request.user = doc.data();
|
|
533
|
+
self.request.user.authenticated = true;
|
|
534
|
+
});
|
|
535
|
+
})
|
|
536
|
+
.catch(function(error) {
|
|
537
|
+
console.error('Error getting documents: ', error);
|
|
452
538
|
});
|
|
453
|
-
})
|
|
454
|
-
.catch(function(error) {
|
|
455
|
-
console.error('Error getting documents: ', error);
|
|
456
|
-
});
|
|
457
539
|
|
|
458
540
|
return _resolve(self.request.user);
|
|
459
541
|
} else {
|
|
460
|
-
self.log('No Firebase ID token was able to be extracted.',
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
542
|
+
// self.log('No Firebase ID token was able to be extracted.',
|
|
543
|
+
// 'Make sure you authenticate your request by providing either the following HTTP header:',
|
|
544
|
+
// 'Authorization: Bearer <Firebase ID Token>',
|
|
545
|
+
// 'or by passing a "__session" cookie',
|
|
546
|
+
// 'or by passing backendManagerKey or authenticationToken in the body or query', logOptions);
|
|
465
547
|
|
|
466
548
|
return _resolve(self.request.user);
|
|
467
549
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware
|
|
3
|
+
* Used to handle middleware for the assistant
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
function Middleware(m) {
|
|
9
|
+
const self = this;
|
|
10
|
+
|
|
11
|
+
self.Manager = m;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Middleware.prototype.run = function (library, req, res, options) {
|
|
15
|
+
const self = this;
|
|
16
|
+
const Manager = self.Manager;
|
|
17
|
+
const { cors } = Manager.libraries;
|
|
18
|
+
|
|
19
|
+
return cors(req, res, async () => {
|
|
20
|
+
const assistant = Manager.Assistant({req: req, res: res});
|
|
21
|
+
|
|
22
|
+
const data = assistant.request.data;
|
|
23
|
+
const geolocation = assistant.request.geolocation;
|
|
24
|
+
const client = assistant.request.client;
|
|
25
|
+
|
|
26
|
+
// Set options
|
|
27
|
+
options = options || {};
|
|
28
|
+
options.setupAnalytics = typeof options.setupAnalytics === 'boolean' ? options.setupAnalytics : true;
|
|
29
|
+
options.setupUsage = typeof options.setupUsage === 'boolean' ? options.setupUsage : true;
|
|
30
|
+
|
|
31
|
+
// Log
|
|
32
|
+
assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city})`, JSON.stringify(data));
|
|
33
|
+
|
|
34
|
+
// Load library
|
|
35
|
+
try {
|
|
36
|
+
library = path.resolve(process.cwd(), `${library}.js`);
|
|
37
|
+
library = new (require(library))();
|
|
38
|
+
} catch (e) {
|
|
39
|
+
assistant.errorManager(`Unable to load library @ (${library}): ${e.message}`, {sentry: true, send: true, log: true});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Setup analytics
|
|
43
|
+
if (options.setupAnalytics) {
|
|
44
|
+
assistant.analytics = Manager.Analytics({
|
|
45
|
+
assistant: assistant,
|
|
46
|
+
uuid: assistant.request.geolocation.ip,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Setup usage
|
|
51
|
+
if (options.setupUsage) {
|
|
52
|
+
assistant.usage = await Manager.Usage().init(assistant);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Process
|
|
56
|
+
try {
|
|
57
|
+
library.main(assistant, req, res)
|
|
58
|
+
// .then(result => {
|
|
59
|
+
// return res.status(200).json(result);
|
|
60
|
+
// })
|
|
61
|
+
.catch(e => {
|
|
62
|
+
assistant.errorManager(e, {sentry: true, send: true, log: true});
|
|
63
|
+
});
|
|
64
|
+
} catch (e) {
|
|
65
|
+
assistant.errorManager(e, {sentry: true, send: true, log: true});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
module.exports = Middleware;
|
|
@@ -244,7 +244,11 @@ Usage.prototype.getUsage = function (path) {
|
|
|
244
244
|
const Manager = self.Manager;
|
|
245
245
|
const assistant = self.assistant;
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
if (path) {
|
|
248
|
+
return _.get(self.user, `usage.${path}.period`, 0);
|
|
249
|
+
} else {
|
|
250
|
+
return self.user.usage;
|
|
251
|
+
}
|
|
248
252
|
};
|
|
249
253
|
|
|
250
254
|
Usage.prototype.getLimit = function (path) {
|
|
@@ -252,7 +256,13 @@ Usage.prototype.getLimit = function (path) {
|
|
|
252
256
|
const Manager = self.Manager;
|
|
253
257
|
const assistant = self.assistant;
|
|
254
258
|
|
|
255
|
-
|
|
259
|
+
const key = `products.${self.options.app}-${self.user.plan.id}.limits`;
|
|
260
|
+
|
|
261
|
+
if (path) {
|
|
262
|
+
return _.get(self.app, `${key}.${path}`, 0);
|
|
263
|
+
} else {
|
|
264
|
+
return _.get(self.app, key, {});
|
|
265
|
+
}
|
|
256
266
|
};
|
|
257
267
|
|
|
258
268
|
Usage.prototype.update = function () {
|
package/src/manager/index.js
CHANGED
|
@@ -606,6 +606,12 @@ Manager.prototype.Usage = function () {
|
|
|
606
606
|
return new self.libraries.Usage(self, ...arguments);
|
|
607
607
|
};
|
|
608
608
|
|
|
609
|
+
Manager.prototype.Middleware = function () {
|
|
610
|
+
const self = this;
|
|
611
|
+
self.libraries.Middleware = self.libraries.Middleware || require('./helpers/middleware.js');
|
|
612
|
+
return new self.libraries.Middleware(self, ...arguments);
|
|
613
|
+
};
|
|
614
|
+
|
|
609
615
|
Manager.prototype.Settings = function () {
|
|
610
616
|
const self = this;
|
|
611
617
|
self.libraries.Settings = self.libraries.Settings || require('./helpers/settings.js');
|