backend-manager 3.2.96 → 3.2.97
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 +3 -2
- package/src/manager/functions/core/actions/api/general/fetch-post.js +95 -0
- package/src/manager/functions/core/actions/api/test/run-hook.js +52 -0
- package/src/manager/helpers/analytics.js +356 -170
- package/src/manager/helpers/settings.js +11 -1
- package/src/manager/index.js +45 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.97",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@firebase/rules-unit-testing": "^2.0.7",
|
|
38
38
|
"@google-cloud/storage": "^7.9.0",
|
|
39
|
+
"@octokit/rest": "^19.0.13",
|
|
39
40
|
"@sendgrid/mail": "^7.7.0",
|
|
40
41
|
"@sentry/node": "^6.19.7",
|
|
41
42
|
"busboy": "^1.6.0",
|
|
@@ -71,4 +72,4 @@
|
|
|
71
72
|
"wonderful-log": "^1.0.5",
|
|
72
73
|
"yargs": "^17.7.2"
|
|
73
74
|
}
|
|
74
|
-
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const { Octokit } = require('@octokit/rest');
|
|
2
|
+
|
|
3
|
+
function Module() {
|
|
4
|
+
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
Module.prototype.main = function () {
|
|
8
|
+
const self = this;
|
|
9
|
+
const Manager = self.Manager;
|
|
10
|
+
const Api = self.Api;
|
|
11
|
+
const assistant = self.assistant;
|
|
12
|
+
const payload = self.payload;
|
|
13
|
+
|
|
14
|
+
return new Promise(async function(resolve, reject) {
|
|
15
|
+
// Setup Octokit
|
|
16
|
+
const octokit = new Octokit({
|
|
17
|
+
auth: Manager?.config?.github?.key,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Setup options
|
|
21
|
+
payload.data.payload.url = payload.data.payload.url || '';
|
|
22
|
+
|
|
23
|
+
// Check for required parameters
|
|
24
|
+
if (!payload.data.payload.url) {
|
|
25
|
+
return reject(assistant.errorify(`Missing required parameter: url`, {code: 400}));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let url;
|
|
29
|
+
try {
|
|
30
|
+
url = new URL(payload.data.payload.url);
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return reject(assistant.errorify(`Invalid URL`, {code: 400}));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get the post
|
|
36
|
+
const filename = url.pathname.replace(/blog|\//ig, '')
|
|
37
|
+
const repoInfo = assistant.parseRepo(self?.Manager?.config?.github?.repo_website);
|
|
38
|
+
const query = `title+repo:${repoInfo.user}/${repoInfo.name}+filename:${filename}`;
|
|
39
|
+
|
|
40
|
+
assistant.log('Running search', query, repoInfo);
|
|
41
|
+
|
|
42
|
+
// Using octokit, search the repo for the file matching the url
|
|
43
|
+
// https://stackoverflow.com/questions/25564760/how-can-i-search-file-name-in-specific-github-repository
|
|
44
|
+
// https://docs.github.com/en/search-github/searching-on-github/searching-code#search-by-filename
|
|
45
|
+
const results = await octokit.rest.search.code({
|
|
46
|
+
q: query,
|
|
47
|
+
}).catch(e => e);
|
|
48
|
+
|
|
49
|
+
// Log
|
|
50
|
+
assistant.log('Results', results);
|
|
51
|
+
|
|
52
|
+
// Check for errors
|
|
53
|
+
if (results instanceof Error) {
|
|
54
|
+
return reject(assistant.errorify(`Error searching for post: ${results}`, {code: 500}));
|
|
55
|
+
} else if (results?.data?.total_count === 0) {
|
|
56
|
+
return reject(assistant.errorify(`Post not found`, {code: 404}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Get the first results
|
|
60
|
+
const firstResult = results.data.items[0];
|
|
61
|
+
|
|
62
|
+
// Fetch the content of the post
|
|
63
|
+
const post = await octokit.rest.repos.getContent({
|
|
64
|
+
owner: repoInfo.user,
|
|
65
|
+
repo: repoInfo.name,
|
|
66
|
+
path: firstResult.path,
|
|
67
|
+
}).catch(e => e);
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// Log
|
|
71
|
+
assistant.log('Post', post);
|
|
72
|
+
|
|
73
|
+
// Check for errors
|
|
74
|
+
if (post instanceof Error) {
|
|
75
|
+
return reject(assistant.errorify(`Error fetching post: ${post}`, {code: 500}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Decode the content
|
|
79
|
+
const fullContent = Buffer.from(post.data.content, 'base64').toString();
|
|
80
|
+
const splitContent = fullContent.split('---');
|
|
81
|
+
const frontmatter = splitContent[1].trim();
|
|
82
|
+
const body = splitContent.slice(2).join('---').trim();
|
|
83
|
+
|
|
84
|
+
// Return
|
|
85
|
+
return resolve({
|
|
86
|
+
data: {
|
|
87
|
+
frontmatter: frontmatter,
|
|
88
|
+
body: body,
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
module.exports = Module;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function Module() {
|
|
2
|
+
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
Module.prototype.main = function () {
|
|
6
|
+
const self = this;
|
|
7
|
+
const Manager = self.Manager;
|
|
8
|
+
const Api = self.Api;
|
|
9
|
+
const assistant = self.assistant;
|
|
10
|
+
const payload = self.payload;
|
|
11
|
+
|
|
12
|
+
return new Promise(async function(resolve, reject) {
|
|
13
|
+
// Log
|
|
14
|
+
assistant.log('assistant.cwd', assistant.cwd)
|
|
15
|
+
|
|
16
|
+
// If not dev, quit
|
|
17
|
+
if (!assistant.isDevelopment()) {
|
|
18
|
+
return reject(assistant.errorify(`This command is only available in development mode`, {code: 401}));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check for required options
|
|
22
|
+
if (!payload.data.payload.path) {
|
|
23
|
+
return reject(assistant.errorify(`Missing required parameter: path`, {code: 400}));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Load the hook
|
|
27
|
+
let hook;
|
|
28
|
+
try {
|
|
29
|
+
hook = (new (require(pathify(`${assistant.cwd}/${payload.data.payload.path}.js`)))()).main(Manager);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
hook = (new (require(pathify(`${assistant.cwd}/methods/hooks/${payload.data.payload.path}.js`)))()).main(Manager);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//
|
|
35
|
+
if (payload.data.payload.status >= 200 && payload.data.payload.status <= 299) {
|
|
36
|
+
return resolve({data: payload.data.payload.response, status: payload.data.payload.status});
|
|
37
|
+
} else if (payload.data.payload.status >= 400 && payload.data.payload.status <= 599) {
|
|
38
|
+
return reject(assistant.errorify(payload.data.payload.response || 'Unknown error message provided', {code: payload.data.payload.status}));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function pathify(path) {
|
|
44
|
+
const fixed = path
|
|
45
|
+
// Replace .js
|
|
46
|
+
.replace('.js', '')
|
|
47
|
+
|
|
48
|
+
// Return
|
|
49
|
+
return `${fixed}.js`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = Module;
|
|
@@ -1,235 +1,421 @@
|
|
|
1
|
-
// const ua = require('universal-analytics');
|
|
2
|
-
const get = require('lodash/get');
|
|
3
1
|
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}/;
|
|
2
|
+
const moment = require('moment');
|
|
6
3
|
let uuidv5;
|
|
7
4
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
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}/;
|
|
6
|
+
const BLOCKED_USER_AGENTS = [
|
|
7
|
+
/(node-fetch)/ig,
|
|
8
|
+
];
|
|
11
9
|
|
|
12
10
|
function Analytics(Manager, options) {
|
|
13
11
|
const self = this;
|
|
14
|
-
self.Manager = Manager;
|
|
15
|
-
// self._request = self.Manager._inner || {};
|
|
16
12
|
|
|
17
|
-
// Set
|
|
18
|
-
|
|
19
|
-
self.analyticsId = self?.Manager?.config?.google_analytics?.id;
|
|
20
|
-
self.analyticsSecret = self?.Manager?.config?.google_analytics?.secret;
|
|
13
|
+
// Set initialized
|
|
14
|
+
self.initialized = false;
|
|
21
15
|
|
|
22
16
|
// Fix options
|
|
23
17
|
options = options || {};
|
|
24
18
|
|
|
25
19
|
// Set properties
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
self.Manager = Manager;
|
|
21
|
+
self.assistant = options?.assistant || Manager.Assistant();
|
|
22
|
+
|
|
23
|
+
// Set request properties
|
|
24
|
+
self.request = {
|
|
25
|
+
ip: self.assistant?.request?.geolocation?.ip || '127.0.0.1',
|
|
26
|
+
country: self.assistant?.request?.geolocation?.country || '',
|
|
27
|
+
city: self.assistant?.request?.geolocation?.city || '',
|
|
28
|
+
referrer: self.assistant?.request?.referrer || '',
|
|
29
|
+
userAgent: self.assistant?.request?.client?.userAgent || '',
|
|
30
|
+
language: (self.assistant?.request?.client?.language || '').split(',')[0],
|
|
31
|
+
mobile: self.assistant?.request?.client?.mobile || false,
|
|
32
|
+
platform: self.assistant?.request?.client?.platform || '',
|
|
33
|
+
name: self.assistant?.meta?.name || '',
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
// Remove blacklisted user agents
|
|
37
|
+
self.request.userAgent = BLOCKED_USER_AGENTS.some((regex) => self.request.userAgent.match(regex))
|
|
38
|
+
? ''
|
|
39
|
+
: self.request.userAgent;
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// Fix options
|
|
42
|
+
options.dataSource = options.dataSource || 'server';
|
|
43
|
+
options.uuid = options.uuid || self.request.ip || Manager.SERVER_UUID;
|
|
44
|
+
options.isDevelopment = typeof options.isDevelopment === 'undefined' ? self.assistant.isDevelopment() : options.isDevelopment;
|
|
45
|
+
options.pageview = typeof options.pageview === 'undefined' ? true : options.pageview;
|
|
46
|
+
options.version = options.version || Manager.package.version;
|
|
47
|
+
options.userProperties = options.userProperties || {};
|
|
48
|
+
|
|
49
|
+
// Set user
|
|
50
|
+
// https://www.optimizesmart.com/how-to-create-and-use-user-properties-in-ga4/
|
|
51
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/user-properties?client_type=gtag
|
|
52
|
+
// https://support.google.com/analytics/answer/12980150?hl=en&co=GENIE.Platform%3DAndroid
|
|
53
|
+
const authUser = self.assistant?.usage?.user;
|
|
54
|
+
self.user = {
|
|
55
|
+
app_version: {
|
|
56
|
+
value: options.version,
|
|
57
|
+
},
|
|
58
|
+
// browser: {
|
|
59
|
+
// value: self.request.userAgent,
|
|
60
|
+
// },
|
|
61
|
+
device_category: {
|
|
62
|
+
value: self.request.mobile ? 'mobile' : 'desktop',
|
|
63
|
+
},
|
|
64
|
+
// device_model: {
|
|
65
|
+
// value: 'None',
|
|
66
|
+
// },
|
|
67
|
+
operating_system: {
|
|
68
|
+
value: self.request.platform,
|
|
69
|
+
},
|
|
70
|
+
// os_version: {
|
|
71
|
+
// value: 'None',
|
|
72
|
+
// },
|
|
73
|
+
// os_with_version: {
|
|
74
|
+
// value: 'None',
|
|
75
|
+
// },
|
|
76
|
+
platform: {
|
|
77
|
+
value: 'web',
|
|
78
|
+
},
|
|
79
|
+
// screen_resolution: {
|
|
80
|
+
// value: 'None',
|
|
81
|
+
// },
|
|
82
|
+
age: {
|
|
83
|
+
value: authUser?.personal?.birthday?.timestampUNIX
|
|
84
|
+
? new Date().getFullYear() - new Date(authUser?.personal?.birthday?.timestampUNIX).getFullYear()
|
|
85
|
+
: 'None',
|
|
86
|
+
},
|
|
87
|
+
country: {
|
|
88
|
+
value: self.request.country
|
|
89
|
+
},
|
|
90
|
+
city: {
|
|
91
|
+
value: self.request.city
|
|
92
|
+
},
|
|
93
|
+
gender: {
|
|
94
|
+
value: authUser?.personal?.gender
|
|
95
|
+
? authUser?.personal?.gender
|
|
96
|
+
: 'None',
|
|
97
|
+
},
|
|
98
|
+
// interests: {
|
|
99
|
+
// value: 'None',
|
|
100
|
+
// },
|
|
101
|
+
language: {
|
|
102
|
+
value: self.request.language,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// TODO
|
|
106
|
+
// Add custom events for user properties, like plan ID, etc, draw from self.assistant.usage, etc
|
|
107
|
+
authenticated: {
|
|
108
|
+
value: authUser?.auth?.uid ? true : false,
|
|
109
|
+
},
|
|
110
|
+
plan_id: {
|
|
111
|
+
value: authUser?.plan?.id || 'basic',
|
|
112
|
+
},
|
|
113
|
+
plan_trial_activated: {
|
|
114
|
+
value: authUser?.plan?.trial?.activated || false,
|
|
115
|
+
},
|
|
116
|
+
activity_created: {
|
|
117
|
+
value: moment(authUser?.activity?.created?.timestampUNIX
|
|
118
|
+
? authUser?.activity?.created?.timestamp
|
|
119
|
+
: self.assistant.meta.startTime.timestamp).format('YYYY-MM-DD'),
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// ds? 'app
|
|
123
|
+
// uid?
|
|
124
|
+
// uip?
|
|
125
|
+
// ua?
|
|
126
|
+
// dr? (referrer)
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Merge user properties
|
|
130
|
+
self.user = {
|
|
131
|
+
...self.user,
|
|
132
|
+
...options.userProperties,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Set id and secret
|
|
136
|
+
self.analyticsId = self?.Manager?.config?.google_analytics?.id;
|
|
137
|
+
self.analyticsSecret = self?.Manager?.config?.google_analytics?.secret;
|
|
44
138
|
|
|
139
|
+
// Check if we have the required properties
|
|
45
140
|
if (!self.analyticsId) {
|
|
46
|
-
self.
|
|
141
|
+
self.assistant.log('analytics(): Not initializing because missing analyticsId', self.analyticsId);
|
|
47
142
|
return self;
|
|
48
143
|
} else if (!self.analyticsSecret) {
|
|
49
|
-
self.
|
|
144
|
+
self.assistant.log('analytics(): Not initializing because missing analyticsSecret', self.analyticsSecret);
|
|
50
145
|
return self;
|
|
51
146
|
}
|
|
52
147
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
148
|
+
// Automatically convert the supplied uuid to a valid uuid (in case the user supplies something else like an IP or email)
|
|
149
|
+
options.uuid = options.uuid.match(UUID_REGEX)
|
|
150
|
+
? options.uuid
|
|
151
|
+
: self.generateId(options.uuid);
|
|
57
152
|
|
|
58
|
-
//
|
|
59
|
-
|
|
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
|
-
// }
|
|
153
|
+
// Attach options
|
|
154
|
+
self.options = options;
|
|
71
155
|
|
|
72
|
-
|
|
156
|
+
// Set initialized
|
|
157
|
+
self.initialized = true;
|
|
73
158
|
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
path: self._request.name,
|
|
77
|
-
location: self._request.name,
|
|
78
|
-
host: self._request.name,
|
|
79
|
-
title: self._request.name,
|
|
80
|
-
})
|
|
81
|
-
}
|
|
159
|
+
// Send pageview if enabled
|
|
160
|
+
// .. Removed
|
|
82
161
|
|
|
162
|
+
// Return
|
|
83
163
|
return self;
|
|
84
164
|
}
|
|
85
165
|
|
|
86
166
|
Analytics.prototype.generateId = function (id) {
|
|
87
167
|
const self = this;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// Disabled because we are using the Measurement Protocol (12/19/2023)
|
|
95
|
-
Analytics.prototype.pageview = function (options) {
|
|
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
|
-
// }
|
|
168
|
+
const Manager = self.Manager;
|
|
169
|
+
const assistant = self.assistant;
|
|
170
|
+
const options = self.options;
|
|
171
|
+
const request = self.request;
|
|
172
|
+
const user = self.user;
|
|
109
173
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
// // dl: options.location,
|
|
113
|
-
// // dh: options.host,
|
|
114
|
-
// // dt: options.title,
|
|
115
|
-
// // }).send();
|
|
174
|
+
// Load uuidv5
|
|
175
|
+
uuidv5 = uuidv5 || require('uuid').v5;
|
|
116
176
|
|
|
117
|
-
//
|
|
177
|
+
// Get namespace
|
|
178
|
+
const namespace = Manager?.config?.backend_manager?.namespace || undefined;
|
|
118
179
|
|
|
119
|
-
//
|
|
180
|
+
// Generate id
|
|
181
|
+
return id && namespace
|
|
182
|
+
? uuidv5(id, namespace)
|
|
183
|
+
: undefined;
|
|
120
184
|
};
|
|
121
185
|
|
|
122
|
-
Analytics.prototype.event = function (
|
|
186
|
+
Analytics.prototype.event = function (payload) {
|
|
123
187
|
const self = this;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
options = options
|
|
127
|
-
|
|
128
|
-
|
|
188
|
+
const Manager = self.Manager;
|
|
189
|
+
const assistant = self.assistant;
|
|
190
|
+
const options = self.options;
|
|
191
|
+
const request = self.request;
|
|
192
|
+
const user = self.user;
|
|
193
|
+
|
|
194
|
+
// Fix payload
|
|
195
|
+
// https://support.google.com/analytics/answer/13316687?hl=en#zippy=%2Cweb
|
|
196
|
+
// https://support.google.com/analytics/answer/9268042?sjid=4476481583372132143-NC
|
|
197
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#screen_view
|
|
198
|
+
payload = payload || {};
|
|
199
|
+
payload.name = (payload.name || '')
|
|
200
|
+
// Replace slashes and spaces with underscores
|
|
201
|
+
.replace(/\/| /g, '_')
|
|
202
|
+
// Remove leading and trailing underscores
|
|
203
|
+
.replace(/^_+|_+$/g, '')
|
|
204
|
+
// Remove multiple underscores
|
|
205
|
+
.replace(/_+/g, '_');
|
|
206
|
+
payload.params = payload.params || {};
|
|
207
|
+
|
|
208
|
+
// Check if initialized
|
|
209
|
+
if (!self.initialized) {
|
|
129
210
|
return self;
|
|
130
|
-
} else if (
|
|
131
|
-
|
|
211
|
+
} else if (options.isDevelopment) {
|
|
212
|
+
assistant.log('analytics().event(): Skipping because in development', options.uuid, JSON.stringify(payload));
|
|
132
213
|
return self;
|
|
133
214
|
}
|
|
134
215
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
216
|
+
// https://stackoverflow.com/questions/71871458/how-to-send-user-properties-to-measurement-protocol-google-analytics-4
|
|
217
|
+
|
|
218
|
+
// USer properties
|
|
219
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/user-properties?client_type=gtag
|
|
220
|
+
|
|
221
|
+
// raw
|
|
222
|
+
// https://stackoverflow.com/questions/69105735/google-analytics-4-measurement-protocol-api-used-without-gtag-js-or-firebase
|
|
223
|
+
|
|
224
|
+
// Fix payload
|
|
225
|
+
payload.params.event_source = options.dataSource;
|
|
226
|
+
payload.params.page_location = request.name; // Supposed to be domain
|
|
227
|
+
// payload.params.page_location = `${INSERT DOMAIN HERE}${request.name}`; // Supposed to be domain
|
|
228
|
+
payload.params.page_title = request.name; // Supposed to be title
|
|
229
|
+
payload.params.ip_override = request.ip;
|
|
230
|
+
payload.params.user_agent = request.userAgent;
|
|
231
|
+
payload.params.page_referrer = request.referrer;
|
|
232
|
+
// https://stackoverflow.com/questions/70708893/google-analytics-4-measurement-protocol-shows-events-but-no-users/71811327#71811327
|
|
233
|
+
payload.params.engagement_time_msec = new Date().getTime() - new Date(assistant.meta.startTime.timestamp).getTime();
|
|
234
|
+
// payload.params.engagement_time_msec = 1;
|
|
235
|
+
payload.params.debug_mode = false;
|
|
236
|
+
payload.params.session_id = assistant.id;
|
|
237
|
+
// payload.params.campaign = 'your_campaign';
|
|
238
|
+
// payload.params.source = 'your_source';
|
|
239
|
+
// payload.params.medium = 'your_medium';
|
|
240
|
+
// payload.params.term = 'your_term';
|
|
241
|
+
// payload.params.content = 'your_content';
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// https://stackoverflow.com/questions/75998626/city-is-not-populating-in-ga4-measurement-protocol-api
|
|
245
|
+
// {
|
|
246
|
+
// "client_id": "6909975079.1681323722",
|
|
247
|
+
// "events": [
|
|
248
|
+
// {
|
|
249
|
+
// "name": "page_view",
|
|
250
|
+
// "params": {
|
|
251
|
+
// "page_title": "Wedding: QR Code",
|
|
252
|
+
// "hostname": "scan.example.com",
|
|
253
|
+
// "landing_page": "/eRCU",
|
|
254
|
+
// "page_location": "https://scan.example.com/eRCU",
|
|
255
|
+
// "page_referrer": "https://scan.example.com/eRCU",
|
|
256
|
+
// "city": "Mumbai",
|
|
257
|
+
// "continent_code": "AS",
|
|
258
|
+
// "country_code": "IN",
|
|
259
|
+
// "country": "India",
|
|
260
|
+
// "latitude": "19.0748",
|
|
261
|
+
// "longitude": "72.8856",
|
|
262
|
+
// "session_id": 1681313623,
|
|
263
|
+
// "engagement_time_msec": 264,
|
|
264
|
+
// "ip_address": "XX.36.XXX.14"
|
|
265
|
+
// }
|
|
266
|
+
// }
|
|
267
|
+
// ]
|
|
268
|
+
// }
|
|
160
269
|
|
|
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
270
|
|
|
271
|
+
// Build url and body
|
|
272
|
+
const url = `https://www.google-analytics.com/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`;
|
|
201
273
|
const body = {
|
|
202
|
-
client_id: self.
|
|
203
|
-
user_id: self.
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
274
|
+
client_id: self.options.uuid,
|
|
275
|
+
user_id: self.options.uuid,
|
|
276
|
+
// timestamp_micros: new Date().getTime() * 1000,
|
|
277
|
+
user_properties: user,
|
|
278
|
+
// consent: {},
|
|
279
|
+
// non_personalized_ads: false,
|
|
280
|
+
events: [payload],
|
|
207
281
|
}
|
|
208
282
|
|
|
209
|
-
// Log
|
|
210
|
-
if (
|
|
211
|
-
|
|
283
|
+
// Log full payload
|
|
284
|
+
if (assistant.isDevelopment()) {
|
|
285
|
+
assistant.log('analytics().event(): Sending...', url, JSON.stringify(body));
|
|
212
286
|
}
|
|
213
287
|
|
|
214
288
|
// Send event
|
|
215
|
-
fetch(
|
|
216
|
-
// fetch(`https://www.google-analytics.com/debug/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`, {
|
|
289
|
+
fetch(url, {
|
|
217
290
|
method: 'post',
|
|
218
291
|
response: 'text',
|
|
219
292
|
tries: 2,
|
|
220
293
|
timeout: 30000,
|
|
294
|
+
// headers: {
|
|
295
|
+
// "Content-Type": "application/json"
|
|
296
|
+
// },
|
|
221
297
|
body: body,
|
|
222
298
|
})
|
|
223
299
|
.then((r) => {
|
|
224
|
-
if (
|
|
225
|
-
|
|
300
|
+
if (assistant.isDevelopment()) {
|
|
301
|
+
assistant.log('analytics().event(): Success', r);
|
|
226
302
|
}
|
|
227
303
|
})
|
|
228
304
|
.catch((e) => {
|
|
229
|
-
|
|
305
|
+
assistant.error('analytics().event(): Failed', e);
|
|
230
306
|
});
|
|
231
307
|
|
|
308
|
+
// Return
|
|
232
309
|
return self;
|
|
233
310
|
};
|
|
234
311
|
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
// Analytics.prototype.send = function (event) {
|
|
316
|
+
// const self = this;
|
|
317
|
+
|
|
318
|
+
// // Check if initialized
|
|
319
|
+
// if (!self.initialized) {
|
|
320
|
+
// return self;
|
|
321
|
+
// } else if (self.options.isDevelopment) {
|
|
322
|
+
// assistant.log('analytics(): Skipping Analytics.event() because in development', self.options.uuid, event);
|
|
323
|
+
// return self;
|
|
324
|
+
// }
|
|
325
|
+
|
|
326
|
+
// /*
|
|
327
|
+
// https://stackoverflow.com/questions/68773179/what-should-the-client-id-be-when-sending-events-to-google-analytics-4-using-the
|
|
328
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
332
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#recommended_parameters_for_reports
|
|
333
|
+
// https://stackoverflow.com/questions/43049662/how-to-send-measurement-protocol-if-there-is-no-clientid
|
|
334
|
+
|
|
335
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=gtag
|
|
336
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag
|
|
337
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events
|
|
338
|
+
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/ua-feature-matrix
|
|
339
|
+
// */
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
// /*
|
|
343
|
+
|
|
344
|
+
// */
|
|
345
|
+
|
|
346
|
+
// // Format event
|
|
347
|
+
// // event = event || {};
|
|
348
|
+
// // event.name = event.name || '';
|
|
349
|
+
// // event.params = event.params || {};
|
|
350
|
+
// // event.params.engagement_time_msec = `${new Date().getTime() - new Date(self.assistant.meta.startTime.timestamp).getTime()}`;
|
|
351
|
+
// // event.params.event_source = self.options.dataSource;
|
|
352
|
+
// // event.params.ip_override = self.request.ip;
|
|
353
|
+
// // event.params.user_agent = self.request.userAgent;
|
|
354
|
+
// // event.params.page_location = self.request.name;
|
|
355
|
+
// // event.params.page_referrer = self.request.referrer;
|
|
356
|
+
// // event.params.page_title = self.request.name;
|
|
357
|
+
|
|
358
|
+
// event = event || {};
|
|
359
|
+
// event.name = event.name || '';
|
|
360
|
+
// event.params = event.params || {};
|
|
361
|
+
// // event.params.session_id = self.assistant.id;
|
|
362
|
+
// // event.params.engagement_time_msec = `${new Date().getTime() - new Date(self.assistant.meta.startTime.timestamp).getTime()}`;
|
|
363
|
+
// // event.params.event_source = self.options.dataSource;
|
|
364
|
+
// // event.params.ip_override = self.request.ip;
|
|
365
|
+
// // event.params.user_agent = self.request.userAgent;
|
|
366
|
+
// // event.params.page_location = self.request.name;
|
|
367
|
+
// // event.params.page_referrer = self.request.referrer;
|
|
368
|
+
// // event.params.page_title = self.request.name;
|
|
369
|
+
|
|
370
|
+
// // "event_source": "server",
|
|
371
|
+
// // "page_location": "https:\/\/www.yourdomain.com\/page2",
|
|
372
|
+
// // "page_referrer": "\/page1",
|
|
373
|
+
// // "page_title": "Page 2",
|
|
374
|
+
// // "ip_override": "xxx.xxx.xxx.0",
|
|
375
|
+
// // "campaign": "your_campaign",
|
|
376
|
+
// // "source": "your_source",
|
|
377
|
+
// // "medium": "your_medium",
|
|
378
|
+
// // "term": "your_term",
|
|
379
|
+
// // "content": "your_content"
|
|
380
|
+
|
|
381
|
+
// const url = `https://www.google-analytics.com/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`;
|
|
382
|
+
// const body = {
|
|
383
|
+
// client_id: self.options.uuid,
|
|
384
|
+
// user_id: self.options.uuid,
|
|
385
|
+
// // timestamp_micros: new Date().getTime() * 1000,
|
|
386
|
+
// user_properties: {},
|
|
387
|
+
// // consent: {},
|
|
388
|
+
// // non_personalized_ads: false,
|
|
389
|
+
// events: [event],
|
|
390
|
+
// }
|
|
391
|
+
|
|
392
|
+
// // Log
|
|
393
|
+
// if (self.assistant.isDevelopment()) {
|
|
394
|
+
// assistant.log('analytics().send(): Sending...', url, JSON.stringify(body));
|
|
395
|
+
// }
|
|
396
|
+
|
|
397
|
+
// // Send event
|
|
398
|
+
// fetch(url, {
|
|
399
|
+
// // fetch(`https://www.google-analytics.com/debug/mp/collect?measurement_id=${self.analyticsId}&api_secret=${self.analyticsSecret}`, {
|
|
400
|
+
// method: 'post',
|
|
401
|
+
// response: 'text',
|
|
402
|
+
// tries: 2,
|
|
403
|
+
// timeout: 30000,
|
|
404
|
+
// // headers: {
|
|
405
|
+
// // "Content-Type": "application/json"
|
|
406
|
+
// // },
|
|
407
|
+
// body: body,
|
|
408
|
+
// })
|
|
409
|
+
// .then((r) => {
|
|
410
|
+
// if (self.assistant.isDevelopment()) {
|
|
411
|
+
// assistant.log('analytics().send(): Success', r);
|
|
412
|
+
// }
|
|
413
|
+
// })
|
|
414
|
+
// .catch((e) => {
|
|
415
|
+
// self.assistant.error('analytics().send(): Failed', e);
|
|
416
|
+
// });
|
|
417
|
+
|
|
418
|
+
// return self;
|
|
419
|
+
// };
|
|
420
|
+
|
|
235
421
|
module.exports = Analytics;
|
|
@@ -53,7 +53,17 @@ Settings.prototype.resolve = function (assistant, schema, settings) {
|
|
|
53
53
|
// assistant.log('resolvedValue:', resolvedValue);
|
|
54
54
|
|
|
55
55
|
// Check if this node is marked as required
|
|
56
|
-
|
|
56
|
+
let isRequired = false;
|
|
57
|
+
if (typeof schemaNode.required === 'function') {
|
|
58
|
+
isRequired = schemaNode.required(assistant);
|
|
59
|
+
} else if (typeof schemaNode.required === 'boolean') {
|
|
60
|
+
isRequired = schemaNode.required;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// assistant.log('isRequired:', isRequired);
|
|
64
|
+
|
|
65
|
+
// If the key is required and the original value is undefined, throw an error
|
|
66
|
+
if (isRequired && typeof originalValue === 'undefined') {
|
|
57
67
|
throw assistant.errorify(`Required key {${path}} is missing in settings`, {code: 400});
|
|
58
68
|
}
|
|
59
69
|
|
package/src/manager/index.js
CHANGED
|
@@ -436,6 +436,7 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
436
436
|
self.storage();
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
// Fetch stats
|
|
439
440
|
if (self.assistant.isDevelopment() && options.fetchStats) {
|
|
440
441
|
setTimeout(function () {
|
|
441
442
|
self.assistant.log('Fetching meta/stats...');
|
|
@@ -445,9 +446,52 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
445
446
|
.then(doc => {
|
|
446
447
|
self.assistant.log('meta/stats', doc.data());
|
|
447
448
|
})
|
|
448
|
-
},
|
|
449
|
+
}, 100);
|
|
449
450
|
}
|
|
450
451
|
|
|
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
|
+
// });
|
|
493
|
+
|
|
494
|
+
// Return
|
|
451
495
|
return self;
|
|
452
496
|
};
|
|
453
497
|
|