backend-manager 4.2.8 → 4.2.10
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/.nvmrc +1 -0
- package/package.json +10 -10
- package/src/cli/cli.js +7 -0
- package/src/manager/functions/core/actions/api/admin/send-email.js +265 -0
- package/src/manager/functions/core/actions/api/admin/send-notification copy.js +327 -0
- package/src/manager/functions/core/actions/api/admin/send-notification.js +96 -71
- package/src/manager/functions/core/actions/api/general/send-email.js +0 -1
- package/src/manager/functions/core/actions/api/user/oauth2/discord.js +14 -4
- package/src/manager/functions/core/actions/api/user/oauth2/google.js +15 -5
- package/src/manager/helpers/assistant.js +5 -5
- package/src/manager/index.js +3 -0
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v18/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.10",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@firebase/rules-unit-testing": "^2.0.7",
|
|
45
|
-
"@google-cloud/storage": "^7.
|
|
45
|
+
"@google-cloud/storage": "^7.15.0",
|
|
46
46
|
"@octokit/rest": "^19.0.13",
|
|
47
47
|
"@sendgrid/mail": "^7.7.0",
|
|
48
48
|
"@sentry/node": "^6.19.7",
|
|
@@ -52,15 +52,15 @@
|
|
|
52
52
|
"cors": "^2.8.5",
|
|
53
53
|
"dotenv": "^16.4.7",
|
|
54
54
|
"express": "^4.21.2",
|
|
55
|
-
"firebase-admin": "^
|
|
56
|
-
"firebase-functions": "^6.1
|
|
55
|
+
"firebase-admin": "^13.0.2",
|
|
56
|
+
"firebase-functions": "^6.3.1",
|
|
57
57
|
"fs-jetpack": "^5.1.0",
|
|
58
|
-
"glob": "^11.0.
|
|
58
|
+
"glob": "^11.0.1",
|
|
59
59
|
"hcaptcha": "^0.1.1",
|
|
60
60
|
"inquirer": "^8.2.5",
|
|
61
61
|
"itwcw-package-analytics": "^1.0.6",
|
|
62
62
|
"json5": "^2.2.3",
|
|
63
|
-
"jwt-decode": "^
|
|
63
|
+
"jwt-decode": "^4.0.0",
|
|
64
64
|
"lodash": "^4.17.21",
|
|
65
65
|
"lowdb": "^1.0.0",
|
|
66
66
|
"mailchimp-api-v3": "^1.15.0",
|
|
@@ -69,20 +69,20 @@
|
|
|
69
69
|
"moment": "^2.30.1",
|
|
70
70
|
"nanoid": "^3.3.8",
|
|
71
71
|
"node-fetch": "^2.7.0",
|
|
72
|
-
"node-powertools": "^1.
|
|
72
|
+
"node-powertools": "^2.1.2",
|
|
73
73
|
"npm-api": "^1.0.1",
|
|
74
74
|
"paypal-server-api": "^2.0.14",
|
|
75
75
|
"pushid": "^1.0.0",
|
|
76
76
|
"resolve-account": "^1.0.26",
|
|
77
|
-
"shortid": "^2.2.
|
|
77
|
+
"shortid": "^2.2.17",
|
|
78
78
|
"sizeitup": "^1.0.9",
|
|
79
79
|
"uid-generator": "^2.0.0",
|
|
80
80
|
"ultimate-jekyll-poster": "^1.0.2",
|
|
81
81
|
"uuid": "^9.0.1",
|
|
82
|
-
"wonderful-fetch": "^1.3.
|
|
82
|
+
"wonderful-fetch": "^1.3.1",
|
|
83
83
|
"wonderful-log": "^1.0.7",
|
|
84
84
|
"wonderful-version": "^1.2.0",
|
|
85
|
-
"yaml": "^2.
|
|
85
|
+
"yaml": "^2.7.0",
|
|
86
86
|
"yargs": "^17.7.2"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
package/src/cli/cli.js
CHANGED
|
@@ -378,11 +378,18 @@ Main.prototype.setup = async function () {
|
|
|
378
378
|
// return isLocal(mine) || !(semver.gt(latest, mine));
|
|
379
379
|
// }, fix_mocha);
|
|
380
380
|
|
|
381
|
+
// Test: Do the dependencies work
|
|
382
|
+
// await self.test(`dependencies work`, function () {
|
|
383
|
+
// return true;
|
|
384
|
+
// }, NOFIX);
|
|
385
|
+
|
|
381
386
|
// Test: Does the project have a "npm start" script
|
|
382
387
|
await self.test(`has "npm start" script`, function () {
|
|
383
388
|
return self.package.scripts.start
|
|
384
389
|
}, fix_startScript);
|
|
385
390
|
|
|
391
|
+
|
|
392
|
+
|
|
386
393
|
// Test: Does the project have a "npm dist" script
|
|
387
394
|
await self.test(`has "npm dist" script`, function () {
|
|
388
395
|
return self.package.scripts.dist
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Module
|
|
2
|
+
function Module() {
|
|
3
|
+
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// Main
|
|
7
|
+
Module.prototype.main = function () {
|
|
8
|
+
const self = this;
|
|
9
|
+
|
|
10
|
+
// Shortcuts
|
|
11
|
+
const Manager = self.Manager;
|
|
12
|
+
const Api = self.Api;
|
|
13
|
+
const assistant = self.assistant;
|
|
14
|
+
const payload = self.payload;
|
|
15
|
+
|
|
16
|
+
return new Promise(async function(resolve, reject) {
|
|
17
|
+
// Load libraries
|
|
18
|
+
const fetch = Manager.require('wonderful-fetch');
|
|
19
|
+
|
|
20
|
+
// Set up response obj
|
|
21
|
+
payload.response.data = {};
|
|
22
|
+
|
|
23
|
+
// Fix notification payload
|
|
24
|
+
const email = payload.data.payload;
|
|
25
|
+
|
|
26
|
+
// Log
|
|
27
|
+
assistant.log('Resolved email payload', email)
|
|
28
|
+
|
|
29
|
+
// Check if user is admin
|
|
30
|
+
if (!payload.user.roles.admin) {
|
|
31
|
+
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Attach BEM key since we are authorized
|
|
35
|
+
email.backendManagerKey = Manager?.config?.backend_manager?.key;
|
|
36
|
+
|
|
37
|
+
// Make request to ITW email service
|
|
38
|
+
await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail', {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
response: 'json',
|
|
41
|
+
body: email,
|
|
42
|
+
})
|
|
43
|
+
.then((r) => {
|
|
44
|
+
// Set response
|
|
45
|
+
payload.response.data = r;
|
|
46
|
+
|
|
47
|
+
// Resolve
|
|
48
|
+
return resolve({data: payload.response.data})
|
|
49
|
+
})
|
|
50
|
+
.catch((e) => {
|
|
51
|
+
return reject(assistant.errorify(`Failed to send email: ${e}`, {code: 400, sentry: true}));
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// HELPERS //
|
|
58
|
+
Module.prototype.processTokens = async function (note, options) {
|
|
59
|
+
const self = this;
|
|
60
|
+
|
|
61
|
+
// Shortcuts
|
|
62
|
+
const Manager = self.Manager;
|
|
63
|
+
const Api = self.Api;
|
|
64
|
+
const assistant = self.assistant;
|
|
65
|
+
const payload = self.payload;
|
|
66
|
+
|
|
67
|
+
// Set options
|
|
68
|
+
options = options || {};
|
|
69
|
+
options.tags = options.tags || false;
|
|
70
|
+
|
|
71
|
+
// Define collection path
|
|
72
|
+
const queryConditions = options.tags
|
|
73
|
+
? [{ field: 'tags', operator: 'array-contains-any', value: options.tags }]
|
|
74
|
+
: [];
|
|
75
|
+
|
|
76
|
+
// Batch processing logic
|
|
77
|
+
await Manager.Utilities().iterateCollection(
|
|
78
|
+
async (batch, index) => {
|
|
79
|
+
let batchTokens = [];
|
|
80
|
+
|
|
81
|
+
// Collect tokens from the current batch
|
|
82
|
+
for (const doc of batch.docs) {
|
|
83
|
+
const data = doc.data();
|
|
84
|
+
batchTokens.push(data.token);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Send the batch
|
|
88
|
+
try {
|
|
89
|
+
assistant.log(`Sending batch ${index} with ${batchTokens.length} tokens.`);
|
|
90
|
+
await self.sendBatch(batchTokens, note, index);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
assistant.error(`Error sending batch ${index}`, e);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
collection: PATH_NOTIFICATIONS,
|
|
97
|
+
where: queryConditions,
|
|
98
|
+
batchSize: BATCH_SIZE,
|
|
99
|
+
log: true,
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
.then(() => {
|
|
103
|
+
assistant.log('All batches processed successfully.');
|
|
104
|
+
})
|
|
105
|
+
.catch(e => {
|
|
106
|
+
assistant.errorify(`Error during token processing: ${e}`, { code: 500, log: true });
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Sending using SDK
|
|
111
|
+
// https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-multiple-devices
|
|
112
|
+
|
|
113
|
+
// Manually sending
|
|
114
|
+
// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send
|
|
115
|
+
// https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually
|
|
116
|
+
Module.prototype.sendBatch = async function (batch, note, id) {
|
|
117
|
+
const self = this;
|
|
118
|
+
|
|
119
|
+
// Shortcuts
|
|
120
|
+
const Manager = self.Manager;
|
|
121
|
+
const Api = self.Api;
|
|
122
|
+
const assistant = self.assistant;
|
|
123
|
+
const payload = self.payload;
|
|
124
|
+
|
|
125
|
+
// Libraries
|
|
126
|
+
const { admin } = self.libraries;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Log
|
|
130
|
+
assistant.log(`Sending batch ID: ${id}`);
|
|
131
|
+
console.log('🚩🚩🚩🚩🚩', 1, ); // FLAG
|
|
132
|
+
assistant.log(`batch`, batch);
|
|
133
|
+
assistant.log(`note`, note);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const response = await admin.messaging().sendMulticast({
|
|
137
|
+
tokens: batch,
|
|
138
|
+
notification: {
|
|
139
|
+
title: 'Your Notification Title',
|
|
140
|
+
body: 'This is the notification message.'
|
|
141
|
+
},
|
|
142
|
+
data: {
|
|
143
|
+
customDataKey: 'customDataValue' // Optional custom data payload
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
assistant.log(`🚩🚩🚩🚩🚩 222 response ${id}`, response); // FLAG
|
|
148
|
+
} catch (e) {
|
|
149
|
+
assistant.error(`🚩🚩🚩🚩🚩 222 error ${id}`, e);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const response = await admin.messaging().sendMulticast({
|
|
154
|
+
tokens: batch,
|
|
155
|
+
notification: note.notification,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
assistant.log(`🚩🚩🚩🚩🚩 333 response ${id}`, response); // FLAG
|
|
159
|
+
} catch (e) {
|
|
160
|
+
assistant.error(`🚩🚩🚩🚩🚩 333 error ${id}`, e);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const messages = batch.map(token => ({
|
|
165
|
+
token: token,
|
|
166
|
+
notification: {
|
|
167
|
+
title: 'Your Notification Title',
|
|
168
|
+
body: 'This is the notification message.'
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
const response = await admin.messaging().sendEach(messages);
|
|
173
|
+
|
|
174
|
+
assistant.log(`🚩🚩🚩🚩🚩 444 response ${id}`, response); // FLAG
|
|
175
|
+
} catch (e) {
|
|
176
|
+
assistant.error(`🚩🚩🚩🚩🚩 444 error ${id}`, e);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Send the batch
|
|
180
|
+
const response = await admin.messaging().sendToDevice(batch, note);
|
|
181
|
+
|
|
182
|
+
// Log
|
|
183
|
+
assistant.log(`Sent batch ID: ${id}, Success: ${response.successCount}, Failures: ${response.failureCount}`);
|
|
184
|
+
|
|
185
|
+
// Clean bad tokens
|
|
186
|
+
if (response.failureCount > 0) {
|
|
187
|
+
await self.cleanTokens(batch, response.results, id);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Update response
|
|
191
|
+
payload.response.data.sent += (batch.length - response.failureCount);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
throw assistant.errorify(`Error sending batch ${id}: ${e}`, { code: 500, log: true });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
Module.prototype.cleanTokens = async function (batch, results, id) {
|
|
198
|
+
const self = this;
|
|
199
|
+
|
|
200
|
+
// Shortcuts
|
|
201
|
+
const Manager = self.Manager;
|
|
202
|
+
const Api = self.Api;
|
|
203
|
+
const assistant = self.assistant;
|
|
204
|
+
const payload = self.payload;
|
|
205
|
+
|
|
206
|
+
// Log
|
|
207
|
+
assistant.log(`Cleaning ${results.length} tokens of batch ID: ${id}`);
|
|
208
|
+
|
|
209
|
+
// Filter out bad tokens
|
|
210
|
+
const cleanPromises = results
|
|
211
|
+
.map((item, index) => {
|
|
212
|
+
// Check if the token is bad
|
|
213
|
+
if (item.error && BAD_TOKEN_REASONS.includes(item.error.code)) {
|
|
214
|
+
const token = batch[index];
|
|
215
|
+
|
|
216
|
+
// Log
|
|
217
|
+
assistant.log(`Found bad token: ${token} (Reason: ${item.error.code})`);
|
|
218
|
+
|
|
219
|
+
// Delete the token
|
|
220
|
+
return self.deleteToken(token, item.error.code);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Return null for valid tokens
|
|
224
|
+
return null;
|
|
225
|
+
})
|
|
226
|
+
// Filter out nulls for valid tokens
|
|
227
|
+
.filter(Boolean);
|
|
228
|
+
|
|
229
|
+
// Clean bad tokens
|
|
230
|
+
try {
|
|
231
|
+
await Promise.all(cleanPromises);
|
|
232
|
+
assistant.log(`Completed cleaning tokens for batch ID: ${id}`);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
assistant.error(`Error cleaning tokens for batch ID: ${id}`, e);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
Module.prototype.deleteToken = async function (token, errorCode) {
|
|
239
|
+
const self = this;
|
|
240
|
+
|
|
241
|
+
// Shortcuts
|
|
242
|
+
const Manager = self.Manager;
|
|
243
|
+
const Api = self.Api;
|
|
244
|
+
const assistant = self.assistant;
|
|
245
|
+
const payload = self.payload;
|
|
246
|
+
|
|
247
|
+
// Libraries
|
|
248
|
+
const { admin } = self.libraries;
|
|
249
|
+
|
|
250
|
+
// Delete the token
|
|
251
|
+
try {
|
|
252
|
+
// Delete the token
|
|
253
|
+
await admin.firestore().doc(`${PATH_NOTIFICATIONS}/${token}`).delete();
|
|
254
|
+
|
|
255
|
+
// Log
|
|
256
|
+
assistant.log(`Deleted bad token: ${token} (Reason: ${errorCode})`);
|
|
257
|
+
|
|
258
|
+
// Update response
|
|
259
|
+
payload.response.data.deleted++;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
assistant.error(`Failed to delete bad token: ${token} (Reason: ${errorCode}). Error: ${error}`);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
module.exports = Module;
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Constants
|
|
2
|
+
const PATH_NOTIFICATIONS = 'notifications';
|
|
3
|
+
const BAD_TOKEN_REASONS = [
|
|
4
|
+
'messaging/invalid-registration-token',
|
|
5
|
+
'messaging/registration-token-not-registered',
|
|
6
|
+
];
|
|
7
|
+
const BATCH_SIZE = 500;
|
|
8
|
+
|
|
9
|
+
// Module
|
|
10
|
+
function Module() {
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Main
|
|
15
|
+
Module.prototype.main = function () {
|
|
16
|
+
const self = this;
|
|
17
|
+
|
|
18
|
+
// Shortcuts
|
|
19
|
+
const Manager = self.Manager;
|
|
20
|
+
const Api = self.Api;
|
|
21
|
+
const assistant = self.assistant;
|
|
22
|
+
const payload = self.payload;
|
|
23
|
+
|
|
24
|
+
return new Promise(async function(resolve, reject) {
|
|
25
|
+
// Set up response obj
|
|
26
|
+
payload.response.data = {
|
|
27
|
+
subscribers: 0,
|
|
28
|
+
batches: 0,
|
|
29
|
+
sent: 0,
|
|
30
|
+
deleted: 0,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fix notification payload
|
|
34
|
+
// Yes, it needs to be NESTED!!! DO NOT REMOVE THE NEST!
|
|
35
|
+
const note = {
|
|
36
|
+
notification: {
|
|
37
|
+
title: payload.data.payload.title || 'Notification',
|
|
38
|
+
body: payload.data.payload.body || 'Check this out',
|
|
39
|
+
icon: payload.data.payload.icon || 'https://cdn.itwcreativeworks.com/assets/itw-creative-works/images/socials/itw-creative-works-brandmark-square-black-1024x1024.png',
|
|
40
|
+
click_action: payload.data.payload.clickAction || 'https://itwcreativeworks.com',
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Set notification payload
|
|
45
|
+
try {
|
|
46
|
+
const url = new URL(note.notification.click_action);
|
|
47
|
+
url.searchParams.set('cb', new Date().getTime());
|
|
48
|
+
note.notification.click_action = url.toString();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
reject(assistant.errorify(`Failed to add cb to URL: ${e}`, {code: 400, log: true}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Log
|
|
54
|
+
assistant.log('Resolved notification payload', note)
|
|
55
|
+
|
|
56
|
+
// Check if user is admin
|
|
57
|
+
if (!payload.user.roles.admin) {
|
|
58
|
+
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if title and body are set
|
|
62
|
+
if (!note.notification.title || !note.notification.body) {
|
|
63
|
+
return reject(assistant.errorify(`Parameters <title> and <body> required`, {code: 400, sentry: true}));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await self.processTokens(note, {tags: false})
|
|
67
|
+
.then(r => {
|
|
68
|
+
return resolve({data: payload.response.data})
|
|
69
|
+
})
|
|
70
|
+
.catch(e => {
|
|
71
|
+
return reject(assistant.errorify(`Failed to send notification: ${e}`, {code: 400, sentry: true}));
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// HELPERS //
|
|
78
|
+
Module.prototype.processTokens = async function (note, options) {
|
|
79
|
+
const self = this;
|
|
80
|
+
|
|
81
|
+
// Shortcuts
|
|
82
|
+
const Manager = self.Manager;
|
|
83
|
+
const Api = self.Api;
|
|
84
|
+
const assistant = self.assistant;
|
|
85
|
+
const payload = self.payload;
|
|
86
|
+
|
|
87
|
+
// Set options
|
|
88
|
+
options = options || {};
|
|
89
|
+
options.tags = options.tags || false;
|
|
90
|
+
|
|
91
|
+
// Define collection path
|
|
92
|
+
const queryConditions = options.tags
|
|
93
|
+
? [{ field: 'tags', operator: 'array-contains-any', value: options.tags }]
|
|
94
|
+
: [];
|
|
95
|
+
|
|
96
|
+
// Batch processing logic
|
|
97
|
+
await Manager.Utilities().iterateCollection(
|
|
98
|
+
async (batch, index) => {
|
|
99
|
+
let batchTokens = [];
|
|
100
|
+
|
|
101
|
+
// Collect tokens from the current batch
|
|
102
|
+
for (const doc of batch.docs) {
|
|
103
|
+
const data = doc.data();
|
|
104
|
+
batchTokens.push(data.token);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Send the batch
|
|
108
|
+
try {
|
|
109
|
+
assistant.log(`Sending batch ${index} with ${batchTokens.length} tokens.`);
|
|
110
|
+
await self.sendBatch(batchTokens, note, index);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
assistant.error(`Error sending batch ${index}`, e);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
collection: PATH_NOTIFICATIONS,
|
|
117
|
+
where: queryConditions,
|
|
118
|
+
batchSize: BATCH_SIZE,
|
|
119
|
+
log: true,
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
.then(() => {
|
|
123
|
+
assistant.log('All batches processed successfully.');
|
|
124
|
+
})
|
|
125
|
+
.catch(e => {
|
|
126
|
+
assistant.errorify(`Error during token processing: ${e}`, { code: 500, log: true });
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Sending using SDK
|
|
131
|
+
// https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-multiple-devices
|
|
132
|
+
|
|
133
|
+
// Manually sending
|
|
134
|
+
// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send
|
|
135
|
+
// https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually
|
|
136
|
+
Module.prototype.sendBatch = async function (batch, note, id) {
|
|
137
|
+
const self = this;
|
|
138
|
+
|
|
139
|
+
// Shortcuts
|
|
140
|
+
const Manager = self.Manager;
|
|
141
|
+
const Api = self.Api;
|
|
142
|
+
const assistant = self.assistant;
|
|
143
|
+
const payload = self.payload;
|
|
144
|
+
|
|
145
|
+
// Libraries
|
|
146
|
+
const { admin } = self.libraries;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Log
|
|
150
|
+
assistant.log(`Sending batch ID: ${id}`);
|
|
151
|
+
console.log('🚩🚩🚩🚩🚩', 1, ); // FLAG
|
|
152
|
+
assistant.log(`batch`, batch);
|
|
153
|
+
assistant.log(`note`, note);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const response = await admin.messaging().sendMulticast({
|
|
157
|
+
tokens: batch,
|
|
158
|
+
// "webpush": {
|
|
159
|
+
// "notification": {
|
|
160
|
+
// "actions": [],
|
|
161
|
+
// "tag": 123456,
|
|
162
|
+
// "title": "Titre de test",
|
|
163
|
+
// "body": "Un contenu de notification",
|
|
164
|
+
// "image": ".\/images\/icon.png",
|
|
165
|
+
// "badge": ".\/images\/badge.png",
|
|
166
|
+
// "icon": ".\/images\/icon256.png",
|
|
167
|
+
// "vibrate": [50, 200, 50],
|
|
168
|
+
// "click_action": "https://somiibo.com",
|
|
169
|
+
// },
|
|
170
|
+
// "data": {
|
|
171
|
+
// "time": "1531396372"
|
|
172
|
+
// },
|
|
173
|
+
// "headers": {
|
|
174
|
+
// "TTL": "60"
|
|
175
|
+
// }
|
|
176
|
+
// },
|
|
177
|
+
// DOES NOT WORK
|
|
178
|
+
// "fcm_options": {
|
|
179
|
+
// "link": "https://dummypage.com"
|
|
180
|
+
// }
|
|
181
|
+
notification: {
|
|
182
|
+
title: 'Your Notification Title',
|
|
183
|
+
body: 'This is the notification message.'
|
|
184
|
+
},
|
|
185
|
+
data: {
|
|
186
|
+
customDataKey: 'customDataValue' // Optional custom data payload
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
assistant.log(`🚩🚩🚩🚩🚩 222 response ${id}`, response); // FLAG
|
|
191
|
+
} catch (e) {
|
|
192
|
+
assistant.error(`🚩🚩🚩🚩🚩 222 error ${id}`, e);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const response = await admin.messaging().sendMulticast({
|
|
197
|
+
tokens: batch,
|
|
198
|
+
notification: note.notification,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
assistant.log(`🚩🚩🚩🚩🚩 333 response ${id}`, response); // FLAG
|
|
202
|
+
} catch (e) {
|
|
203
|
+
assistant.error(`🚩🚩🚩🚩🚩 333 error ${id}`, e);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const messages = batch.map(token => ({
|
|
208
|
+
token: token,
|
|
209
|
+
notification: {
|
|
210
|
+
title: 'Your Notification Title',
|
|
211
|
+
body: 'This is the notification message.'
|
|
212
|
+
},
|
|
213
|
+
}));
|
|
214
|
+
|
|
215
|
+
const response = await admin.messaging().sendEach(messages);
|
|
216
|
+
|
|
217
|
+
assistant.log(`🚩🚩🚩🚩🚩 444 response ${id}`, response); // FLAG
|
|
218
|
+
} catch (e) {
|
|
219
|
+
assistant.error(`🚩🚩🚩🚩🚩 444 error ${id}`, e);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const message = {
|
|
224
|
+
notification: {
|
|
225
|
+
title: 'Your Notification Title',
|
|
226
|
+
body: 'This is the notification message.'
|
|
227
|
+
},
|
|
228
|
+
data: {
|
|
229
|
+
customDataKey: 'customDataValue' // Optional custom data payload
|
|
230
|
+
},
|
|
231
|
+
token: batch[0],
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const response = await admin.messaging().send(message);
|
|
235
|
+
|
|
236
|
+
assistant.log(`🚩🚩🚩🚩🚩 555 response ${id}`, response); // FLAG
|
|
237
|
+
} catch (e) {
|
|
238
|
+
assistant.error(`🚩🚩🚩🚩🚩 555 error ${id}`, e);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Send the batch
|
|
242
|
+
const response = await admin.messaging().sendToDevice(batch, note);
|
|
243
|
+
|
|
244
|
+
// Log
|
|
245
|
+
assistant.log(`Sent batch ID: ${id}, Success: ${response.successCount}, Failures: ${response.failureCount}`);
|
|
246
|
+
|
|
247
|
+
// Clean bad tokens
|
|
248
|
+
if (response.failureCount > 0) {
|
|
249
|
+
await self.cleanTokens(batch, response.results, id);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Update response
|
|
253
|
+
payload.response.data.sent += (batch.length - response.failureCount);
|
|
254
|
+
} catch (e) {
|
|
255
|
+
throw assistant.errorify(`Error sending batch ${id}: ${e}`, { code: 500, log: true });
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
Module.prototype.cleanTokens = async function (batch, results, id) {
|
|
260
|
+
const self = this;
|
|
261
|
+
|
|
262
|
+
// Shortcuts
|
|
263
|
+
const Manager = self.Manager;
|
|
264
|
+
const Api = self.Api;
|
|
265
|
+
const assistant = self.assistant;
|
|
266
|
+
const payload = self.payload;
|
|
267
|
+
|
|
268
|
+
// Log
|
|
269
|
+
assistant.log(`Cleaning ${results.length} tokens of batch ID: ${id}`);
|
|
270
|
+
|
|
271
|
+
// Filter out bad tokens
|
|
272
|
+
const cleanPromises = results
|
|
273
|
+
.map((item, index) => {
|
|
274
|
+
// Check if the token is bad
|
|
275
|
+
if (item.error && BAD_TOKEN_REASONS.includes(item.error.code)) {
|
|
276
|
+
const token = batch[index];
|
|
277
|
+
|
|
278
|
+
// Log
|
|
279
|
+
assistant.log(`Found bad token: ${token} (Reason: ${item.error.code})`);
|
|
280
|
+
|
|
281
|
+
// Delete the token
|
|
282
|
+
return self.deleteToken(token, item.error.code);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Return null for valid tokens
|
|
286
|
+
return null;
|
|
287
|
+
})
|
|
288
|
+
// Filter out nulls for valid tokens
|
|
289
|
+
.filter(Boolean);
|
|
290
|
+
|
|
291
|
+
// Clean bad tokens
|
|
292
|
+
try {
|
|
293
|
+
await Promise.all(cleanPromises);
|
|
294
|
+
assistant.log(`Completed cleaning tokens for batch ID: ${id}`);
|
|
295
|
+
} catch (e) {
|
|
296
|
+
assistant.error(`Error cleaning tokens for batch ID: ${id}`, e);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
Module.prototype.deleteToken = async function (token, errorCode) {
|
|
301
|
+
const self = this;
|
|
302
|
+
|
|
303
|
+
// Shortcuts
|
|
304
|
+
const Manager = self.Manager;
|
|
305
|
+
const Api = self.Api;
|
|
306
|
+
const assistant = self.assistant;
|
|
307
|
+
const payload = self.payload;
|
|
308
|
+
|
|
309
|
+
// Libraries
|
|
310
|
+
const { admin } = self.libraries;
|
|
311
|
+
|
|
312
|
+
// Delete the token
|
|
313
|
+
try {
|
|
314
|
+
// Delete the token
|
|
315
|
+
await admin.firestore().doc(`${PATH_NOTIFICATIONS}/${token}`).delete();
|
|
316
|
+
|
|
317
|
+
// Log
|
|
318
|
+
assistant.log(`Deleted bad token: ${token} (Reason: ${errorCode})`);
|
|
319
|
+
|
|
320
|
+
// Update response
|
|
321
|
+
payload.response.data.deleted++;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
assistant.error(`Failed to delete bad token: ${token} (Reason: ${errorCode}). Error: ${error}`);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
module.exports = Module;
|
|
@@ -30,28 +30,40 @@ Module.prototype.main = function () {
|
|
|
30
30
|
deleted: 0,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Prefix payload
|
|
34
|
+
payload.data.payload.notification = payload.data.payload.notification || {};
|
|
35
|
+
payload.data.payload.filters = payload.data.payload.filters || {};
|
|
36
|
+
|
|
33
37
|
// Fix notification payload
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
// https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.notification.md#notification_interface
|
|
39
|
+
payload.data.payload.notification.title = payload.data.payload.notification.title
|
|
40
|
+
|| 'Notification';
|
|
41
|
+
payload.data.payload.notification.body = payload.data.payload.notification.body
|
|
42
|
+
|| 'Check this out';
|
|
43
|
+
payload.data.payload.notification.imageUrl = payload.data.payload.notification.icon
|
|
44
|
+
|| 'https://cdn.itwcreativeworks.com/assets/itw-creative-works/images/socials/itw-creative-works-brandmark-square-black-1024x1024.png';
|
|
45
|
+
payload.data.payload.notification.click_action = payload.data.payload.notification.clickAction
|
|
46
|
+
|| payload.data.payload.notification.click_action
|
|
47
|
+
|| 'https://itwcreativeworks.com';
|
|
48
|
+
|
|
49
|
+
// Fix filters
|
|
50
|
+
// TODO: Implement filters
|
|
51
|
+
|
|
52
|
+
// Set notification payload
|
|
53
|
+
const notification = payload.data.payload.notification;
|
|
54
|
+
const filters = payload.data.payload.filters;
|
|
43
55
|
|
|
44
56
|
// Set notification payload
|
|
45
57
|
try {
|
|
46
|
-
const url = new URL(
|
|
58
|
+
const url = new URL(notification.click_action);
|
|
47
59
|
url.searchParams.set('cb', new Date().getTime());
|
|
48
|
-
|
|
60
|
+
notification.click_action = url.toString();
|
|
49
61
|
} catch (e) {
|
|
50
62
|
reject(assistant.errorify(`Failed to add cb to URL: ${e}`, {code: 400, log: true}));
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
// Log
|
|
54
|
-
assistant.log('Resolved notification payload',
|
|
66
|
+
assistant.log('Resolved notification payload', notification)
|
|
55
67
|
|
|
56
68
|
// Check if user is admin
|
|
57
69
|
if (!payload.user.roles.admin) {
|
|
@@ -59,11 +71,11 @@ Module.prototype.main = function () {
|
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
// Check if title and body are set
|
|
62
|
-
if (!
|
|
74
|
+
if (!notification.title || !notification.body) {
|
|
63
75
|
return reject(assistant.errorify(`Parameters <title> and <body> required`, {code: 400, sentry: true}));
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
await self.processTokens(
|
|
78
|
+
await self.processTokens(notification, {tags: false})
|
|
67
79
|
.then(r => {
|
|
68
80
|
return resolve({data: payload.response.data})
|
|
69
81
|
})
|
|
@@ -75,7 +87,7 @@ Module.prototype.main = function () {
|
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
// HELPERS //
|
|
78
|
-
Module.prototype.processTokens = async function (
|
|
90
|
+
Module.prototype.processTokens = async function (notification, options) {
|
|
79
91
|
const self = this;
|
|
80
92
|
|
|
81
93
|
// Shortcuts
|
|
@@ -107,7 +119,7 @@ Module.prototype.processTokens = async function (note, options) {
|
|
|
107
119
|
// Send the batch
|
|
108
120
|
try {
|
|
109
121
|
assistant.log(`Sending batch ${index} with ${batchTokens.length} tokens.`);
|
|
110
|
-
await self.sendBatch(batchTokens,
|
|
122
|
+
await self.sendBatch(batchTokens, index, notification);
|
|
111
123
|
} catch (e) {
|
|
112
124
|
assistant.error(`Error sending batch ${index}`, e);
|
|
113
125
|
}
|
|
@@ -133,7 +145,16 @@ Module.prototype.processTokens = async function (note, options) {
|
|
|
133
145
|
// Manually sending
|
|
134
146
|
// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send
|
|
135
147
|
// https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually
|
|
136
|
-
|
|
148
|
+
|
|
149
|
+
// Help
|
|
150
|
+
// https://stackoverflow.com/questions/79408734/send-firebase-cloud-messaging-fcm-notification-with-click-action
|
|
151
|
+
// https://stackoverflow.com/questions/72494678/an-error-occurred-when-trying-to-authenticate-to-the-fcm-servers-on-firebase-c
|
|
152
|
+
// https://stackoverflow.com/questions/72552943/how-can-i-add-firebase-admin-role-to-firebase-project-service-accouts
|
|
153
|
+
|
|
154
|
+
// https://stackoverflow.com/questions/50148266/click-action-attribute-for-web-push-notification-through-fcm
|
|
155
|
+
// https://stackoverflow.com/questions/49177428/http-v1-api-click-action-for-webpush-notification/52764782#52764782
|
|
156
|
+
// https://firebase.google.com/docs/cloud-messaging/js/receive#setting_notification_options_in_the_send_request
|
|
157
|
+
Module.prototype.sendBatch = async function (batch, id, notification) {
|
|
137
158
|
const self = this;
|
|
138
159
|
|
|
139
160
|
// Shortcuts
|
|
@@ -147,64 +168,65 @@ Module.prototype.sendBatch = async function (batch, note, id) {
|
|
|
147
168
|
|
|
148
169
|
try {
|
|
149
170
|
// Log
|
|
150
|
-
assistant.log(`Sending batch
|
|
151
|
-
console.log('🚩🚩🚩🚩🚩', 1, ); // FLAG
|
|
152
|
-
assistant.log(`batch`, batch);
|
|
153
|
-
assistant.log(`note`, note);
|
|
171
|
+
assistant.log(`Sending batch #${id}: tokens=${batch.length}...`, notification);
|
|
154
172
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
173
|
+
// Prepare messages
|
|
174
|
+
// We have to set click_action because Firebase DOES NOT properly handle it in the service worker, so we HANDLE IT OURSELVES
|
|
175
|
+
const messages = batch.map(token => ({
|
|
176
|
+
token: token,
|
|
177
|
+
notification: {
|
|
178
|
+
title: notification.title,
|
|
179
|
+
body: notification.body,
|
|
180
|
+
imageUrl: notification.imageUrl,
|
|
181
|
+
},
|
|
182
|
+
webpush: {
|
|
158
183
|
notification: {
|
|
159
|
-
title:
|
|
160
|
-
body:
|
|
184
|
+
title: notification.title,
|
|
185
|
+
body: notification.body,
|
|
186
|
+
icon: notification.imageUrl,
|
|
187
|
+
click_action: notification.click_action,
|
|
161
188
|
},
|
|
162
189
|
data: {
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
assistant.log(`🚩🚩🚩🚩🚩 222 response ${id}`, response); // FLAG
|
|
168
|
-
} catch (e) {
|
|
169
|
-
assistant.error(`🚩🚩🚩🚩🚩 222 error ${id}`, e);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
const response = await admin.messaging().sendMulticast({
|
|
174
|
-
tokens: batch,
|
|
175
|
-
notification: note.notification,
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
assistant.log(`🚩🚩🚩🚩🚩 333 response ${id}`, response); // FLAG
|
|
179
|
-
} catch (e) {
|
|
180
|
-
assistant.error(`🚩🚩🚩🚩🚩 333 error ${id}`, e);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const messages = batch.map(token => ({
|
|
185
|
-
token: token,
|
|
186
|
-
notification: {
|
|
187
|
-
title: 'Your Notification Title',
|
|
188
|
-
body: 'This is the notification message.'
|
|
190
|
+
click_action: notification.click_action,
|
|
189
191
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
192
|
+
fcm_options: {
|
|
193
|
+
link: notification.click_action,
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
data: {
|
|
197
|
+
click_action: notification.click_action,
|
|
198
|
+
},
|
|
199
|
+
}));
|
|
198
200
|
|
|
199
201
|
// Send the batch
|
|
200
|
-
const response = await admin.messaging().sendToDevice(batch, note);
|
|
202
|
+
// const response = await admin.messaging().sendToDevice(batch, note);
|
|
203
|
+
const response = await admin.messaging().sendEach(messages);
|
|
204
|
+
// responses: [
|
|
205
|
+
// { success: false, error: [FirebaseMessagingError] },
|
|
206
|
+
// {
|
|
207
|
+
// success: true,
|
|
208
|
+
// messageId: 'projects/promo-server-api/messages/1e91fbc4-2d50-4457-addc-b9add252ae7b'
|
|
209
|
+
// },
|
|
210
|
+
// { success: false, error: [FirebaseMessagingError] }
|
|
211
|
+
// ],
|
|
212
|
+
// successCount: 1,
|
|
213
|
+
// failureCount: 2
|
|
214
|
+
|
|
215
|
+
// Log
|
|
216
|
+
assistant.log(`Sent batch #${id}: tokens=${batch.length}, success=${response.successCount}, failures=${response.failureCount}`, JSON.stringify(response));
|
|
201
217
|
|
|
202
218
|
// Log
|
|
203
219
|
assistant.log(`Sent batch ID: ${id}, Success: ${response.successCount}, Failures: ${response.failureCount}`);
|
|
204
220
|
|
|
221
|
+
// Attach token to response
|
|
222
|
+
response.responses = response.responses.map((item, index) => {
|
|
223
|
+
item.token = batch[index];
|
|
224
|
+
return item;
|
|
225
|
+
});
|
|
226
|
+
|
|
205
227
|
// Clean bad tokens
|
|
206
228
|
if (response.failureCount > 0) {
|
|
207
|
-
await self.cleanTokens(batch, response.
|
|
229
|
+
await self.cleanTokens(batch, response.responses, id);
|
|
208
230
|
}
|
|
209
231
|
|
|
210
232
|
// Update response
|
|
@@ -230,18 +252,21 @@ Module.prototype.cleanTokens = async function (batch, results, id) {
|
|
|
230
252
|
const cleanPromises = results
|
|
231
253
|
.map((item, index) => {
|
|
232
254
|
// Check if the token is bad
|
|
233
|
-
|
|
234
|
-
const token = batch[index];
|
|
255
|
+
const shouldClean = BAD_TOKEN_REASONS.includes(item?.error?.code)
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
|
|
257
|
+
// Log
|
|
258
|
+
assistant.log(`Checking #${index}: success=${item.success}, error=${item?.error?.code || null}, clean=${shouldClean}`, item.error);
|
|
259
|
+
assistant.log(`item.error`, item.error);
|
|
260
|
+
assistant.log(`item?.error?.code`, item?.error?.code);
|
|
261
|
+
assistant.log(`item?.error?.message`, item?.error?.message);
|
|
238
262
|
|
|
239
|
-
|
|
240
|
-
|
|
263
|
+
// Quit if no error
|
|
264
|
+
if (!item.error || !shouldClean) {
|
|
265
|
+
return null;
|
|
241
266
|
}
|
|
242
267
|
|
|
243
|
-
//
|
|
244
|
-
return
|
|
268
|
+
// Delete the token
|
|
269
|
+
return self.deleteToken(item.token, item.error.code);
|
|
245
270
|
})
|
|
246
271
|
// Filter out nulls for valid tokens
|
|
247
272
|
.filter(Boolean);
|
|
@@ -278,7 +303,7 @@ Module.prototype.deleteToken = async function (token, errorCode) {
|
|
|
278
303
|
// Update response
|
|
279
304
|
payload.response.data.deleted++;
|
|
280
305
|
} catch (error) {
|
|
281
|
-
assistant.error(`Failed to delete bad token: ${token} (Reason: ${errorCode})
|
|
306
|
+
assistant.error(`Failed to delete bad token: ${token} (Reason: ${errorCode})`, error);
|
|
282
307
|
}
|
|
283
308
|
};
|
|
284
309
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
1
|
+
// Librairies
|
|
2
|
+
const fetch = require('wonderful-fetch');
|
|
3
|
+
const { jwtDecode } = require('jwt-decode');
|
|
4
4
|
|
|
5
|
+
// Module
|
|
5
6
|
function OAuth2() {
|
|
6
7
|
const self = this;
|
|
7
8
|
self.provider = 'discord';
|
|
@@ -19,6 +20,8 @@ function OAuth2() {
|
|
|
19
20
|
|
|
20
21
|
OAuth2.prototype.buildUrl = function (state, url) {
|
|
21
22
|
const self = this;
|
|
23
|
+
|
|
24
|
+
// Shortcuts
|
|
22
25
|
const Manager = self.Manager;
|
|
23
26
|
const assistant = self.assistant;
|
|
24
27
|
|
|
@@ -34,11 +37,16 @@ OAuth2.prototype.buildUrl = function (state, url) {
|
|
|
34
37
|
|
|
35
38
|
OAuth2.prototype.verifyIdentity = function (tokenizeResult) {
|
|
36
39
|
const self = this;
|
|
40
|
+
|
|
41
|
+
// Shortcuts
|
|
37
42
|
const Manager = self.Manager;
|
|
38
43
|
const assistant = self.assistant;
|
|
39
44
|
|
|
40
45
|
return new Promise(async function(resolve, reject) {
|
|
46
|
+
// Log
|
|
47
|
+
assistant.log('verifyIdentity(): tokenizeResult', tokenizeResult);
|
|
41
48
|
|
|
49
|
+
// Get identity
|
|
42
50
|
const identityResponse = await fetch('https://discord.com/api/users/@me', {
|
|
43
51
|
timeout: 60000,
|
|
44
52
|
response: 'json',
|
|
@@ -52,8 +60,10 @@ OAuth2.prototype.verifyIdentity = function (tokenizeResult) {
|
|
|
52
60
|
.then(json => json)
|
|
53
61
|
.catch(e => e)
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
// Log
|
|
64
|
+
assistant.log('verifyIdentity(): identityResponse', identityResponse);
|
|
56
65
|
|
|
66
|
+
// Check if error
|
|
57
67
|
if (identityResponse instanceof Error) {
|
|
58
68
|
return reject(identityResponse);
|
|
59
69
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
1
|
+
// Librairies
|
|
2
|
+
const fetch = require('wonderful-fetch');
|
|
3
|
+
const { jwtDecode } = require('jwt-decode');
|
|
4
4
|
|
|
5
|
+
// Module
|
|
5
6
|
function OAuth2() {
|
|
6
7
|
const self = this;
|
|
7
8
|
self.provider = 'google';
|
|
@@ -18,6 +19,8 @@ function OAuth2() {
|
|
|
18
19
|
|
|
19
20
|
OAuth2.prototype.buildUrl = function (state, url) {
|
|
20
21
|
const self = this;
|
|
22
|
+
|
|
23
|
+
// Shortcuts
|
|
21
24
|
const Manager = self.Manager;
|
|
22
25
|
const assistant = self.assistant;
|
|
23
26
|
|
|
@@ -35,13 +38,20 @@ OAuth2.prototype.buildUrl = function (state, url) {
|
|
|
35
38
|
|
|
36
39
|
OAuth2.prototype.verifyIdentity = function (tokenizeResult) {
|
|
37
40
|
const self = this;
|
|
41
|
+
|
|
42
|
+
// Shortcuts
|
|
38
43
|
const Manager = self.Manager;
|
|
39
44
|
const assistant = self.assistant;
|
|
40
45
|
|
|
41
46
|
return new Promise(async function(resolve, reject) {
|
|
42
|
-
|
|
47
|
+
// Log
|
|
48
|
+
assistant.log('verifyIdentity(): tokenizeResult', tokenizeResult);
|
|
49
|
+
|
|
50
|
+
// Decode token
|
|
51
|
+
const decoded = jwtDecode(tokenizeResult.id_token);
|
|
43
52
|
|
|
44
|
-
//
|
|
53
|
+
// Log
|
|
54
|
+
assistant.log('verifyIdentity(): decoded', decoded);
|
|
45
55
|
|
|
46
56
|
// Check if exists
|
|
47
57
|
Manager.libraries.admin.firestore().collection(`users`)
|
|
@@ -1068,14 +1068,14 @@ BackendAssistant.prototype.parseMultipartFormData = function (options) {
|
|
|
1068
1068
|
}
|
|
1069
1069
|
|
|
1070
1070
|
const isJWT = (token) => {
|
|
1071
|
-
|
|
1072
|
-
const parts = token.split('.');
|
|
1071
|
+
const { jwtDecode } = require('jwt-decode');
|
|
1073
1072
|
|
|
1074
1073
|
try {
|
|
1075
|
-
// Decode the
|
|
1076
|
-
const
|
|
1074
|
+
// Decode the token and request the header
|
|
1075
|
+
const decoded = jwtDecode(token, { header: true });
|
|
1076
|
+
|
|
1077
1077
|
// Check for expected JWT keys in the header
|
|
1078
|
-
return
|
|
1078
|
+
return decoded?.alg && decoded?.typ === 'JWT';
|
|
1079
1079
|
} catch (err) {
|
|
1080
1080
|
// If parsing fails, it's not a valid JWT
|
|
1081
1081
|
return false;
|
package/src/manager/index.js
CHANGED
|
@@ -152,6 +152,9 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
152
152
|
? self.assistant.meta.environment
|
|
153
153
|
: process.env.ENVIRONMENT;
|
|
154
154
|
|
|
155
|
+
// Set BEM env variables
|
|
156
|
+
process.env.BEM_FUNCTIONS_URL = self.project.functionsUrl;
|
|
157
|
+
|
|
155
158
|
// Use the working Firebase logger that they disabled for whatever reason
|
|
156
159
|
if (
|
|
157
160
|
process.env.GCLOUD_PROJECT
|